【问题标题】:AES-GCM: AEADBadTagException: mac check in GCM failedAES-GCM:AEADBadTagException:GCM 中的 mac 检查失败
【发布时间】:2015-02-08 17:57:39
【问题描述】:

在第一次尝试实现 AES-GCM 时,我们面临生成 AuthenticationTag、加密密码和 GCM mac 检查最终失败的问题。对于当前的实现,tag[] 正在填充,但byte[] encrypted 仍然为空。正因为如此,cipher.doFinal(data1, offset) 给出了“mac check in GCM failed”。字节数组的大小似乎存在一些问题,有人可以分享在什么基础上确定输出缓冲区大小吗?这应该分块完成吗?

任何指向 AES-GCM 实现的指针/链接都将受到高度赞赏。

以下是我们的实现:

public class GCMTest {

    public static void main(String[] args) throws Exception {

        //***********************************************************
        //Key
        byte[] key = MessageDigest.getInstance("MD5").digest("1234567890123456".getBytes("UTF-8"));//this is the random key

        //Iv
        SecureRandom srand = SecureRandom.getInstance("SHA1PRNG");
        byte[] iv = new byte[256];
        srand.nextBytes(iv);

        //Input
        byte[] data="inputPlainText".getBytes();

        final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * Byte.SIZE, iv);

        //***********************************************************
        //Encryption
        final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", new BouncyCastleProvider());
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), gcmParameterSpec);

        cipher.updateAAD("MyAAD".getBytes("UTF-8"));

        //Encrypted output
        final byte[] encrypted = new byte[cipher.getOutputSize(data.length)];
        cipher.update(data, 0, data.length, encrypted, 0);  //Not being updated for current data. 

        //Tag output
        byte[] tag = new byte[cipher.getOutputSize(data.length)];
        cipher.doFinal(tag, 0);


        //***********************************************************
        //Decryption
        final SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);

        cipher.updateAAD("MyAAD".getBytes("UTF-8"));

        //What size should be assigned to outputBuffer?
        final byte[] data1 = new byte[256];

        int offset = cipher.update(encrypted, 0, encrypted.length, data1, 0);
        cipher.update(tag, 0, tag.length, data1, offset);
        cipher.doFinal(data1, offset);

        boolean isValid = checkEquals(data, data1);
        System.out.println("isValid :"+isValid);
    }

    private static boolean checkEquals(byte[] a, byte[] b)
    {
        int diff = a.length ^ b.length;
        for(int i = 0; i < a.length && i < b.length; i++)
            diff |= a[i] ^ b[i];
        return diff == 0;
    }
}

它给出了以下异常:

Exception in thread "main" javax.crypto.AEADBadTagException: mac check in GCM failed
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(Unknown Source)
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
    at javax.crypto.Cipher.doFinal(Cipher.java:2068)
    at GCMTest.main(GCMTest.java:56)

提前致谢!!

【问题讨论】:

  • 有时这可能是由线程问题引起的:Cipher isn't thread safe.
  • @ThiagoPorciúncula 什么样的问题?
  • @IgorGanapolsky Cipher 根据我提到的问题显然不是线程安全的,所以我的意思是这个问题中提到的异常可能是由不同的访问 cipher 实例引起的同时线程。我遇到了这个问题,在我的代码中添加同步后它就停止了。但是我在 Android 上使用这些 API 时遇到了各种各样的问题,所以也可能是其他问题。
  • @ThiagoPorciúncula 谢谢。您是否找到了适用于 Android 的可靠 OP 解决方案?我仍在寻找可行的解决方案。
  • @IgorGanapolsky 不幸的是,我还没有找到一种可靠的方法来使用 Android Keystore API 而不会在某些特定设备上遇到可悲的随机崩溃。

标签: java aes-gcm


【解决方案1】:

我遇到了同样的问题。对我来说,它与编码字符串有关。我最终做了:

  1. 从您要加密的字符串中获取 ASCII 字节(在您的情况下为 UTF-8)
  2. 加密字节
  3. 在 Base64 字符串中编码字节

然后解密我所做的字符串:

  1. 将加密字符串解码为 Base64 字节
  2. 解密 Base64 字节
  3. 使用 ASCII 创建新字符串。

代码如下:

private String encrypt(String src) {
    byte[] srcBytes = src.getBytes(StandardCharsets.US_ASCII);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, secureRandom);

    byte[] cipherText = cipher.doFinal(srcBytes);
    byte[] encryptedBytes = new byte[12 + cipherText.length];

    System.arraycopy(ivBytes, 0, encryptedBytes, 0, 12);
    System.arraycopy(cipherText, 0, encryptedBytes, 12, cipherText.length);

    return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
}

private String decrypt(String encryptedString) {
    byte[] encryptedBytes = Base64.decode(encryptedString, Base64.DEFAULT);

    cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, encryptedBytes, 0, 12));
    byte[] decryptedBytes = cipher.doFinal(encryptedBytes, 12, encryptedBytes.length-12);

    return Base64.encodeToString(decryptedBytes, Base64.DEFAULT);
}

我没有包括如何初始化它们的任何变量都可以从 java 文档中推断出来。我试图在 Android 中做到这一点,所以我不确定它有多么不同。我发现这篇文章非常有帮助:Java AES/GCM/NoPadding - What is cipher.getIV() giving me?

【讨论】:

  • 什么是length-12??
  • 谷歌,加密 iv。前 12 个字节仅用于加密。但在这种情况下,我不完全确定 IV 字节是随机字节
【解决方案2】:

你应该更新部分代码

错误部分代码:

//What size should be assigned to outputBuffer?
final byte[] data1 = new byte[256];

int offset = cipher.update(encrypted, 0, encrypted.length, data1, 0);
cipher.update(tag, 0, tag.length, data1, offset);
cipher.doFinal(data1, offset);

更新新代码:

final byte[] data1 = new byte[encrypted.length];
int offset = cipher.update(encrypted, 0, encrypted.length, data1, 0);
offset += cipher.update(tag, 0, tag.length, data1, offset);
cipher.doFinal(data1, offset);

【讨论】:

  • 这与我的情况非常相关,但除此之外,我还需要删除 cipher.updateAAD("MyAAD".getBytes("UTF-8")); 行;基本上是cipher.updateAAD()
  • cipher.update(tag, 0, tag.length, data1, offset);这里的标签是什么。加密中auth标签是自动生成的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-29
  • 2016-06-04
  • 2021-10-08
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
  • 2015-03-29
相关资源
最近更新 更多