使用Crypto-JS解密Base64密文出现Malformed UTF-8 data错误
问题原因
Malformed UTF-8 data 错误的本质是解密输出的二进制序列不符合UTF-8编码规范,你的代码存在3个直接触发该错误的问题:
- 密文传参格式错误:你直接将截取到的明文字符串传入
CryptoJS.AES.decrypt,没有做Base64解码。CryptoJS默认会将传入的普通字符串按UTF-8编码解析为密文,和实际Base64编码的密文格式不匹配,解密输出完全是乱码,转UTF-8自然报错。 - 密钥生成逻辑错误:你每次运行代码都通过
genRanHex(0x10)生成随机的16位十六进制前缀拼接成密钥,AES是对称加密算法,解密密钥必须和加密时使用的密钥完全一致,每次随机生成的密钥不可能正确解密历史加密数据。 - 算法参数未显式声明:没有显式指定加密模式、填充规则,一旦CryptoJS默认值和加密端实际使用的参数不匹配,解密结果就会是乱码。
修复代码
const CryptoJS = require('crypto-js') const fs = require('fs'); // 读取加密文件,提前去掉首尾空白字符避免截取偏移 const str = fs.readFileSync('file.txt','utf8').trim(); const genRanHex = function(length) { const chars = '0123456789abcdef'; const result = []; for (let i = 0; i < length; i++) { result.push(chars.charAt(Math.floor(Math.random() * chars.length))); } return result.join(''); } // 注意:这里必须替换成加密时实际使用的完整密钥,不要每次运行重新生成随机前缀 // 仅在加密流程中可以用genRanHex生成随机密钥,解密时必须使用和加密完全一致的KEY const KEYS = '替换成加密时实际用的32位十六进制密钥'; // 示例:genRanHex(0x10) + '3c07f4b0efef700a' const key = CryptoJS.enc.Hex.parse(KEYS); // 前24个Base64字符对应16字节IV(和你原有截取逻辑一致) const iv = CryptoJS.enc.Base64.parse(str.substring(0, 24)); // 24位之后的内容是Base64编码的实际密文,解码后传入解密函数 const ciphertext = CryptoJS.enc.Base64.parse(str.substring(24)); try { const decrypted = CryptoJS.AES.decrypt( ciphertext, key, { iv: iv, mode: CryptoJS.mode.CBC, // 替换为加密端实际使用的模式 padding: CryptoJS.pad.Pkcs7 // 替换为加密端实际使用的填充方式 } ); // 解密结果转UTF-8字符串 const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8); const originalText = JSON.parse(decryptedStr); console.log(originalText); } catch (err) { console.error('解密失败:', err); }
排查指引
- 先确认密钥正确性:固定写死加密时使用的完整32位十六进制密钥,不要动态随机生成,这是对称加解密能正常工作的前提。
- 确认密文拼接规则:如果加密端不是采用「前24位Base64字符存IV+后续字符存Base64密文」的拼接格式,需要对应调整截取的偏移量。
- 对齐算法参数:加密模式、填充规则必须和加密端完全一致,常见的组合是CBC模式+Pkcs7填充,如果加密端用了其他配置要对应修改。
内容的提问来源于stack exchange,提问作者Zaito
相关产品推荐
相关产品推荐

