You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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==")
}

额外排查点

  1. 确认密钥完全一致:加密和解密使用的Base64密钥必须完全相同,包括大小写、特殊字符,不能有额外空格或换行。
  2. 验证加密方的数据格式:确认加密方是否是将IV + 密文 + 标签拼接后再进行Base64编码,部分实现可能会将IV单独传递,而非嵌入到加密字符串中。
  3. 检查字符编码:确保解密后的字节数组使用与加密方一致的字符集(这里用UTF-8是通用选择,但需确认加密方是否相同)。

内容的提问来源于stack exchange,提问作者Dagon1292

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:24:54