Skip to content

Instantly share code, notes, and snippets.

@ecki
Created December 3, 2015 19:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ecki/7dc4c49cb0ee94ddce4e to your computer and use it in GitHub Desktop.
Save ecki/7dc4c49cb0ee94ddce4e to your computer and use it in GitHub Desktop.
package test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class Crypt
{
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException
{
Security.addProvider(new BouncyCastleProvider());
SecureRandom random = SecureRandom.getInstanceStrong();
// create random secret key
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, random);
SecretKey secretKey = keyGen.generateKey();
byte[] cipher = createEncrypted(secretKey);
// cipher[12]++; // introduce modification
decrypt(cipher, secretKey);
}
static byte[] createEncrypted(SecretKey key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException
{
// generate random IV (as GCMParameter)
byte ivbytes[] = new byte[12];
new SecureRandom().nextBytes(ivbytes); // no need for "strong"
GCMParameterSpec spec = new GCMParameterSpec(/*taglen(bits)=*/128, ivbytes);
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, key, spec);
assert spec.getIV().length == ivbytes.length;
assert c.getIV().length == spec.getIV().length;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(spec.getIV());
byte[] cb;
// write out sample 33 byte data not block aligned
cb = c.update("1234567".getBytes(StandardCharsets.UTF_8));
if (cb != null) bos.write(cb);
cb = c.doFinal("89012345612345678901234561".getBytes(StandardCharsets.UTF_8));
if (cb != null) bos.write(cb);
bos.close();
byte[] cipher = bos.toByteArray();
System.out.println("ciphertext (+12) len=" + cipher.length);
return cipher;
}
static void decrypt(byte[] cipherText, SecretKey key) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException
{
InputStream in = new ByteArrayInputStream(cipherText);
// read back IV
byte[] ivbytes = readFully(in, 12);
GCMParameterSpec spec = new GCMParameterSpec(128, ivbytes);
Cipher c = Cipher.getInstance("AES/GCM/NoPadding" /*, "BC" */ );
c.init(Cipher.DECRYPT_MODE, key, spec); // same as encrypt mode
CipherInputStream cis = new CipherInputStream(in, c);
// if we read exactly plaintext bytes BC will throw in close, SunJCE always in first(!) read
for(int i=0;i<32;i++) System.out.print(" " + Integer.toHexString(cis.read()));
System.out.println();
cis.close();
}
private static byte[] readFully(InputStream in, int i) throws IOException
{
byte[] b = new byte[i];
int len = i;
int off = 0;
do {
int read = in.read(b, off, len);
off+=read;
len-=read;
} while(len > 0);
return b;
}
}

No modification (BC or SunJCE) - Java 8:

ciphertext (+12) len=61
 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36

Modification BC, read 0..32

ciphertext (+12) len=61
 32 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36
Exception in thread "main" java.io.IOException: javax.crypto.BadPaddingException: mac check in GCM failed
	at javax.crypto.CipherInputStream.close(CipherInputStream.java:321)
	at test.Crypt.decrypt(Crypt.java:91)
    at test.Crypt.main(Crypt.java:42)
Caused by: javax.crypto.BadPaddingException: mac check in GCM failed
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
    at javax.crypto.Cipher.doFinal(Cipher.java:2048)
    at javax.crypto.CipherInputStream.close(CipherInputStream.java:314)
    ... 2 more

Modification BC, read 0..33 (EOF)

ciphertext (+12) len=61
 36 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36Exception in thread "main" java.io.IOException: javax.crypto.BadPaddingException: mac check in GCM failed
	at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:115)
	at javax.crypto.CipherInputStream.read(CipherInputStream.java:186)
	at test.Crypt.decrypt(Crypt.java:89)
	at test.Crypt.main(Crypt.java:42)
Caused by: javax.crypto.BadPaddingException: mac check in GCM failed
	at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
    at javax.crypto.Cipher.doFinal(Cipher.java:2048)
    at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:112)
    ... 3 more

Modification, SunJCE

ciphertext (+12) len=61
Exception in thread "main" java.io.IOException: javax.crypto.AEADBadTagException: Tag mismatch!
    at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:115)
    at javax.crypto.CipherInputStream.read(CipherInputStream.java:186)
    at test.Crypt.decrypt(Crypt.java:89)
    at test.Crypt.main(Crypt.java:42)
Caused by: javax.crypto.AEADBadTagException: Tag mismatch!
    at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:524)
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1023)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:960)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
    at javax.crypto.Cipher.doFinal(Cipher.java:2048)
    at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:112)
    ... 3 more
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment