加密数据转字符串再还原时字节不一致问题求助
问题根源
我来帮你分析下问题所在,以及如何修复:
首先你遇到的核心问题是:加密后的二进制数据(ArrayBuffer)不能直接通过UTF-8编码/解码转换成字符串。
加密输出的是任意二进制字节,其中很多值并不属于合法的UTF-8字符范围。当你用TextDecoder('utf-8').decode()处理这些二进制时,解码器会把无法识别的字节替换成替换字符�(U+FFFD),这就导致了字节数增加(原139字节变成262字节),同时数据也被损坏了,无法还原回原加密结果。
另外你的代码里还有几个具体错误:
const str = new TextEncoder().encode(buff1);:TextEncoder.encode()的参数应该是字符串,但你传了一个Buffer(Uint8Array),JS会把这个对象强制转成字符串"[object Uint8Array]",这完全不是你想要的操作。- 变量名错误:
const utf8buff = new TextDecoder('utf-8').decode(buff);里的buff应该是buff1,但即使修正了,前面说的UTF-8解码问题依然存在。
正确解决方案:用Base64转换二进制和字符串
Base64是专门设计用来将任意二进制数据转换成可安全存储、传输的ASCII字符串的编码方式,不会损坏数据,也不会改变原始信息。
下面是修正后的完整代码,我会标注关键修改点:
async function encryptText(plainText, iv, password) { const ptUtf8 = new TextEncoder().encode(plainText); const pwUtf8 = new TextEncoder().encode(password); const pwHash = await crypto.subtle.digest('SHA-256', pwUtf8); const alg = { name: 'AES-GCM', iv: iv }; const key = await crypto.subtle.importKey('raw', pwHash, alg, false, ['encrypt']); return crypto.subtle.encrypt(alg, key, ptUtf8); }; async function encrBuffToUtf8Test() { const plainData = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' + 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'; const password = 'infamous'; const randomValues = crypto.getRandomValues(new Uint8Array(12)); // 加密得到ArrayBuffer const encryptionResult1 = await encryptText(plainData, randomValues, password); // --- 关键修改1:将ArrayBuffer转成Base64字符串 --- // 浏览器环境写法: const base64Str = btoa(String.fromCharCode(...new Uint8Array(encryptionResult1))); // 如果是Node.js环境,可以用更简洁的写法: // const base64Str = Buffer.from(encryptionResult1).toString('base64'); console.log('加密后的Base64字符串:', base64Str); console.log('Base64字符串长度:', base64Str.length); // --- 关键修改2:将Base64字符串转回ArrayBuffer --- // 浏览器环境写法: const decodedUint8 = Uint8Array.from(atob(base64Str), c => c.charCodeAt(0)); const encryptionResult2 = decodedUint8.buffer; // Node.js环境写法: // const encryptionResult2 = Buffer.from(base64Str, 'base64').buffer; // 验证两个ArrayBuffer是否相等(注意:直接===比较的是引用,要比较内容) const isEqual = new Uint8Array(encryptionResult1).every((byte, idx) => byte === new Uint8Array(encryptionResult2)[idx]); console.log('数据是否还原一致:', isEqual); // 应该输出true } // 执行测试 encrBuffToUtf8Test();
额外说明
为什么不用UTF-8?
UTF-8是用来编码文本字符的,不是用来编码任意二进制的。任意二进制中可能包含UTF-8无法解析的字节序列,解码时会丢失信息,导致无法还原原始数据。关于ArrayBuffer的比较
直接用===比较两个ArrayBuffer是比较它们的内存引用,而不是内容。所以要验证还原后的数据是否正确,需要逐字节比较两个Uint8Array的内容。
内容的提问来源于stack exchange,提问作者MaestroMaus
相关产品推荐
相关产品推荐

