|
| 1 | +package com.mkyong.crypto.encryptor; |
| 2 | + |
| 3 | +import com.mkyong.crypto.utils.CryptoUtils; |
| 4 | + |
| 5 | +import javax.crypto.Cipher; |
| 6 | +import javax.crypto.SecretKey; |
| 7 | +import javax.crypto.spec.GCMParameterSpec; |
| 8 | +import java.nio.ByteBuffer; |
| 9 | +import java.nio.charset.Charset; |
| 10 | +import java.nio.charset.StandardCharsets; |
| 11 | + |
| 12 | +/** |
| 13 | + * AES-GCM inputs - 12 bytes IV, need the same IV and secret keys for encryption and decryption. |
| 14 | + * <p> |
| 15 | + * The output consist of iv, encrypted content and auth tag in the following format: |
| 16 | + * output = byte[] {i i i c c c c c c ...} |
| 17 | + * <p> |
| 18 | + * i = IV bytes |
| 19 | + * c = content bytes (encrypted content, auth tag) |
| 20 | + */ |
| 21 | +public class EncryptorAesGcm { |
| 22 | + |
| 23 | + // AES-CBC |
| 24 | + // IvParameterSpec, 16 bytes IV |
| 25 | + // private static final String ENCRYPT_ALGO = "AES/CBC/PKCS5Padding"; |
| 26 | + |
| 27 | + private static final String ENCRYPT_ALGO = "AES/GCM/NoPadding"; |
| 28 | + private static final int TAG_LENGTH_BIT = 128; |
| 29 | + private static final int IV_LENGTH_BYTE = 12; |
| 30 | + private static final int AES_KEY_BIT = 256; |
| 31 | + private static final Charset UTF_8 = StandardCharsets.UTF_8; |
| 32 | + |
| 33 | + // AES-GCM needs GCMParameterSpec |
| 34 | + public static byte[] encrypt(byte[] pText, SecretKey secret, byte[] iv) throws Exception { |
| 35 | + |
| 36 | + Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO); |
| 37 | + cipher.init(Cipher.ENCRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv)); |
| 38 | + byte[] encryptedText = cipher.doFinal(pText); |
| 39 | + return encryptedText; |
| 40 | + |
| 41 | + } |
| 42 | + |
| 43 | + // prefix IV length + IV bytes to cipher text |
| 44 | + public static byte[] encryptWithPrefixIV(byte[] pText, SecretKey secret, byte[] iv) throws Exception { |
| 45 | + |
| 46 | + byte[] cipherText = encrypt(pText, secret, iv); |
| 47 | + |
| 48 | + byte[] cipherTextWithIv = ByteBuffer.allocate(iv.length + cipherText.length) |
| 49 | + .put(iv) |
| 50 | + .put(cipherText) |
| 51 | + .array(); |
| 52 | + return cipherTextWithIv; |
| 53 | + |
| 54 | + } |
| 55 | + |
| 56 | + public static String decrypt(byte[] cText, SecretKey secret, byte[] iv) throws Exception { |
| 57 | + |
| 58 | + Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO); |
| 59 | + cipher.init(Cipher.DECRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv)); |
| 60 | + byte[] plainText = cipher.doFinal(cText); |
| 61 | + return new String(plainText, UTF_8); |
| 62 | + |
| 63 | + } |
| 64 | + |
| 65 | + public static String decryptWithPrefixIV(byte[] cText, SecretKey secret) throws Exception { |
| 66 | + |
| 67 | + ByteBuffer bb = ByteBuffer.wrap(cText); |
| 68 | + |
| 69 | + byte[] iv = new byte[IV_LENGTH_BYTE]; |
| 70 | + bb.get(iv); |
| 71 | + //bb.get(iv, 0, iv.length); |
| 72 | + |
| 73 | + byte[] cipherText = new byte[bb.remaining()]; |
| 74 | + bb.get(cipherText); |
| 75 | + |
| 76 | + String plainText = decrypt(cipherText, secret, iv); |
| 77 | + return plainText; |
| 78 | + |
| 79 | + } |
| 80 | + |
| 81 | + public static void main(String[] args) throws Exception { |
| 82 | + |
| 83 | + String OUTPUT_FORMAT = "%-30s:%s"; |
| 84 | + |
| 85 | + String pText = "Hello World AES-GCM, Welcome to Cryptography!"; |
| 86 | + |
| 87 | + // encrypt and decrypt need the same key. |
| 88 | + // get AES 256 bits (32 bytes) key |
| 89 | + SecretKey secretKey = CryptoUtils.getAESKey(AES_KEY_BIT); |
| 90 | + |
| 91 | + // encrypt and decrypt need the same IV. |
| 92 | + // AES-GCM needs IV 96-bit (12 bytes) |
| 93 | + byte[] iv = CryptoUtils.getRandomNonce(IV_LENGTH_BYTE); |
| 94 | + |
| 95 | + byte[] encryptedText = EncryptorAesGcm.encryptWithPrefixIV(pText.getBytes(UTF_8), secretKey, iv); |
| 96 | + |
| 97 | + System.out.println("\n------ AES GCM Encryption ------"); |
| 98 | + System.out.println(String.format(OUTPUT_FORMAT, "Input (plain text)", pText)); |
| 99 | + System.out.println(String.format(OUTPUT_FORMAT, "Key (hex)", CryptoUtils.hex(secretKey.getEncoded()))); |
| 100 | + System.out.println(String.format(OUTPUT_FORMAT, "IV (hex)", CryptoUtils.hex(iv))); |
| 101 | + System.out.println(String.format(OUTPUT_FORMAT, "Encrypted (hex) ", CryptoUtils.hex(encryptedText))); |
| 102 | + System.out.println(String.format(OUTPUT_FORMAT, "Encrypted (hex) (block = 16)", CryptoUtils.hexWithBlockSize(encryptedText, 16))); |
| 103 | + |
| 104 | + System.out.println("\n------ AES GCM Decryption ------"); |
| 105 | + System.out.println(String.format(OUTPUT_FORMAT, "Input (hex)", CryptoUtils.hex(encryptedText))); |
| 106 | + System.out.println(String.format(OUTPUT_FORMAT, "Input (hex) (block = 16)", CryptoUtils.hexWithBlockSize(encryptedText, 16))); |
| 107 | + System.out.println(String.format(OUTPUT_FORMAT, "Key (hex)", CryptoUtils.hex(secretKey.getEncoded()))); |
| 108 | + |
| 109 | + String decryptedText = EncryptorAesGcm.decryptWithPrefixIV(encryptedText, secretKey); |
| 110 | + |
| 111 | + System.out.println(String.format(OUTPUT_FORMAT, "Decrypted (plain text)", decryptedText)); |
| 112 | + |
| 113 | + } |
| 114 | + |
| 115 | +} |
0 commit comments