加密和解密使用 Crypto JS 类似于 php 中的 openssl 功能

问题描述 投票:0回答:2

我正在尝试用 PHP 和 JS 加密和解密字符串。

PHP

function encrypt_decrypt($action, $string)
{
    $output = false;
    $encrypt_method = "AES-256-CBC";
    $secret_key = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';
    $secret_iv = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';

    $key = hash('sha256', $secret_key);

    //  echo $key.'  '; // bc11b452cdf3f8155b5fd3e3711b1d0712638c613f33f5551b96d5bb37b490ae

    // echo strlen($key); // 64

    // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
    $iv = substr(hash('sha256', $secret_iv), 0, 16);

    //   echo $iv.' '; // bc11b452cdf3f815

    if ($action == 'encrypt') {
        $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
        $output = base64_encode($output); // output is base 64 encoded
    } else if ($action == 'decrypt') {
        $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
    }
    return $output;
}

$encrypted = encrypt_decrypt('encrypt', 'alaks@123');

// echo "encrypted string is " . $encrypted; // Rko0Q0lsdnl3Ukp6RkI2bXA4STF6QT09

// echo "decrypted string is " . encrypt_decrypt('decrypt', $encrypted); // alaks@123

我正在尝试应用相同的 secret_key 和 secret_iv 并获得密钥和 iv 匹配

JS

