package at.favre.lib.bytes.otherPackage; import org.junit.Test; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import static org.junit.Assert.assertEquals; /** * A simple showcase for encryption and decryption with AES + GCM in Java */ public class AesGcmTest { private final SecureRandom secureRandom = new SecureRandom(); private final static int GCM_IV_LENGTH = 12; @Test public void testEncryption() throws Exception { //create new random key byte[] key = new byte[16]; secureRandom.nextBytes(key); SecretKey secretKey = new SecretKeySpec(key, "AES"); byte[] associatedData = "ProtocolVersion1".getBytes(StandardCharsets.UTF_8); //meta data you want to verify with the secret message String message = "the secret message"; byte[] cipherText = encrypt(message, secretKey, associatedData); String decrypted = decrypt(cipherText, secretKey, associatedData); assertEquals(message, decrypted); } /** * Encrypt a plaintext with given key. * * @param plaintext to encrypt (utf-8 encoding will be used) * @param secretKey to encrypt, must be AES type, see {@link SecretKeySpec} * @param associatedData optional, additional (public) data to verify on decryption with GCM auth tag * @return encrypted message * @throws Exception if anything goes wrong */ private byte[] encrypt(String plaintext, SecretKey secretKey, byte[] associatedData) throws Exception { byte[] iv = new byte[GCM_IV_LENGTH]; //NEVER REUSE THIS IV WITH SAME KEY secureRandom.nextBytes(iv); final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); if (associatedData != null) { cipher.updateAAD(associatedData); } byte[] cipherText = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length); byteBuffer.put(iv); byteBuffer.put(cipherText); return byteBuffer.array(); } /** * Decrypts encrypted message (see {@link #encrypt(String, SecretKey, byte[])}). * * @param cipherMessage iv with ciphertext * @param secretKey used to decrypt * @param associatedData optional, additional (public) data to verify on decryption with GCM auth tag * @return original plaintext * @throws Exception if anything goes wrong */ private String decrypt(byte[] cipherMessage, SecretKey secretKey, byte[] associatedData) throws Exception { final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); //use first 12 bytes for iv AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, GCM_IV_LENGTH); cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv); if (associatedData != null) { cipher.updateAAD(associatedData); } //use everything from 12 bytes on as ciphertext byte[] plainText = cipher.doFinal(cipherMessage, GCM_IV_LENGTH, cipherMessage.length - GCM_IV_LENGTH); return new String(plainText, StandardCharsets.UTF_8); } }