Android Kotlin AES/GCM解密抛出AEADBadTagException问题
AES/GCM解密抛出AEADBadTagException问题排查
解密其他应用采用AES/GCM模式加密的字符串时,调用自定义解密方法抛出致命异常,异常栈如下:
javax.crypto.AEADBadTagException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT at java.lang.reflect.Constructor.newInstance0(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:343) at com.android.org.conscrypt.OpenSSLCipher$EVP_AEAD.throwAEADBadTagExceptionIfAvailable(OpenSSLCipher.java:1216) at com.android.org.conscrypt.OpenSSLCipher$EVP_AEAD.doFinalInternal(OpenSSLCipher.java:1245) at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:363) at javax.crypto.Cipher.doFinal(Cipher.java:2055) at fr.lundimatin.core.utils.AESEncryptionUtil$Companion.decrypt(AESEncryptionUtil.kt:40)
我的Kotlin代码及测试数据如下:
const val IV_LENGTH = 16 fun getSecretKey(key: String) : SecretKey { val decodedKey: ByteArray = Base64.getDecoder().decode(key) val originalKey: SecretKey = SecretKeySpec(decodedKey, 0, decodedKey.size, "AES") return originalKey } // 测试加密字符串: "MjY2NjI4QkI0NDYwRTExMxPQoQrBDTPMWTTIAhJ5QH4cKjzmWIIfW0mBv3zIC7yHJr5nqfmmEYk34BW9ag==" fun decrypt(message: String): String { val cipher = Cipher.getInstance("AES/GCM/NoPadding") val ivString = message.substring(0, IV_LENGTH) val ivEncrypted = Base64.getDecoder().decode(ivString) val spec = GCMParameterSpec(128, ivEncrypted) cipher.init(Cipher.DECRYPT_MODE, getEncryptionKey(), spec) val messageEncrypted = Base64.getDecoder().decode(message.substring(IV_LENGTH)) val decryptedBytes = cipher.doFinal(messageEncrypted) return String(decryptedBytes, StandardCharsets.UTF_8) // 预期解密结果: 400172870-327468066356882-CHE } fun getEncryptionKey() : SecretKey { return getSecretKey("k8h1k9LyiHgcStf/crHlkw==") }
问题分析与解决方案
AEADBadTagException本质是GCM模式的认证标签验证失败,核心原因是密钥不匹配、IV处理错误、密文/标签被篡改,你的代码主要问题出在IV的处理逻辑上:
- 错误操作:直接对Base64编码后的字符串截取前16个字符作为IV的Base64再解码。正确流程应该是先解码整个加密后的Base64字符串为字节数组,再从字节数组中拆分IV、密文和标签。
- IV长度疑问:AES/GCM推荐使用12字节(96位)IV,你设置的
IV_LENGTH=16可能与加密方的IV长度不匹配,需要确认加密方的IV实际长度。
修改后的解密代码示例(假设加密方使用12字节IV):
const val IV_LENGTH = 12 // 按加密方实际IV长度调整,GCM推荐12字节 const val GCM_TAG_LENGTH = 128 // 标签长度,通常128位 fun getSecretKey(key: String): SecretKey { val decodedKey = Base64.getDecoder().decode(key) return SecretKeySpec(decodedKey, "AES") } // 测试加密字符串: "MjY2NjI4QkI0NDYwRTExMxPQoQrBDTPMWTTIAhJ5QH4cKjzmWIIfW0mBv3zIC7yHJr5nqfmmEYk34BW9ag==" fun decrypt(message: String): String { val cipher = Cipher.getInstance("AES/GCM/NoPadding") // 先解码整个加密字符串为字节数组 val fullData = Base64.getDecoder().decode(message) // 拆分IV和密文+标签 val iv = fullData.copyOfRange(0, IV_LENGTH) val ciphertextWithTag = fullData.copyOfRange(IV_LENGTH, fullData.size) val spec = GCMParameterSpec(GCM_TAG_LENGTH, iv) cipher.init(Cipher.DECRYPT_MODE, getEncryptionKey(), spec) val decryptedBytes = cipher.doFinal(ciphertextWithTag) return String(decryptedBytes, StandardCharsets.UTF_8) } fun getEncryptionKey(): SecretKey { return getSecretKey("k8h1k9LyiHgcStf/crHlkw==") }
额外排查点
- 确认密钥完全一致:加密和解密使用的Base64密钥必须完全相同,包括大小写、特殊字符,不能有额外空格或换行。
- 验证加密方的数据格式:确认加密方是否是将IV + 密文 + 标签拼接后再进行Base64编码,部分实现可能会将IV单独传递,而非嵌入到加密字符串中。
- 检查字符编码:确保解密后的字节数组使用与加密方一致的字符集(这里用UTF-8是通用选择,但需确认加密方是否相同)。
内容的提问来源于stack exchange,提问作者Dagon1292
相关产品推荐
相关产品推荐