<script src="node_modules/crypto-js/crypto-js.js"></script>
<script>
  
    const secret_key = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';
    const secret_iv = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';

    const text = 'alaks@123';

    const key = CryptoJS.SHA256(secret_key).toString();
    const iv = CryptoJS.SHA256(secret_iv).toString().substring(0, 16);

    // console.log("key", key); // bc11b452cdf3f8155b5fd3e3711b1d0712638c613f33f5551b96d5bb37b490ae
   
    // console.log("iv", iv); // bc11b452cdf3f815
    
    function aesEncrypt(data) {
        let cipher = CryptoJS.AES.encrypt(data, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return btoa(cipher.toString()); // convert the encrypted string to base64
    }

    function aesDecrypt(data) {
        let cipher = CryptoJS.AES.decrypt(atob(data), key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return cipher.toString(CryptoJS.enc.Utf8);
    }

    const encryptedText = aesEncrypt(text);
    const decryptedText = aesDecrypt(encryptedText);
    console.log('Encrypted Text - ' + encryptedText); // this encrypted text is different from php generated
    console.log('Decrypted Text - ' + decryptedText);

    // actual encrypted text = VTJGc2RHVmtYMSsvblljaUpsdElzQ2w4eVFsRnZHS01oR2g2WS8raTJLbz0=
    // expected encrypted text = Rko0Q0lsdnl3Ukp6RkI2bXA4STF6QT09

   
</script>

生成的密文也和PHP不一致。以下是每次刷新时的加密文本

encrypted text generated = VTJGc2RHVmtYMTlISHlLdlBGdDB5Sitlb3pnY04wQjJtWWIwV0RCR1I1Yz0=
encrypted text generated = VTJGc2RHVmtYMS9XZXNyTzhMWkZqdEtpVkF1MURJeUdzUlhqUUlUcXRlTT0=
encrypted text generated = VTJGc2RHVmtYMTh3dHY0VDBBanhzbnltejV1K1MxV2FYb1ZXalJuTjJxZz0=
encrypted text generated = VTJGc2RHVmtYMTh3Z081YVViNnc3dCtZUlAvN05NZ09nRUh0blB0eTBpaz0=

我的问题是,CrptoJS 设置应该是什么,以获得与 PHP 中相同的编码输出 (Rko0Q0lsdnl3Ukp6RkI2bXA4STF6QT09)

const secret_key = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';
    const secret_iv = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';

    const text = 'alaks@123';

    const key = CryptoJS.SHA256(secret_key).toString().substring(0,32);
  
    const iv = CryptoJS.SHA256(secret_iv).toString().substring(0, 16);

    console.log("key", key); // bc11b452cdf3f8155b5fd3e3711b1d0712638c613f33f5551b96d5bb37b490ae
   
    // console.log("iv", iv); // bc11b452cdf3f815
    
    function aesEncrypt(data) {
        let cipher = CryptoJS.AES.encrypt(data, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return btoa(cipher.toString()); // convert the encrypted string to base64
    }

    function aesDecrypt(data) {
        let cipher = CryptoJS.AES.decrypt(atob(data), key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return cipher.toString(CryptoJS.enc.Utf8);
    }

//**Edit 1: Added Code Snippet**
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

结合 PHP 和 JS 代码

    <?php
$text = 'alaks@123';

define('SECRET_KEY', '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,');
define('SECRET_IV', '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,');

function encrypt_decrypt($action, $string)
{
    $collectionInPhp = [];
    $encrypt_method = "AES-256-CBC";
    $output = false;
    $key = hash('sha256', SECRET_KEY);
    $iv = substr(hash('sha256', SECRET_IV), 0, 16);

    $collectionInPhp['secret_key'] = SECRET_KEY;
    $collectionInPhp['secret_iv'] = SECRET_IV;
    $collectionInPhp['key'] = $key;
    $collectionInPhp['iv'] = $iv;

    if ($action == 'encrypt') {
        $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
        $collectionInPhp['before_base64_encode'] = $output;
         $output = base64_encode($output);
         $collectionInPhp['after_base64_encode'] = $output;

    } else if ($action == 'decrypt') {
        $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
    }
    return [$output, $collectionInPhp];
}

$result = encrypt_decrypt('encrypt', $text);

$encrypted = $result[0];
$collectionInPhp = $result[1];
?>

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/3.0.0/pure-min.css"
        integrity="sha512-X2yGIVwg8zeG8N4xfsidr9MqIfIE8Yz1It+w2rhUJMqxUwvbVqC5OPcyRlPKYOw/bsdJut91//NO9rSbQZIPRQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>

<body>
    <table class="pure-table pure-table-bordered">
        <thead>
            <tr>
                <th>#</th>
                <th>PHP</th>
                <th>JS</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Secret Key</td>
                <td class="php secret-key"></td>
                <td class="js secret-key"></td>
            </tr>
            <tr>
                <td>Secret IV</td>
                <td class="php secret-iv"></td>
                <td class="js secret-iv"></td>
            </tr>
            <tr>
                <td>Key</td>
                <td class="php key"></td>
                <td class="js key"></td>
            </tr>
            <tr>
                <td>IV</td>
                <td class="php iv"></td>
                <td class="js iv"></td>
            </tr>
            <tr>
                <td>Before Base64Encode</td>
                <td class="php beforeBase64Encode"></td>
                <td class="js"></td>
            </tr>
            <tr>
                <td>Encrypted</td>
                <td class="php encrypted"></td>
                <td class="js"></td>
            </tr>
            <tr>
                <td>Received in JS</td>
                <td class="php"></td>
                <td class="js encrypted"></td>
            </tr>
            <tr>
                <td>After Base64Decode</td>
                <td class="php"></td>
                <td class="js afterBase64Decode"></td>
            </tr>

            <tr>
                <td>Decrypted</td>
                <td class="php"></td>
                <td class="js decrypted"></td>
            </tr>
        </tbody>
    </table>
    <script src="node_modules/crypto-js/crypto-js.js"></script>
    <script>
    const collectionInPhp = <?php echo json_encode($collectionInPhp); ?>;
    const encrypted = "<?php echo $encrypted; ?>";
    const base64Decoded = atob("<?php echo $encrypted; ?>");

    // const secret_key = CryptoJS.enc.Utf8.parse("<?php // echo SECRET_KEY; ?>");
    // const secret_iv = CryptoJS.enc.Utf8.parse("<?php // echo SECRET_IV; ?>");
    // const key = CryptoJS.SHA256(secret_key).toString().substring(0,32); 
    // const iv = CryptoJS.SHA256(secret_iv).toString().substring(0, 16);

    const secret_key = "<?php echo SECRET_KEY; ?>";
    const secret_iv = "<?php  echo SECRET_IV; ?>";
    const key = CryptoJS.enc.Utf8.parse(CryptoJS.SHA256(secret_key)).toString().substring(0, 32);
    const iv = CryptoJS.enc.Utf8.parse(CryptoJS.SHA256(secret_iv)).toString().substring(0, 16);

    function aesDecrypt(data) {
        let cipher = CryptoJS.AES.decrypt(data, key, { // test case 1
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });

        // return cipher.toString(CryptoJS.enc.Utf8);
        return cipher.toString();
    }
    const decrypted = aesDecrypt(base64Decoded);
    const collectionInJS = {
        base64Decoded: base64Decoded,
        encrypted: encrypted,
        key: key,
        iv: iv,
        secret_key: secret_key,
        secret_iv: secret_iv,
        decrypted: decrypted
    }

    document.querySelector('.php.secret-key').innerHTML = collectionInPhp.secret_key;
    document.querySelector('.js.secret-key').innerHTML = collectionInJS.secret_key;

    document.querySelector('.php.secret-iv').innerHTML = collectionInPhp.secret_iv;
    document.querySelector('.js.secret-iv').innerHTML = collectionInJS.secret_iv;

    document.querySelector('.php.key').innerHTML = collectionInPhp.key;
    document.querySelector('.js.key').innerHTML = collectionInJS.key;

    document.querySelector('.php.iv').innerHTML = collectionInPhp.iv;
    document.querySelector('.js.iv').innerHTML = collectionInJS.iv;

    document.querySelector('.php.beforeBase64Encode').innerHTML = collectionInPhp.before_base64_encode;
    document.querySelector('.js.afterBase64Decode').innerHTML = collectionInJS.base64Decoded;


    document.querySelector('.php.encrypted').innerHTML = collectionInPhp.after_base64_encode;
    document.querySelector('.js.encrypted').innerHTML = collectionInJS.encrypted;

    document.querySelector('.js.decrypted').innerHTML = collectionInJS.decrypted;
    </script>

</body>

</html>

PHP 和 JS 的价值比较

php encryption cryptojs
2个回答
0
投票

在 PHP 代码中,密钥生成为密码

$secret_key
的 SHA256 哈希值。
hash()
默认返回十六进制编码的结果,这就是为什么结果由 64 个十六进制数字组成,即 64 个字节。
PHP/OpenSSL 根据指定的摘要
AES-256-CBC
隐式地将密钥长度截断为 32 字节。 CryptoJS 不会自动缩短,即缩短必须明确完成。

IV 生成为密码

$secret_iv
的 SHA256 哈希。与密钥不同,它被正确截断为正确的大小。

为了让 CryptoJS 将密钥材料解释为密钥,它必须作为 WordArray 传递。为此,必须使用 UTF-8 编码器转换密钥。这同样适用于 IV.

const secret_key = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';
const secret_iv = '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,';

const text = 'alaks@123';

const keyHex = CryptoJS.SHA256(secret_key).toString().substring(0,32); // Fix 1: Truncate the key to 32 bytes
const ivHex = CryptoJS.SHA256(secret_iv).toString().substring(0, 16);
const key = CryptoJS.enc.Utf8.parse(keyHex); // Fix 2: Convert key and IV to a WordArray using the UTF8 encoder
const iv = CryptoJS.enc.Utf8.parse(ivHex); 

function aesEncrypt(data) {
    let cipher = CryptoJS.AES.encrypt(data, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return btoa(cipher.toString()); 
}

function aesDecrypt(data) {
    let cipher = CryptoJS.AES.decrypt(atob(data), key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return cipher.toString(CryptoJS.enc.Utf8);
}
const ciphertext = aesEncrypt('alaks@123');
const decrypted = aesDecrypt(ciphertext);
console.log(ciphertext); // Rko0Q0lsdnl3Ukp6RkI2bXA4STF6QT09
console.log(decrypted);  // alaks@123
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>


PHP代码有一些漏洞和低效:

  • 不应使用十六进制编码作为键,而是实际值。类似于 IV.
  • 应该应用随机 IV,而不是静态 IV。
  • 代替快速摘要,应该使用经过验证的密钥派生(至少 PBKDF2)。
  • 不应应用双重 Base64 编码/解码。

0
投票

基于@Topaco 的回答,下面是完整的工作代码。

    <?php
    $text = 'alaks@123';

    define('SECRET_KEY', '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,');
    define('SECRET_IV', '$6Za+?k}^`5q:f^@TSxy69gf7JcKuF!,');

    function encrypt_decrypt($action, $string)
    {
        $collectionInPhp = [];
        $encrypt_method = "AES-256-CBC";
        $output = false;
        $key = hash('sha256', SECRET_KEY);
        $iv = substr(hash('sha256', SECRET_IV), 0, 16);

        $collectionInPhp['secret_key'] = SECRET_KEY;
        $collectionInPhp['secret_iv'] = SECRET_IV;
        $collectionInPhp['key'] = $key;
        $collectionInPhp['iv'] = $iv;

        if ($action == 'encrypt') {
            $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
            $collectionInPhp['before_base64_encode'] = $output;
            $output = base64_encode($output);
            $collectionInPhp['after_base64_encode'] = $output;

        } else if ($action == 'decrypt') {
            $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
        }
        return [$output, $collectionInPhp];
    }

    $result = encrypt_decrypt('encrypt', $text);

    $encrypted = $result[0];
    $collectionInPhp = $result[1];
    ?>

    <!DOCTYPE html>
    <html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/3.0.0/pure-min.css"
            integrity="sha512-X2yGIVwg8zeG8N4xfsidr9MqIfIE8Yz1It+w2rhUJMqxUwvbVqC5OPcyRlPKYOw/bsdJut91//NO9rSbQZIPRQ=="
            crossorigin="anonymous" referrerpolicy="no-referrer" />
    </head>

    <body>
        <table class="pure-table pure-table-bordered">
            <thead>
                <tr>
                    <th>#</th>
                    <th>PHP</th>
                    <th>JS</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>Secret Key</td>
                    <td class="php secret-key"></td>
                    <td class="js secret-key"></td>
                </tr>
                <tr>
                    <td>Secret IV</td>
                    <td class="php secret-iv"></td>
                    <td class="js secret-iv"></td>
                </tr>
                <tr>
                    <td>Key</td>
                    <td class="php key"></td>
                    <td class="js key"></td>
                </tr>
                <tr>
                    <td>IV</td>
                    <td class="php iv"></td>
                    <td class="js iv"></td>
                </tr>
                <tr>
                    <td>Before Base64Encode</td>
                    <td class="php beforeBase64Encode"></td>
                    <td class="js"></td>
                </tr>
                <tr>
                    <td>Encrypted</td>
                    <td class="php encrypted"></td>
                    <td class="js"></td>
                </tr>
                <tr>
                    <td>Received in JS</td>
                    <td class="php"></td>
                    <td class="js encrypted"></td>
                </tr>
                <tr>
                    <td>After Base64Decode</td>
                    <td class="php"></td>
                    <td class="js afterBase64Decode"></td>
                </tr>

                <tr>
                    <td>Decrypted</td>
                    <td class="php"></td>
                    <td class="js decrypted"></td>
                </tr>
            </tbody>
        </table>
        <script src="node_modules/crypto-js/crypto-js.js"></script>
        <script>
        const collectionInPhp = <?php echo json_encode($collectionInPhp); ?>;
        const encrypted = "<?php echo $encrypted; ?>";
        const base64Decoded = atob("<?php echo $encrypted; ?>");
        const secret_key = "<?php echo SECRET_KEY; ?>";
        const secret_iv = "<?php  echo SECRET_IV; ?>";
        const key = CryptoJS.enc.Utf8.parse(CryptoJS.SHA256(secret_key).toString().substring(0, 32));
        const iv = CryptoJS.enc.Utf8.parse(CryptoJS.SHA256(secret_iv).toString().substring(0, 16));

        function aesDecrypt(data) {
            let cipher = CryptoJS.AES.decrypt(data, key, { // test case 1
                iv: iv,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

            return cipher.toString(CryptoJS.enc.Utf8);
            // return cipher.toString();
        }
        const decrypted = aesDecrypt(base64Decoded);
        const collectionInJS = {
            base64Decoded: base64Decoded,
            encrypted: encrypted,
            key: key,
            iv: iv,
            secret_key: secret_key,
            secret_iv: secret_iv,
            decrypted: decrypted
        }

        document.querySelector('.php.secret-key').innerHTML = collectionInPhp.secret_key;
        document.querySelector('.js.secret-key').innerHTML = collectionInJS.secret_key;

        document.querySelector('.php.secret-iv').innerHTML = collectionInPhp.secret_iv;
        document.querySelector('.js.secret-iv').innerHTML = collectionInJS.secret_iv;

        document.querySelector('.php.key').innerHTML = collectionInPhp.key;
        document.querySelector('.js.key').innerHTML = collectionInJS.key;

        document.querySelector('.php.iv').innerHTML = collectionInPhp.iv;
        document.querySelector('.js.iv').innerHTML = collectionInJS.iv;

        document.querySelector('.php.beforeBase64Encode').innerHTML = collectionInPhp.before_base64_encode;
        document.querySelector('.js.afterBase64Decode').innerHTML = collectionInJS.base64Decoded;


        document.querySelector('.php.encrypted').innerHTML = collectionInPhp.after_base64_encode;
        document.querySelector('.js.encrypted').innerHTML = collectionInJS.encrypted;

        document.querySelector('.js.decrypted').innerHTML = collectionInJS.decrypted;
        </script>

    </body>

    </html>

© www.soinside.com 2019 - 2024. All rights reserved.