升级crypto-js至4.2.0后React前端出现UTF-8格式错误及加解密兼容问题
解决CryptoJS 3.x升级到4.x后的UTF-8格式错误及跨端解密问题
核心问题原因
CryptoJS 4.x对API输入的格式要求更严格,尤其是AES.decrypt方法:
- 3.x版本中传入Base64密文字符串时,会自动隐式转换为WordArray;但4.x版本需要显式将Base64密文转换为WordArray,否则解密过程会生成无效的字节序列,最终抛出
malformed UTF-8错误。 - 另外,解密失败时直接调用
toString(CryptoJS.enc.Utf8)会因无效字节触发错误,需要增加容错处理。
修正后的代码
const iterationCount = 1000; const keySize = 128 / 32; // 对应128位密钥,4个WordArray元素 function generateKey(salt, passPhrase) { return CryptoJS.PBKDF2( passPhrase, CryptoJS.enc.Hex.parse(salt), { keySize, iterations: iterationCount } ); } function encrypt(salt, iv, plainText) { const passPhrase = process.env.ENCRYPT_SECRET; const key = generateKey(salt, passPhrase); const encrypted = CryptoJS.AES.encrypt( CryptoJS.enc.Utf8.parse(plainText), // 显式转换明文为WordArray,避免编码歧义 key, { iv: CryptoJS.enc.Hex.parse(iv), mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding } ); return encrypted.ciphertext.toString(CryptoJS.enc.Base64); } function decrypt(salt, iv, cipherText) { const passPhrase = process.env.DECRYPT_SECRET; const key = generateKey(salt, passPhrase); // 关键修正:将Base64密文显式转换为WordArray const cipherWordArray = CryptoJS.enc.Base64.parse(cipherText); const decrypted = CryptoJS.AES.decrypt( { ciphertext: cipherWordArray }, // 传入CipherParams对象,而非原始字符串 key, { iv: CryptoJS.enc.Hex.parse(iv), mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding } ); try { // 尝试转换为UTF-8,失败则返回原始Hex或其他格式 const result = decrypted.toString(CryptoJS.enc.Utf8); return result || decrypted.toString(CryptoJS.enc.Hex); // 容错处理 } catch (e) { return decrypted.toString(CryptoJS.enc.Hex); } }
额外注意事项
- 确保前后端的加密参数完全一致:包括PBKDF2的迭代次数、密钥长度、AES的模式(CTR)、填充方式(NoPadding),以及salt/iv的编码格式(Hex)。
- 前端加密时显式将明文转换为
WordArray(CryptoJS.enc.Utf8.parse(plainText)),避免不同版本对字符串编码的隐式处理差异。 - 后端解密时,需对应处理前端传入的Base64密文,确保解码为字节数组后再进行AES解密,保持和前端的编码逻辑对齐。
内容的提问来源于stack exchange,提问作者Rukku
相关产品推荐
相关产品推荐

