Node.js加密的AES-GCM文件无法在Deno中解密的问题求助
问题根源分析
导致Node.js加密的文件在Deno中解密失败、两端加密结果不一致的核心原因,是两者在AES-128-GCM的实现细节处理上存在差异,主要集中在以下几点:
- 密钥导入/编码不一致:Node.js的
crypto模块直接支持Buffer/字符串密钥,而Deno遵循Web Crypto API,需要明确指定密钥格式(如raw)和编码,若导入时格式不匹配,实际使用的密钥会不同。 - GCM标签长度与分离逻辑不匹配:Node.js默认生成16字节(128位)的认证标签,且需手动获取并与密文拼接;Deno解密时必须正确分离密文和标签,若标签长度或拼接顺序不一致,会直接触发解密失败。
- IV的传递/解析错误:GCM推荐使用12字节IV,若两端对IV的编码(如Base64/Hex)或传递方式(如文件中存储位置)不同,会导致解密时IV不匹配。
- 附加认证数据(AAD)未同步:如果加密时使用了AAD,两端必须完全同步AAD的内容、编码和长度,否则会触发认证失败。
排查与修正步骤
针对以上问题,可按以下步骤排查并修正代码:
1. 统一密钥处理逻辑
确保两端使用相同长度(AES-128对应16字节)和编码的密钥:
- Node.js侧:直接用Buffer导入密钥
const key = Buffer.from('your-16-byte-secret', 'utf8'); // 必须16字节 - Deno侧:按Web Crypto API规范导入原始格式密钥
const keyBuffer = new TextEncoder().encode('your-16-byte-secret'); const cryptoKey = await crypto.subtle.importKey( 'raw', keyBuffer, { name: 'AES-GCM' }, false, ['decrypt'] );
2. 统一加密输出格式
Node.js加密时需按「IV(12字节) + 密文 + 标签(16字节)」的顺序存储,Deno解密时按同样规则分离:
- Node.js加密代码(修正后)
const fs = require('fs'); const crypto = require('crypto'); const encryptFile = (inputPath, outputPath, key) => { const iv = crypto.randomBytes(12); // GCM标准12字节IV const cipher = crypto.createCipheriv('aes-128-gcm', key, iv); // 若使用AAD,需与Deno保持一致 // const aad = Buffer.from('fixed-aad-content', 'utf8'); // cipher.setAAD(aad); const inputStream = fs.createReadStream(inputPath); const outputStream = fs.createWriteStream(outputPath); // 先写入IV outputStream.write(iv); // 写入密文 inputStream.pipe(cipher).pipe(outputStream); // 写入认证标签 inputStream.on('end', () => { outputStream.write(cipher.getAuthTag()); outputStream.end(); }); }; const key = Buffer.from('your-16-byte-secret', 'utf8'); encryptFile('input.txt', 'encrypted.bin', key); - Deno解密代码(对应格式)
const decryptFile = async (inputPath, outputPath, keyStr) => { const encryptedData = await Deno.readFile(inputPath); // 分离IV、密文、标签 const iv = encryptedData.slice(0, 12); const ciphertext = encryptedData.slice(12, encryptedData.length - 16); const authTag = encryptedData.slice(encryptedData.length - 16); // 导入密钥 const keyBuffer = new TextEncoder().encode(keyStr); const cryptoKey = await crypto.subtle.importKey( 'raw', keyBuffer, { name: 'AES-GCM' }, false, ['decrypt'] ); // 解密(同步AAD和标签长度) const decryptedBuffer = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv, // additionalData: new TextEncoder().encode('fixed-aad-content'), // 与Node.js一致 tagLength: 128 // 对应16字节标签 }, cryptoKey, new Uint8Array([...ciphertext, ...authTag]) ); await Deno.writeFile(outputPath, new Uint8Array(decryptedBuffer)); }; await decryptFile('encrypted.bin', 'decrypted.txt', 'your-16-byte-secret');
3. 验证IV与标签长度
- 确保IV始终为12字节(GCM最优长度,避免兼容性问题)
- 若手动设置了标签长度(如Node.js中
cipher.setAuthTagLength(12)),Deno解密时需在选项中指定tagLength: 96(12字节对应96位)
内容的提问来源于stack exchange,提问作者aosamesan
相关产品推荐
相关产品推荐

