java - 在 Android 中使用 AES/GCM/NoPadding 加密的消息解密时出错

标签 java android encryption aes aes-gcm

我目前正在使用 AES/GCM/NoPadding 来执行密码运算。

我的加密代码:

fun encrypt(plainText: ByteArray, key: Key): ByteArray? {
        var resultText: ByteArray? = null
        try {
            val cipher = Cipher.getInstance(ALGORITHM)
            cipher.init(Cipher.ENCRYPT_MODE, key)

            val cipherText = cipher.doFinal(plainText)

            resultText = ByteBuffer.allocate(1 + cipher.iv.size + cipherText.size)
                    .put(cipher.iv.size.toByte())
                    .put(cipher.iv)
                    .put(cipherText)
                    .array()
        } catch (e : Exception) {
            Logger.e(TAG, "Error encrypting plain text", e)
        }

        return resultText
    }

我的解密代码:

fun decrypt(cipherTextWithHeaders: ByteArray, key: Key): ByteArray? {
        var plainText: ByteArray? = null
        try {
            val cipher = Cipher.getInstance(ALGORITHM)

            val ivSize = cipherTextWithHeaders[0].toInt()
            val iv = ByteArray(ivSize)
            System.arraycopy(cipherTextWithHeaders, 1, iv, 0, ivSize)
            cipher.init(Cipher.DECRYPT_MODE, key, GCMParameterSpec(ivSize * 8, iv))

            val headerLen = 1 + ivSize

            val cipherText = ByteArray(cipherTextWithHeaders.size - headerLen)
            System.arraycopy(cipherTextWithHeaders, headerLen, cipherText, 0, cipherTextWithHeaders.size - headerLen)

            plainText = cipher.doFinal(cipherText)
        } catch (e : Exception) {
            Logger.e(TAG, "Error decrypting cipher text", e)
        }

        return plainText
    }

我在上面的 decrypt 方法中执行 doFinal 时遇到此异常:

javax.crypto.IllegalBlockSizeException
    at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
    at javax.crypto.Cipher.doFinal(Cipher.java:1736)

我在加密过程中尝试了以下选项:

val temp = ByteArray(12)
SecureRandom().nextBytes(temp)
cipher.init(Cipher.ENCRYPT_MODE, key, GCMParameterSpec(96, temp))

但这会引发以下错误:

java.security.InvalidAlgorithmParameterException: Caller-provided IV not permitted
    at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:85)
    at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
    at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:148)
    at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2659)
    at javax.crypto.Cipher.tryCombinations(Cipher.java:2570)
    at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2475)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:566)
    at javax.crypto.Cipher.init(Cipher.java:973)
    at javax.crypto.Cipher.init(Cipher.java:908)

最佳答案

GCM 授权标签长度与 IV 长度无关。 AES-GCM 的标准实际上是 12-bytes IV 和 128 位 GCM 标签,参见 RFC 5288, Section 3 .

例子:

String input = "abcdef";

byte[] key = new byte[16];
(new SecureRandom()).nextBytes(key);

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
byte[] ciphertext = cipher.doFinal(input.getBytes());
byte[] iv = cipher.getIV();
GCMParameterSpec gcmspec = cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
System.out.println("ciphertext: " + ciphertext.length + ", IV: " + iv.length + ", tLen: " + gcmspec.getTLen());

cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
byte[] plaintext = cipher.doFinal(ciphertext);

System.out.println("plaintext : " + new String(plaintext));

打印:

ciphertext: 22, IV: 12, tLen: 128
plaintext : abcdef

尝试将 GCMParameterSpec(ivSize * 8, iv) 更改为 GCMParameterSpec(128, iv)

尽管问题也可能出在外部,即密文可能编码错误或在某处被截断。检查 cipherText.length

java.security.InvalidAlgorithmParameterException: Caller-provided IV not permitted

这是 Android 加密实现的局限性;它想在加密过程中自己生成 IV。

关于java - 在 Android 中使用 AES/GCM/NoPadding 加密的消息解密时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51295133/

相关文章:

java - Android 应用程序说该文件不存在,但确实存在。我需要安装它

java - 带有用户输入的 Servlet?

android - 如何本地化Google Play和App Store应用程序链接?

java - 如何仅使用 XML 在 Android 中显示谷歌广告?

azure - 不在 Azure Blob 存储上启用加密的原因?

java - 通知取代了 Android 中的旧版本

java - 如何使用Dozer排除java中的字符串列表?

android - 扩展自定义布局时,选项卡出现在操作栏上方

encryption - 为什么私钥不应该逐字或以纯文本形式存储在本地计算机上?

windows - 我可以在不使用密码的情况下检查加密的 zip 存档的内容吗?