Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

While trying to implement AES-GCM for the first time, we are facing issue in generating AuthenticationTag, Encrypted cipher & GCM mac check fails in the end. For out current implementation tag[] is being populated but byte[] encrypted remains empty. And because of this cipher.doFinal(data1, offset) gives ' mac check in GCM failed '. It appears to be some issue around the size of byte arrays, can someone please share on what basis should the output buffer size be determined? Should this be done in chunks?

Any pointers/links to AES-GCM implementation will be highly appreciated.

Following is our implementation:

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

It gives following exception:

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)

Thanks in advance!!

@IgorGanapolsky Cipher clearly isn't thread safe according to the question I mentioned, so what I meant is that the exception mentioned in this question might be caused by accessing the cipher instance by different threads at the same time. I've had this issue and it stopped happening after I added synchronization in my code. But I've had all sorts of issues while using these APIs on Android, so it might also have been something else. – Fred Porciúncula May 30, 2018 at 14:11 @ThiagoPorciúncula Thanks. Have you found a reliable solution to your OP which works for you on Android? I'm still hunting for a viable solution. – IgorGanapolsky May 30, 2018 at 14:43 @IgorGanapolsky I haven't found a reliable way to use the Android Keystore APIs without running into sad random crashes on some specific devices, unfortunately. – Fred Porciúncula May 30, 2018 at 14:51

I was having this same issue. For me, it had to do with encoding the string. I ended up doing:

  • Get ASCII bytes from string you want to encrypt (UTF-8 in your case)
  • Encrypt bytes
  • Encode bytes in Base64 string
  • Then to decrypt string I did:

  • Decode encrypted string to Base64 bytes
  • Decrypt Base64 bytes
  • Create new string using ASCII.
  • Here is the code :

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

    Any variables I didn't include how to initialize them can be inferred from the java docs. I was trying to do this in Android so I'm not sure how different it is. I found this post to be incredibly helpful: Java AES/GCM/NoPadding - What is cipher.getIV() giving me?

    Google, encryption iv. The first 12 bytes are only used for encrypting. But in this case, I'm not entirely sure the IV bytes are random bytes – Dakota Jay Whipple May 27, 2018 at 22:17 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);

    update the new code:

    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);
                    This one was really relevant for my case, but in addition to this I need to remove the cipher.updateAAD("MyAAD".getBytes("UTF-8")); line; basically the cipher.updateAAD().
    – Nah
                    Feb 17, 2022 at 9:19
                    cipher.update(tag, 0, tag.length, data1, offset); what is the tag here. In encryption the auth tag is generated automatically?
    – Bilal Bangash
                    Mar 1, 2022 at 5:46
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.