使用 CryptoJS 的 OpenSSL 加密解密

OpenSSL Encrypt-Decrypt with CryptoJS

我在 JS 中生成加密时遇到问题, 我在 PHP 中有这样的加密生成器:

$secret_key = 'thisIsK3y';
$secret_iv  = 'tHis1s1v';
    
$output         = false;
$encrypt_method = "AES-256-CBC";
$key            = hash( 'sha256', $secret_key );
$iv             = substr( hash( 'sha256', $secret_iv ), 0, 16 );
    
if( $action == 'e' ) 
{
   $output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
}
else if( $action == 'd')
{
    $output = openssl_decrypt( base64_decode( $string ), $encrypt_method, $key, 0, $iv );
}
    
return $output;

然后我尝试使用 CryptoJS 在 React 上翻译成 JS :

import sha256 from "crypto-js/sha256";
import Base64 from "crypto-js/enc-base64";
import AES from "crypto-js/aes";

let secret_key = "thisIsK3y";
let secret_iv = "tHis1s1v";
            
let output = false;
let encrypt_method = "AES-256-CBC";
let key = sha256(secret_key);
let iv = String(sha256(secret_iv)).substr(0, 16);
     
if (action == "e") { // encrypt action
   output = AES.encrypt("test", Utf8.parse(key), {
            iv: Utf8.parse(iv),
   }).toString();

   alert(Base64.parse(output));
}

然后警报向我显示此加密:fd0337c029ad25c240316a1d61db9144,然后我尝试在我的 php.but 中解密它显示纯文本似乎不可打印的 ascii

b"}¦7▀À4█ÍØ█ù6ÒM§Ú¡]ÙW[¸^8"

并且有PHP通知:

PHP Notice: iconv(): Detected an illegal character in input string in /var/www/html/blabla/vendor/symfony/var-dumper/Dumper/AbstractDumper.php on line 203

谁能帮帮我?

如果PHP代码以明文$string = "test";执行,则加密部分提供:

MWNjdVlVL1hBWGN2UFlpMG9yMGZBUT09

关于PHP代码,需要注意以下几点:

  1. 加密过程Base64编码两次。一次显式使用 base64_encode,一次默认隐式使用 (s. openssl_encrypt, 4th parameter)。这种冗余是不必要的,即应该删除两个 Base64 编码之一。类似于解密。
  2. hash函数returns默认情况下数据以小写十六进制编码。因此 $key 的大小为 64 字节。对于 AES-256,OpenSSL 仅隐式使用前 32 个字节。

您发布的 CryptoJS 代码可以修改如下以实现此功能 (JavaScript):

var Sha256 = CryptoJS.SHA256;
var Hex = CryptoJS.enc.Hex;
var Utf8 = CryptoJS.enc.Utf8;
var Base64 = CryptoJS.enc.Base64;
var AES = CryptoJS.AES;

var secret_key = 'thisIsK3y';
var secret_iv  = 'tHis1s1v';

var key = Sha256(secret_key).toString(Hex).substr(0,32); // Use the first 32 bytes (see 2.)
var iv = Sha256(secret_iv).toString(Hex).substr(0,16);

// Encryption
var output = AES.encrypt("test", Utf8.parse(key), {
            iv: Utf8.parse(iv),
   }).toString(); // First Base64 encoding, by default (see 1.)

var output2ndB64 = Utf8.parse(output).toString(Base64); // Second Base64 encoding (see 1.)
console.log(output2ndB64); // MWNjdVlVL1hBWGN2UFlpMG9yMGZBUT09

// Decryption
var decrypted = AES.decrypt(output, Utf8.parse(key), {
            iv: Utf8.parse(iv),
   }).toString(Utf8); 
console.log(decrypted); // test
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

针对PHP代码考虑到上面提到的两点。与PHP代码中的hash函数一样,toString(Hex)也是使用小写字母(所以这方面不需要转换)。

请注意,出于安全原因,实际上必须使用静态 IV。相反,应该为每次加密重新生成 IV,并将其与密文一起发送给收件人(IV 不是秘密的)。此外,使用 SHA256 作为密钥派生函数也不是很安全。