如何在不修改JS代码的前提下用PHP解密CryptoJS加密字符串
解密失败核心原因
返回null本质是PHP端传入openssl_decrypt的所有参数都和CryptoJS默认加密逻辑不匹配:
- 算法模式选错:
CryptoJS.AES.encrypt传入字符串密钥时,默认走AES-256-CBC模式+PKCS7填充,根本不是你写的aes-128-ctr - 密钥派生逻辑错:当你给CryptoJS传字符串作为密钥时,它不会直接把这个字符串当原始加密密钥用,而是走OpenSSL兼容的
EVP_BytesToKey派生逻辑:生成8字节随机盐存在密文里,结合口令用MD5做1次迭代哈希,派生出32位AES密钥+16位初始向量IV - 密文结构没处理:CryptoJS返回的密文是Base64编码的,解码后结构是
8字节固定标识Salted__ + 8字节随机盐 + 真实密文,直接把整个字符串丢给openssl_decrypt肯定解析失败
可直接使用的解密代码
以下代码完全对齐CryptoJS默认加密逻辑,不需要修改任何已有JS加密代码:
function cryptoJsAesDecrypt(string $ciphertext, string $passphrase): string|false { // Base64解码存储的密文 $cipherRaw = base64_decode($ciphertext); // 校验密文合法格式 if (substr($cipherRaw, 0, 8) !== 'Salted__') { return false; } // 拆分盐和真实密文 $salt = substr($cipherRaw, 8, 8); $realCipherText = substr($cipherRaw, 16); // 按EVP_BytesToKey规则派生密钥和IV,和CryptoJS默认逻辑完全一致:MD5摘要、1次迭代 $derivedKeyIv = ''; $hashBlock = ''; while (strlen($derivedKeyIv) < 48) { // 32字节AES256密钥 + 16字节CBC模式IV 共48字节 $hashBlock = md5($hashBlock . $passphrase . $salt, true); $derivedKeyIv .= $hashBlock; } $key = substr($derivedKeyIv, 0, 32); $iv = substr($derivedKeyIv, 32, 16); // 解密,OPENSSL_RAW_DATA标识传入的密文是原始二进制格式 return openssl_decrypt($realCipherText, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv); } // 调用示例:传入数据库中存储的密文、原加密用的密钥即可 $plainText = cryptoJsAesDecrypt("数据库存储的JS加密后密文", "Key");
注意事项
- 不要手动给openssl_decrypt添加
OPENSSL_ZERO_PADDING参数,openssl默认开启的PKCS7填充和CryptoJS默认填充规则完全兼容 - 如果解密返回false,优先检查从数据库取出的密文是否存在多余空格、转义字符,Base64格式被破坏会直接导致解密失败
内容的提问来源于stack exchange,提问作者Milad Xandi
相关产品推荐
相关产品推荐

