使用加密相同参数时,CryptoJS AES解密结果异常
CryptoJS AES加密含特殊字符密钥后解密失败的解决方法
问题重现
使用含\xb5这类非UTF-8兼容字符的密钥时,加密后解密得到空字符串;换成纯ASCII密钥则正常。代码及输出如下:
加密解密代码:
var s = "125\xb5\xb5$8" /* var s = "AAAA" */ var key = CryptoJS.enc.Utf8.parse(s) var iv = CryptoJS.enc.Utf8.parse(s) var plain = "Very secret message!" var encrypted = CryptoJS.AES.encrypt(plain, key, { keySize: 16, iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); var encryptedb64 = encrypted.toString() console.log("encrypted:", encryptedb64) var decrypted = CryptoJS.AES.decrypt(encryptedb64, key, { keySize: 16, iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); console.log("decrypted:", decrypted.toString(CryptoJS.enc.Utf8))
引入CryptoJS的脚本:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
异常输出(用"125\xb5\xb5$8"作为密钥):
"encrypted:", "W2dFmOY22wp2W1ayCa1xmk3EwaxG/H+LKkgnADKAYmY=" "decrypted:", ""
正常输出(用"AAAA"作为密钥):
"encrypted:", "OAmNRtxYfJIFEq4VK4xPievfuXIIfEV+48yHBqVexWI=" "decrypted:", "Very secret message!"
原因分析
- UTF-8解析的字节篡改:
\xb5属于ISO-8859-1(Latin1)字符,但不是有效的UTF-8字节(UTF-8中0xB5是续字节,不能单独存在)。使用CryptoJS.enc.Utf8.parse()处理这类字符串时,会将无效的UTF-8字节序列替换为U+FFFD(替换字符,对应UTF-8字节0xEF 0xBF 0xBD),直接改变了原始密钥的字节内容。 - 密钥长度不符合AES规范:AES要求密钥长度必须为128/192/256位(即16/24/32字节)。用
Utf8.parse处理后得到的密钥长度不符合规范,CryptoJS内部处理时会出现加密和解密的实际密钥不一致的情况,最终导致解密失败输出空字符串。 - 纯ASCII密钥的特殊性:纯ASCII字符串在UTF-8和Latin1解析下结果完全一致,且即使长度不符合AES规范,CryptoJS的内部处理恰好能保持加密解密的一致性,所以能得到正确结果。
解决方案
将密钥和IV的解析方式从Utf8.parse改为Latin1.parse,因为Latin1会直接把每个字节映射到对应的Unicode字符,不会修改原始字节内容,确保加密和解密使用的密钥/IV完全一致:
修改后的代码:
var s = "125\xb5\xb5$8" /* var s = "AAAA" */ // 使用Latin1解析,保留原始字节 var key = CryptoJS.enc.Latin1.parse(s) var iv = CryptoJS.enc.Latin1.parse(s) var plain = "Very secret message!" var encrypted = CryptoJS.AES.encrypt(plain, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); var encryptedb64 = encrypted.toString() console.log("encrypted:", encryptedb64) var decrypted = CryptoJS.AES.decrypt(encryptedb64, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); console.log("decrypted:", decrypted.toString(CryptoJS.enc.Utf8))
另外注意:keySize参数仅在密钥为字符串(需要派生密钥)时生效,当密钥是WordArray类型时该参数会被忽略,因此可以移除该参数避免混淆。
修改后运行,加密和解密结果将匹配,能正确还原原始明文。
内容的提问来源于stack exchange,提问作者SegFault
相关产品推荐
相关产品推荐

