javax.crypto.AEADBadTagException:GCM中的mac签入失败

javax.crypto.AEADBadTagException:GCM中的mac签入失败,java,android,kotlin,encryption,aes-gcm,Java,Android,Kotlin,Encryption,Aes Gcm,我正在开发Android客户端。我们所拥有的: 服务器ecnrypts数据通过AES算法和GCM 服务器通过RESTAPI发送给客户端 客户端必须解密数据并显示它 我坚持走3步。我的问题是如何解密到达的数据。这是我已经尝试过的: class PrivateInfoDecrypter { companion object { const val SECRET_KEY = "secret_key" } private val bytes

我正在开发Android客户端。我们所拥有的:

  • 服务器ecnrypts数据通过AES算法和GCM
  • 服务器通过RESTAPI发送给客户端
  • 客户端必须解密数据并显示它
  • 我坚持走3步。我的问题是如何解密到达的数据。这是我已经尝试过的:

    class PrivateInfoDecrypter {
    
        companion object {
            const val SECRET_KEY = "secret_key"
        }
    
        private val bytes = ByteArray(16)
        private val gcmParameterSpec: GCMParameterSpec = GCMParameterSpec(128, bytes)
    
        fun decrypt(input: String, smsCode: String): String {
            val keySpec = generateKey(smsCode)
    
            val cipher: Cipher = Cipher.getInstance("AES/GCM/NoPadding")
            cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec)
    
            val original = cipher.doFinal(input.toByteArray())
    
            return String(original)
        }
    
        private fun generateKey(smsCode: String): SecretKeySpec {
            val spec: KeySpec = PBEKeySpec(smsCode.toCharArray(), SECRET_KEY.encodeToByteArray(), 65536, 256)
            val f: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
            val key: SecretKey = f.generateSecret(spec)
    
            return SecretKeySpec(key.encoded, "AES")
        }
    
    }
    
    我在logcat中得到这个错误:

    E/Whoops: javax.crypto.AEADBadTagException: mac check in GCM failed
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
        at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(BaseBlockCipher.java:1367)
        at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:1100)
        at javax.crypto.Cipher.doFinal(Cipher.java:2056)
    
    加密端:(服务器端代码)


    目前,如果没有看到加密方面,您的问题是无法回答的,即使它应该使用不同的语言。原因很简单-GCM模式通过添加HMAC值提供了额外的安全性,但不同语言和实现如何传输该值(“GCM标记”)是不同的。第二:在GCMParameterSpec中,值始终为16个零字节-这对于完整加密的安全性来说是可怕的。请将加密代码添加到您的问题中。@MichaelFehr editedDecryption缺少密文的十六进制解码,除此之外,解密可以在我的机器上工作(如果加密和解密中使用一致的数据,并且省略了未发布的
    getPseudoSaltedValue()
    )@Topaco这意味着服务器必须向客户端发送带有编码数据的IV?在代码中,用于加密(第1条注释)的IV(实际上是GCM的12个字节)和用于密钥派生的salt(这里称为
    secretKey
    )都是静态的(通常都是为每次加密随机生成的,并与密文一起发送,例如连接). 对于AES/GCM,出于安全原因,密钥/IV对只能使用一次至关重要。为了使用静态IV实现这一点,每次加密都必须更改密钥(或者同样使用静态salt,密码,这里称为
    optCode
    /
    smsCode
    )。
        private static final byte[] bytes = new byte[16];
        static final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, bytes);
    
        String encrypt(String input) throws Exception {
            KeySpec spec = new PBEKeySpec(otpCode.toCharArray(), secretKey.getEncoded(), 65536, 256);
            SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            byte[] key = f.generateSecret(spec).getEncoded();
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
            var salted = getPseudoSaltedValue(val);
            return new String(Hex.encode(cipher.doFinal(salted.getBytes())));
        }