CryptoJs - 通过 PHP 和 Javascript 加密/解密 - 简单输出加密字符串

Fra*_* G. 1 javascript php encryption cryptojs

我想在 Php 和 Javascript 中加密和解密一些字符串,并在网上查看,最好和最安全的方法似乎是 CryptoJs。

这篇文章不是Encrypt with PHP, Decrypt with Javascript (cryptojs)的副本,因为它的输出字符串并不简单。

这是我的代码,但 Js 解密代码不起作用。怎么了?

<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
    </head>
    <body>
        <p>--- PHP ------------------</p>
    
        <?php
            function myCrypt($value, $passphrase, $iv){
                $encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $passphrase, true, $iv);
                return base64_encode($encrypted_data);
            }
            
            function myDecrypt($value, $passphrase, $iv){
                $value = base64_decode($value);
                $data = openssl_decrypt($value, 'aes-256-cbc', $passphrase, true, $iv);
                return $data;
            }
            

            $valTxt="MyText";
            $pswd="MyPassword";
            $vector="1234567890123412";
            $encrypted = myCrypt($valTxt, $pswd, $vector);
            $decrypted = myDecrypt($encrypted, $pswd, $vector);
            
            echo "<p>Text to crypt --------> ".$valTxt." </p>";
            echo "<p>Password: ".$pswd." </p>";
            echo "<p>Vector: ".$vector." </p>";
            echo "<p>TextEncrypt: ".$encrypted." </p>";
            echo "<p>TextDecrypt: ".$decrypted." </p>";
        ?>
        
        <br><br><br>
        <p>--- Javascript ------------------</p>
        <p>JS-DataEncrypt: --------- <span id="DataEncrypt"></span></p>
        <p>JS-DataPassword: -------- <span id="DataPassword"></span></p>
        <p>JS-DataVector: ---------- <span id="DataVector"></span></p>
        <p>JS-TextDecrypted: ------- <span id="result"></span></p>
        
        <script>
            var DataEncrypt='<?php echo $encrypted;?>';
            var DataPassword='<?php echo $pswd;?>';
            var DataVector='<?php echo $vector;?>';
            
            //var key = CryptoJS.enc.Hex.parse(DataPassword);
            //var iv = CryptoJS.enc.Hex.parse(DataVector);
            //var decrypted = CryptoJS.AES.decrypt(DataEncrypt, key, { iv: iv });
            
            var decrypted = CryptoJS.AES.decrypt(DataEncrypt, DataPassword, { iv: DataVector });
            
            decrypted= CryptoJS.enc.Utf8.stringify(decrypted)
            
            
            
            document.getElementById("DataEncrypt").innerHTML = DataEncrypt;
            document.getElementById("DataPassword").innerHTML = DataPassword;
            document.getElementById("DataVector").innerHTML = DataVector;
            document.getElementById("result").innerHTML = decrypted;
        </script>
    
    
        
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

附注。如果输出字符串($encrypted)是 16 位数字 A-Za-z0-9 会更好……是否可以更改“aes-256-cbc”?

Top*_*aco 6

在 PHP 代码中,应考虑以下几点:

  • $passphrase不表示密码,而是密钥。此键的大小必须为 32 字节以供选择aes-256-cbc。如果太短,则用0值填充,如果太长,则被截断。这是一个常见的错误来源,因此应使用正好为 32 字节的密钥。如果您想使用密码,则必须使用 KDF(如 PBKDF2)。
  • 在第四个参数中设置了标志,并且没有布尔表达式(如true)。如果数据应该以二进制形式返回,则OPENSSL_RAW_DATA必须设置标志。
  • 静态IV是不安全的,通常每次加密都会生成一个新的IV,与密文一起发送给接收者。由于IV不是秘密的,它通常在字节级别放在密文前面而不加密。

以下示例 PHP 代码(基于发布的代码):

function myCrypt($value, $key, $iv){
    $encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    return base64_encode($encrypted_data);
}

function myDecrypt($value, $key, $iv){
    $value = base64_decode($value);
    $data = openssl_decrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    return $data;
}

$valTxt="MyText";
$key="01234567890123456789012345678901"; // 32 bytes
$vector="1234567890123412"; // 16 bytes
$encrypted = myCrypt($valTxt, $key, $vector);
$decrypted = myDecrypt($encrypted, $key, $vector);
print($encrypted . "\n");
print($decrypted . "\n");
Run Code Online (Sandbox Code Playgroud)

返回以下结果:

1SF+kez1CE5Rci3H6ff8og==
MyText
Run Code Online (Sandbox Code Playgroud)

对应的CryptoJS解密代码为:

function myCrypt($value, $key, $iv){
    $encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    return base64_encode($encrypted_data);
}

function myDecrypt($value, $key, $iv){
    $value = base64_decode($value);
    $data = openssl_decrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    return $data;
}

$valTxt="MyText";
$key="01234567890123456789012345678901"; // 32 bytes
$vector="1234567890123412"; // 16 bytes
$encrypted = myCrypt($valTxt, $key, $vector);
$decrypted = myDecrypt($encrypted, $key, $vector);
print($encrypted . "\n");
print($decrypted . "\n");
Run Code Online (Sandbox Code Playgroud)
1SF+kez1CE5Rci3H6ff8og==
MyText
Run Code Online (Sandbox Code Playgroud)

MyText对应于原始明文的输出。

将密钥作为 传递很重要WordArray,以便将其解释为密钥而不是密码。对于转换,CryptoJS 提供了编码器(如CryptoJS.enc.Utf8)。


关于你最后的问题:密文是二进制任意序列,可以转换为具有特殊二进制到文本编码的字符串(例如本例中的 Base64 或十六进制),通常比原始数据(Base64: 75% 的效率,十六进制:50% 的效率,见这里)。
因此,通常不可能用与块大小(例如 AES 的 16 个字节)相等的字母数字字符(例如 16 个字符)的数量来表示密文块。
请注意,转换为具有字符集编码(例如 UTF8)的字符串也不是解决方案,但会损坏数据。