Java AES-GCM:AEADBadTagException:GCM中的mac签入失败

Java AES-GCM:AEADBadTagException:GCM中的mac签入失败,java,aes-gcm,Java,Aes Gcm,在第一次尝试实现AES-GCM时,我们面临生成AuthenticationTag的问题,加密密码和GCM mac检查最终失败。对于当前的实现,标记[]正在填充,但字节[]加密仍为空。正因为如此,cipher.doFinal(data1,offset)给出了“GCM中的mac检查失败””。字节数组的大小似乎存在一些问题,有人能告诉我们输出缓冲区的大小是根据什么来确定的吗?这应该分块进行吗 任何指向AES-GCM实施的指针/链接都将受到高度赞赏 以下是我们的执行情况: public class GC

在第一次尝试实现AES-GCM时,我们面临生成AuthenticationTag的问题,加密密码和GCM mac检查最终失败。对于当前的实现,
标记[]
正在填充,但
字节[]加密
仍为空。正因为如此,
cipher.doFinal(data1,offset)
给出了“
GCM中的mac检查失败”
”。字节数组的大小似乎存在一些问题,有人能告诉我们输出缓冲区的大小是根据什么来确定的吗?这应该分块进行吗

任何指向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;
    }
}

提前谢谢

我也有同样的问题。对我来说,这与编码字符串有关。我最后做了:

  • 从要加密的字符串中获取ASCII字节(本例中为UTF-8)
  • 加密字节
  • 以Base64字符串编码字节
  • 然后,要解密字符串,我执行了以下操作:

  • 将加密字符串解码为Base64字节
  • 解密Base64字节
  • 使用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上做这个,所以我不确定它有多不同。我发现这篇文章非常有用:

    你应该更新章节代码

    错误部分代码:

    //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);
    

    有时这可能是由线程问题引起的:.@thiagoprocciúncula什么样的问题?@IgorGanapolsky
    Cipher
    显然不是线程安全的根据我提到的问题,所以我的意思是,这个问题中提到的异常可能是由不同线程同时访问
    cipher
    实例引起的。我遇到了这个问题,在我的代码中添加了同步之后,它就停止了。但我在Android上使用这些API时遇到了各种各样的问题,所以可能还有其他问题。@Thiagoprocciúncula谢谢。你有没有找到一个可靠的解决方案,可以在Android上运行?我仍在寻找一个可行的解决方案。@IgorGanapolsky很遗憾,我还没有找到一种可靠的方法来使用Android密钥库API,而不会在某些特定设备上遇到严重的随机崩溃。长度是多少?Google,encryption iv。前12个字节仅用于加密。但在这种情况下,我不能完全确定IV字节是随机字节