You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用加密相同参数时,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!"

原因分析

  1. 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),直接改变了原始密钥的字节内容。
  2. 密钥长度不符合AES规范:AES要求密钥长度必须为128/192/256位(即16/24/32字节)。用Utf8.parse处理后得到的密钥长度不符合规范,CryptoJS内部处理时会出现加密和解密的实际密钥不一致的情况,最终导致解密失败输出空字符串。
  3. 纯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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:06:24