Last active
May 13, 2024 10:20
-
-
Save praseodym/f2499b3e14d872fe5b4a to your computer and use it in GitHub Desktop.
JDK8 AES-GCM code example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javax.crypto.*; | |
import javax.crypto.spec.GCMParameterSpec; | |
import java.nio.ByteBuffer; | |
import java.security.SecureRandom; | |
import java.util.Arrays; | |
public class AESGCMUpdateAAD2 { | |
// AES-GCM parameters | |
public static final int AES_KEY_SIZE = 128; // in bits | |
public static final int GCM_NONCE_LENGTH = 12; // in bytes | |
public static final int GCM_TAG_LENGTH = 16; // in bytes | |
public static void main(String[] args) throws Exception { | |
int testNum = 0; // pass | |
if (args.length > 0) { | |
testNum = Integer.parseInt(args[0]); | |
if (testNum <0 || testNum > 3) { | |
System.out.println("Usage: java AESGCMUpdateAAD2 [X]"); | |
System.out.println("X can be 0, 1, 2, 3"); | |
System.exit(1); | |
} | |
} | |
byte[] input = "Hello AES-GCM World!".getBytes(); | |
// Initialise random and generate key | |
SecureRandom random = SecureRandom.getInstanceStrong(); | |
KeyGenerator keyGen = KeyGenerator.getInstance("AES"); | |
keyGen.init(AES_KEY_SIZE, random); | |
SecretKey key = keyGen.generateKey(); | |
// Encrypt | |
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); | |
final byte[] nonce = new byte[GCM_NONCE_LENGTH]; | |
random.nextBytes(nonce); | |
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce); | |
cipher.init(Cipher.ENCRYPT_MODE, key, spec); | |
byte[] aad = "Whatever I like".getBytes();; | |
cipher.updateAAD(aad); | |
byte[] cipherText = cipher.doFinal(input); | |
// Decrypt; nonce is shared implicitly | |
cipher.init(Cipher.DECRYPT_MODE, key, spec); | |
// EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting | |
// because AAD value is altered | |
if (testNum == 1) aad[1]++; | |
cipher.updateAAD(aad); | |
// EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting | |
// because the encrypted data has been altered | |
if (testNum == 2) cipherText[10]++; | |
// EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting | |
// because the tag has been altered | |
if (testNum == 3) cipherText[cipherText.length - 2]++; | |
try { | |
byte[] plainText = cipher.doFinal(cipherText); | |
if (testNum != 0) { | |
System.out.println("Test Failed: expected AEADBadTagException not thrown"); | |
} else { | |
// check if the decryption result matches | |
if (Arrays.equals(input, plainText)) { | |
System.out.println("Test Passed: match!"); | |
} else { | |
System.out.println("Test Failed: result mismatch!"); | |
System.out.println(new String(plainText)); | |
} | |
} | |
} catch(AEADBadTagException ex) { | |
if (testNum == 0) { | |
System.out.println("Test Failed: unexpected ex " + ex); | |
ex.printStackTrace(); | |
} else { | |
System.out.println("Test Passed: expected ex " + ex); | |
} | |
} | |
} | |
} |
For completeness you should also add the IV to the tag, e.g.
cipher.updateAAD(nonce);
The nonce is already included in the calculation of the authentication tag. Not really any reason why you would need to include it in AAD as you can't verify it without already having the IV in the first place.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This code throws a error
java.security.InvalidAlgorithmParameterException: unknown parameter type.
while running the same on JDK 12