Android使用AES-256-GCM加密带重音文本Node.js解密失败排查
问题根因
这个报错和带重音符号的文本没有直接关系,核心错误出在Android端密文与认证标签的拆分逻辑,非ASCII字符的编码长度差刚好触发了问题:
- 纯ASCII字符场景下,字符串字符数和UTF-8字节数相等,原有拆分逻辑可以正常运行
- 带重音的字符、中文等非ASCII字符在UTF-8编码下占2-4字节,此时用字符串长度
text.length()(统计字符数)去切分字节数组,会导致密文、GCM认证标签(tag)截取错误,Node.js端拿到无效tag做完整性校验时,就会抛出Unsupported state or unable to authenticate data错误。
另外如果两端密钥派生参数不对齐,也会触发同类报错。
修复步骤
1. 修正Android端密文与tag的拆分逻辑
原有代码用text.length()作为密文长度判断依据是完全错误的,GCM模式下tag长度是固定值(代码里配置的是128位即16字节),直接从加密结果末尾截取tag即可,不需要依赖原文本长度。
找到encrypt方法里的错误代码:
byte[] ciphertext = Arrays.copyOfRange(cipher, 0, text.length()); byte[] tag = Arrays.copyOfRange(cipher, text.length(), cipher.length);
替换为:
// GCM tag长度为16字节,对应传入GCMParameterSpec的128位tag配置 int tagByteLength = 16; byte[] ciphertext = Arrays.copyOfRange(cipher, 0, cipher.length - tagByteLength); byte[] tag = Arrays.copyOfRange(cipher, cipher.length - tagByteLength, cipher.length);
修改后无论原文本包含什么编码的字符,都能正确拆分密文和tag。
2. 对齐两端PBKDF2密钥派生参数
检查Android端getKeyFromPassword方法的参数,必须和Node.js端完全一致:
- 迭代次数:2145
- 派生密钥长度:32字节(对应AES-256)
- 哈希算法:SHA-512
任意参数不匹配都会导致两端生成的密钥不同,直接触发认证失败。
3. (可选优化)修正Node.js端解密的编码参数
Node.js端update方法传入的text本身是Buffer对象,不需要指定binary输入编码,把解密返回的代码改成:
return decipher.update(text, null, 'utf8') + decipher.final('utf8');
验证说明
修复后使用提供的测试文本Désolé、密钥tIJg9tyj2setkg7gknVreP5PXzRQV5J3测试,GCM认证可以正常通过,解密结果完全正确,不会再抛出认证错误。
内容的提问来源于stack exchange,提问作者Henock Bongi
相关产品推荐
相关产品推荐

