AES-GCM 256 Node.js加密Android解密16字节认证标签不匹配如何解决
AES-GCM 256 Node.js加密Android端解密认证标签不匹配问题解决方案
问题根因
- Node.js侧密钥处理逻辑错误:你已经通过
new Buffer(密钥字符串, 'hex')将16进制密钥转为了正确的字节数组,但在createCipheriv调用时又错误执行了Buffer.from(KEY, 'utf-8'),相当于把正确的密钥字节按UTF-8编码重新转换,导致实际使用的密钥和Android端不一致 - 密文和认证标签的拼接逻辑不匹配:Android的AES-GCM实现默认会把16字节认证标签直接拼接在密文末尾,而Node.js的GCM实现是将密文和认证标签分开返回,你当前没有将Node.js生成的认证标签拼在密文后发给Android,导致Android端解密时读取到的标签是错误的
- Node.js代码存在基础错误:一是加密模块名应为
crypto而非crypt,二是加密过程中update返回值赋值给了ciph变量,final拼接的是未初始化的ciphered变量,导致生成的密文本身就不正确
修正后的代码实现
Node.js加密代码
const crypto = require('crypto'); // 密钥直接用hex转buffer,不需要二次转码 const KEY = Buffer.from('1bc0d563329d29fc6236f099371e334feb9bbf3e91bba2adb4043feb870b6b21', 'hex'); // 注意:生产环境禁止使用固定IV,必须使用crypto.randomBytes(12)生成随机12字节IV const iv = Buffer.from("0000000000000000",'base64'); const cipher = crypto.createCipheriv('aes-256-gcm', KEY, iv); // 统一密文变量名 let ciphered = cipher.update(Buffer.from("Hello World", 'utf-8'), 'utf8', 'base64'); ciphered += cipher.final('base64'); // 获取认证标签转base64后拼在密文后面 const authTag = cipher.getAuthTag().toString('base64'); // 最终发给Android的内容是密文+标签拼接后的base64字符串 const finalEncrypted = ciphered + authTag; console.log(finalEncrypted);
Android解密代码注意事项
确认你的ALGORITHM_NAME常量取值必须为"AES/GCM/NoPadding",不需要其他逻辑修改,Android会自动从传入的加密数据末尾读取16字节作为认证标签校验。
生产环境安全建议
- 禁止使用固定IV,每次加密都要生成随机12字节IV,将IV、密文、标签拼接后一起传输(推荐结构为
[12字节IV][密文][16字节标签]),解密端先取出前12字节作为IV再解密 - 不要硬编码密钥在代码中,Android端建议使用系统KeyStore存储敏感密钥
内容的提问来源于stack exchange,提问作者Daniel Brox
相关产品推荐
相关产品推荐

