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

Kotlin加密密文Java解密时触发javax.crypto.AEADBadTagException标签不匹配

解决AES/GCM解密时的javax.crypto.AEADBadTagException: Tag mismatch!异常

问题场景

从Kotlin应用向Java应用发送AES/GCM加密后的密文,Java端解密时触发以下异常:

Exception in thread "main" javax.crypto.AEADBadTagException: Tag mismatch!
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:620)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116)
at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at crypto.CryptoCore.c3andC4(CryptoCore.java:61)
at PokusMain.main(PokusMain.java:197)

两端代码

Kotlin加密代码

fun getFinalCipher(): String {
    //generating key from some parameters
    var ukeyString = hash(appParameters.userKey + "user" + userCryptogram.nonce.toString() +
            receiverCryptogram.nonce.toString(), "SHA-1")
    if (ukeyString != null) {
        ukeyString = ukeyString!!.substring(0,16)
    }
    val command = "unlock"
    val ukey: SecretKey = SecretKeySpec(ukeyString!!.toByteArray(), "AES")
    val parameterSpec = GCMParameterSpec(128, getUserIv()) //IV has the length of 12
    val cipherC3 = Cipher.getInstance("AES/GCM/NoPadding")
    cipherC3.init(Cipher.ENCRYPT_MODE, ukey, parameterSpec)
    val plaintext = appParameters.atu + command.toByteArray()
    var ciphertext = cipherC3.doFinal(plaintext)
    val ciphertextHex = ciphertext.toHex()
    return ciphertextHex
}

Java解密代码

public void c3andC4(String ciphertextHex) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
    byte[] ciphertext = decodeHexString(ciphertextHex);
    String userKey = hash(obuParameters.getDriverKey() + userCryptogram.getHatu(), "SHA-1");
    String ukeyString = hash(userKey + "user" + userCryptogram.getNonce() + receiverCryptogram.getNonce(),"SHA-1");
    userKey= userKey.substring(0, 16);
    ukeyString = ukeyString.substring(0,16);
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    SecretKey ukey = new SecretKeySpec(ukeyString.getBytes(),"AES");
    GCMParameterSpec parameterSpec = new GCMParameterSpec(128, userCryptogram.getIv()); //IV has the length of 12
    cipher.init(Cipher.DECRYPT_MODE,ukey, parameterSpec);
    byte[] decryptedMessage = cipher.doFinal(ciphertext); //this line throws exception
}

问题分析

Tag mismatch!是AES/GCM模式的典型异常,说明解密端的密钥、IV、密文(含认证标签)三者中至少有一个与加密端不一致。从代码对比看,核心问题是:

  1. 密钥生成逻辑完全不一致

    • Kotlin端直接用原始的appParameters.userKey拼接字符串后做SHA-1,取前16字节作为密钥
    • Java端先对obuParameters.getDriverKey() + userCryptogram.getHatu()做SHA-1得到userKey,再用这个hash结果拼接字符串生成密钥
      这导致两端AES密钥完全不同,是触发异常的主要原因。
  2. 其他潜在问题:

    • IV一致性:需确认Kotlin的getUserIv()返回的字节数组与Java的userCryptogram.getIv()完全一致,避免toString或编码转换导致的差异
    • Hex编解码:Kotlin的toHex()方法与Java的decodeHexString()需采用相同规则(如大小写、无分隔符)
    • 明文拼接:Kotlin端appParameters.atu + command.toByteArray()如果atu是String,需确认其字节编码(如UTF-8)与解密端预期一致

修复建议

  1. 对齐密钥生成逻辑
    修改Java端代码,去掉多余的hash步骤,直接用原始的obuParameters.getDriverKey()生成密钥:

    // 移除这一行:String userKey = hash(obuParameters.getDriverKey() + userCryptogram.getHatu(), "SHA-1");
    String ukeyString = hash(obuParameters.getDriverKey() + "user" + userCryptogram.getNonce() + receiverCryptogram.getNonce(),"SHA-1");
    ukeyString = ukeyString.substring(0,16);
    
  2. 验证IV一致性
    打印两端IV的字节数组十六进制值,确认完全相同,避免类型转换(如Long转String再转字节)导致的差异。

  3. 确认Hex编解码逻辑
    确保Kotlin的toHex()生成纯十六进制字符串(无空格/分隔符),Java的decodeHexString()能正确解析对应大小写格式。

内容的提问来源于stack exchange,提问作者F. Texl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 13:51:30