2

I have some AES/GCM encrypted data and wanted to decrypt it. I want to decrypt it bypassing authentication as the data does not contain authentication information(Data is encrypted by a third party application). I tried decryption with javax.crypto package and it is always throwing tag mismatch error. Is there any way to bypass this tag checking and decrypt data. Data is encrypted with AES128 and it is using 12 byte initialization vector.

Edit: I got a temporary solution for this issue. Not sure if this is correct method.

            Key key = new SecretKeySpec(hlsKey, "AES");
            GCMParameterSpec gCMParameterSpec = new     GCMParameterSpec(96, initialisationVector);
            final Cipher c = Cipher.getInstance("AES/GCM/NoPadding", "BC");
            c.init(Cipher.DECRYPT_MODE, key, gCMParameterSpec);

            byte[] nodata = new byte[len * 2];
            System.arraycopy(cipherText, 0, nodata, 0, len);
            byte[] plaindata = new byte[len * 2];
            try { 

                int decrypted_index = 0;
                while (decrypted_index < len) {
                    int cp = c.update(nodata, decrypted_index, nodata.length - decrypted_index, plaindata, decrypted_index);//doFinal(nodata);
                    decrypted_index += cp;
                }
               if(decrypted_index>=len){
                   System.arraycopy(plaindata, 0, plainText, 0, len);
                   retvalue=1;
               }
            } catch (Exception e) {
                e.printStackTrace();
            }
5
  • The description doesn't make any sense. You say it was AES-GCM encrypted. OK, that means it has an authentication tag. A tag length of zero is not supported, the smallest length (for special cases only) is 32. Then you say it doesn't contain authentication information. Maybe it isn't encrypted with AES-GCM. Commented Mar 12, 2018 at 16:17
  • @user1802492 Perhaps you mean there is no associated data not that it does not have authentication. There is no point in GCM mode without authentication.. Commented Mar 12, 2018 at 23:30
  • @JamesKPolk , I am getting this data from a third party application which encrypt data with AES/GCM and it is not providing authentication information, I want to decrypt this to know the actual data from device. Commented Mar 15, 2018 at 12:47
  • I got a temporary solution for this issue, Not sure if this is correct method Commented Mar 15, 2018 at 12:52
  • Please put the edit in an answer instead of in the question. The code is OK, but only if c.update does always return the plaintext data before validating the authentication tag. And that's implementation specific. It's also relatively inefficient wrt memory, but for a make-shift solution it is OK. Oh yeah, and the if(decrypted_index>=len){ test is of course bunk given the test within the while loop; decrypted_index must be >= len. Commented Aug 24, 2018 at 7:45

1 Answer 1

10

Warning: hazardous material. Only proceed if you understand the implications of handling the resulting plaintext without verifying the authentication tag first. This could lead not just to an attacker changing the message, but also compromising confidentiality through plaintext oracle attacks.


Yes, it is possible to decrypt the message without the authentication tag: if you read the GCM specification you can see that the IV for CTR is simply the IV, appended with four bytes 00000002 (i.e. a counter starting at zero, increased by one for calculating the authentication tag and again for the starting value of the counter for encryption).

So here's the code, where I do the inc twice as I used it to validate my counter code; it is of course possible to simply set the last byte to value 0x02 as well.

package nl.owlstead.so;

import java.nio.charset.StandardCharsets;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.Arrays;

public class DecryptGCMWithoutVerification {

    private static final int TAG_SIZE = 128;

    public DecryptGCMWithoutVerification() {
        // TODO Auto-generated constructor stub
    }

    public static void main(String[] args) throws Exception {
        
        // --- encryption using GCM
        
        Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKey key = new SecretKeySpec(new byte[16], "AES");
        byte[] ivBytes = new byte[12];
        GCMParameterSpec iv = new GCMParameterSpec(TAG_SIZE, ivBytes);
        gcm.init(Cipher.ENCRYPT_MODE, key, iv);
        byte[] ct = gcm.doFinal("owlstead".getBytes(StandardCharsets.US_ASCII));
        
        // --- decryption using underlying CTR mode
        
        Cipher ctr = Cipher.getInstance("AES/CTR/NoPadding");

        // WARNING: this is only correct for a 12 byte IV in GCM mode
        byte[] counter = Arrays.concatenate(ivBytes, new byte[4]);
        inc(counter);
        inc(counter);
        IvParameterSpec ctrIV = new IvParameterSpec(counter);
        ctr.init(Cipher.DECRYPT_MODE, key, ctrIV);
        
        byte[] pt = ctr.doFinal(ct, 0, ct.length - TAG_SIZE / Byte.SIZE);
        
        System.out.println(new String(pt, StandardCharsets.US_ASCII));
    }
    
    private static final byte inc(byte[] counter) {
        for (int i = counter.length - 1; i >= 0; i--) {
            if (++counter[i] != 0) {
                return 0;
            }
        }
        return 1;
    }

}

EDIT: this code is for an invalid tag or a tag that cannot be recalculated (the AAD could be missing, for instance). Remove - TAG_SIZE / Byte.SIZE from doFinal if the tag is missing entirely.

EDIT 2: note that this assumes a 12 byte / 96 bit IV, the default IV size for GCM. For any other size you need to calculate the IV first.

Sign up to request clarification or add additional context in comments.

5 Comments

You're in luck because your IV is 12 bytes. Needs additional work for other length IV's.
GCMBlockCipher from Bouncy Castle gives an example how to calculate CTR IV for all lengths of IV. Enough to implement similar to what they do with J0 in init(). Then you need to increment it and you can use SICBlockCipher to decrypt passing J0 as IV.
Note to readers: In case it's not obvious, this is the crypto equivalent of replacing a fuse with a bent nail. Sure, it may "work", but you've just bypassed a major safety feature with a dodgy hack and it's 100% your fault if your house burns down or your cryptosystem gets compromised.
I have had a similar issue here: stackoverflow.com/questions/71651996/… when porting a small python script to JavaScript, where I just could not find out, why I could not decrypt. After adding authTag = iv + "00000002", now it works like a charm. Still not fully understanding, why, and why the python library (cryptodome) is doing this "transparently"? Thanks for the hint!
@fritz In hindsight this may be AES-GCM-SIV (synthetic IV) but I would need to verify that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.