使用node-forge AES-CBC解密文件失败,请求技术支持
问题排查与修复方案
我帮你排查了代码里的几个关键问题,这些应该就是导致解密后文件异常的核心原因:
1. 废弃的readAsBinaryString引发的编码破坏
readAsBinaryString已经被浏览器废弃,它会把二进制数据转换成UTF-8字符串,这会破坏原始字节(尤其是非ASCII范围的字节),直接导致哈希校验、IV读取、解密过程都出现数据失真。必须改用readAsArrayBuffer来处理所有二进制数据,它能完整保留原始字节信息。
2. IV的格式不符合forge要求
你当前把IV读取成字符串后直接传给解密函数,但node-forge的AES-CBC解密要求IV是字节数组或者forge的Buffer对象,字符串格式的IV会导致解密算法无法正确解析,最终输出错误的解密结果。
3. 解密后数据的转换错误
cipher.output是forge的Buffer对象,直接取.data得到的是字符串,用这个字符串创建File时会默认用UTF-8编码,破坏原始二进制内容。需要把forge Buffer转换成Uint8Array后再创建文件。
4. 哈希校验的逻辑错误
同样因为用了readAsBinaryString,哈希计算的是字符串的字节,而不是原始文件的字节,即使服务器返回的哈希正确,也可能出现校验不匹配(或者侥幸匹配但后续解密出错)。
修正后的完整代码
// 获取文件 const xhr = new XMLHttpRequest(); xhr.open("GET", config.urlLogin + "files/" + e.id, true); xhr.responseType = "arraybuffer"; xhr.send(); xhr.onreadystatechange = () => { if (xhr.readyState !== 4) return; if (xhr.status !== 200) { console.log(xhr.status + ": " + xhr.statusText); return; } const file = new File([xhr.response], e.name); this.checkHash(file, (validatedFile) => { if (validatedFile) { this.decryptFile(validatedFile); } else { console.log("哈希校验失败"); } }); }; // 校验哈希(改用ArrayBuffer处理) checkHash(file, callback) { const reader = new FileReader(); const expectedHash = this.state.fileChoose.hash; reader.onload = (event) => { const arrayBuffer = event.target.result; // 用forge处理ArrayBuffer const buffer = forge.util.createBuffer(new Uint8Array(arrayBuffer)); const md = forge.md.sha256.create(); md.update(buffer.getBytes()); const computedHash = window.btoa(md.digest().data); if (expectedHash === computedHash) { callback(file); } else { callback(false); } }; reader.readAsArrayBuffer(file); } // 合并解密逻辑(直接从ArrayBuffer处理IV和加密内容) decryptFile(file) { const reader = new FileReader(); const { key, name } = this.state.fileChoose; reader.onload = (event) => { const arrayBuffer = event.target.result; // 提取前16字节作为IV,剩下的是加密内容 const ivBytes = new Uint8Array(arrayBuffer.slice(0, 16)); const encryptedBytes = new Uint8Array(arrayBuffer.slice(16)); // 初始化forge解密器 const decipher = forge.cipher.createDecipher("AES-CBC", window.atob(key)); decipher.start({ iv: ivBytes }); // 把加密字节转成forge Buffer decipher.update(forge.util.createBuffer(encryptedBytes)); const decryptSuccess = decipher.finish(); if (!decryptSuccess) { console.log("解密失败,可能是密钥或IV错误"); return; } // 把forge Buffer转成Uint8Array,再创建文件 const decryptedBuffer = decipher.output; const decryptedBytes = new Uint8Array(decryptedBuffer.length()); for (let i = 0; i < decryptedBuffer.length(); i++) { decryptedBytes[i] = decryptedBuffer.getByte(i); } const finishFile = new File([decryptedBytes], name); const link = document.createElement("a"); link.download = name; link.href = URL.createObjectURL(finishFile); link.click(); // 释放URL对象,避免内存泄漏 URL.revokeObjectURL(link.href); }; reader.readAsArrayBuffer(file); }
关键修复点说明
- 全程用
readAsArrayBuffer处理二进制数据,避免编码失真 - 直接从ArrayBuffer切片提取IV和加密内容,保证字节完整性
- 用Uint8Array作为IV传给forge解密器,符合格式要求
- 解密后把forge Buffer转成Uint8Array再创建文件,确保原始字节不被编码破坏
- 增加了解密失败的判断逻辑,方便排查问题
- 增加了
URL.revokeObjectURL释放资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者Vadim Baranov
相关产品推荐
相关产品推荐

