Kotlin AES GCM解密Tag mismatch问题求助
解决Kotlin AES-GCM解密Tag mismatch问题
我在使用Kotlin实现AES-GCM解密时遇到Tag mismatch错误,密钥、IV、Tag和密文均来自第三方API,其官方JavaScript解密示例代码如下:
for (var i = 0; i < validations.length; i++) { var decipher = forge.cipher.createDecipher("AES-GCM", sessionKey); decipher.start({ iv: atob(validations[i].iv), tag: atob(validations[i].tag), }); decipher.update(forge.util.createBuffer(atob(validations[i].data))); var pass = decipher.finish(); if (pass) { dec_validations.push({ data: btoa(decipher.output.data), id: validations[i].id, }); } }
我的Kotlin实现代码如下:
fun main() { val myTestKey = "22+uMvw39JLTXeBJweORNRQPeVneGP0+oQgvwmjSx4Y=" val iv = "+7/pEeTMDH8uyShG" val tag = "3sSY6JEEY8EdXMmrnhgN3Q==" val cipherText = "jzgiE5bec+ty0xI3zhsXZVbCuUwSttOBpuH2Ts0R/CLVh9Vf4rw2HM7ZRWizXcQ1SElurXdNNFRyfXMF8N/WUjjPgaUAV9dvtB8wfDqkq5rsbvpUIi8OAVXvAa/rwhMZgfTcIOUbB2q3A+tft3T+K4aBSOTl1a8btmjMmlZ+lFYIXWLBSu0L9As6gAkSUkCWOQMs7Up9guOnP9r6E/lhO1gQnuPkebiKjJWkVYM+Ezwgnne0b4P6dD59aPc03eqxmMRdxk5/SSbPLysj2pHprsD01KsID+Ny5sJDS1gvXz8eegTySriKEIpMn8nUkK5yKru4aEgGFcz0amSlK1dmqmAdL6ZOPcT3EAcobY=" val decodedKey = Base64.getDecoder().decode(myTestKey) val sessionKeySpec: Key = SecretKeySpec(decodedKey, "AES") val ivBytes = Base64.getDecoder().decode(iv) val tagBytes = Base64.getDecoder().decode(tag) val gcmSpec: AlgorithmParameterSpec = GCMParameterSpec(128, ivBytes) val cipher = Cipher.getInstance("AES/GCM/NoPadding") cipher.init(Cipher.DECRYPT_MODE, sessionKeySpec, gcmSpec) val ciphertext = Base64.getDecoder().decode(cipherText) val combined = ByteBuffer.allocate(ciphertext.size + tagBytes.size) .put(ciphertext) .put(tagBytes) .array() val plaintext = cipher.doFinal(combined) println(String(plaintext)) }
我已尝试以下两种方案,但问题仍未解决:
- 添加
cipher.updateAAD(tagBytes) - 将Tag字节与密文拼接
解决方法
问题核心是Java/Kotlin的Cipher与Forge的AES-GCM处理逻辑存在差异:Forge支持单独传入Tag做验证,而Java系的Cipher需要将Tag作为最后一段数据传入doFinal,而非和密文直接拼接后一次性传入。
修改后的Kotlin代码如下:
fun main() { val myTestKey = "22+uMvw39JLTXeBJweORNRQPeVneGP0+oQgvwmjSx4Y=" val iv = "+7/pEeTMDH8uyShG" val tag = "3sSY6JEEY8EdXMmrnhgN3Q==" val cipherText = "jzgiE5bec+ty0xI3zhsXZVbCuUwSttOBpuH2Ts0R/CLVh9Vf4rw2HM7ZRWizXcQ1SElurXdNNFRyfXMF8N/WUjjPgaUAV9dvtB8wfDqkq5rsbvpUIi8OAVXvAa/rwhMZgfTcIOUbB2q3A+tft3T+K4aBSOTl1a8btmjMmlZ+lFYIXWLBSu0L9As6gAkSUkCWOQMs7Up9guOnP9r6E/lhO1gQnuPkebiKjJWkVYM+Ezwgnne0b4P6dD59aPc03eqxmMRdxk5/SSbPLysj2pHprsD01KsID+Ny5sJDS1gvXz8eegTySriKEIpMn8nUkK5yKru4aEgGFcz0amSlK1dmqmAdL6ZOPcT3EAcobY=" // 解码密钥 val decodedKey = Base64.getDecoder().decode(myTestKey) val sessionKeySpec: Key = SecretKeySpec(decodedKey, "AES") // 解码IV和Tag val ivBytes = Base64.getDecoder().decode(iv) val tagBytes = Base64.getDecoder().decode(tag) // 初始化GCM参数和Cipher val gcmSpec = GCMParameterSpec(128, ivBytes) val cipher = Cipher.getInstance("AES/GCM/NoPadding") cipher.init(Cipher.DECRYPT_MODE, sessionKeySpec, gcmSpec) // 处理密文 val ciphertext = Base64.getDecoder().decode(cipherText) cipher.update(ciphertext) // 传入Tag完成解密和验证 val plaintext = cipher.doFinal(tagBytes) println(String(plaintext)) }
额外确认点:
- 解码后的IV为12字节,符合GCM推荐的标准长度
- 解码后的Tag为16字节,对应GCM默认的128位验证标签长度
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

