44import com .uid2 .shared .model .EncryptionKey ;
55import com .uid2 .shared .model .KeyIdentifier ;
66import com .uid2 .shared .model .KeysetKey ;
7- import io .vertx .core .buffer .Buffer ;
87
98import javax .crypto .Cipher ;
109import javax .crypto .SecretKey ;
1110import javax .crypto .spec .GCMParameterSpec ;
1211import javax .crypto .spec .SecretKeySpec ;
1312import java .nio .charset .StandardCharsets ;
13+ import java .security .GeneralSecurityException ;
1414
1515public class AesGcm {
16- private static final String cipherScheme = "AES/GCM/NoPadding" ;
16+ private static final String CIPHER_SCHEME = "AES/GCM/NoPadding" ;
1717 public static final int GCM_AUTHTAG_LENGTH = 16 ;
1818 public static final int GCM_IV_LENGTH = 12 ;
1919
20+ private static final ThreadLocal <Cipher > CIPHER = ThreadLocal .withInitial (() -> {
21+ try {
22+ return Cipher .getInstance (CIPHER_SCHEME );
23+ } catch (GeneralSecurityException e ) {
24+ throw new RuntimeException ("Unable to create cipher" , e );
25+ }
26+ });
27+
2028 public static EncryptedPayload encrypt (byte [] b , KeysetKey key ) {
2129 return encrypt (b , key .getKeyBytes (), key .getKeyIdentifier ());
2230 }
@@ -32,11 +40,16 @@ private static EncryptedPayload encrypt(byte[] b, byte[] secretBytes, KeyIdentif
3240 public static byte [] encrypt (byte [] b , byte [] secretBytes ) {
3341 try {
3442 final SecretKey k = new SecretKeySpec (secretBytes , "AES" );
35- final Cipher c = Cipher . getInstance ( cipherScheme );
43+ final Cipher c = CIPHER . get ( );
3644 final byte [] ivBytes = Random .getBytes (GCM_IV_LENGTH );
37- GCMParameterSpec gcmParameterSpec = new GCMParameterSpec (GCM_AUTHTAG_LENGTH * 8 , ivBytes );
45+ final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec (GCM_AUTHTAG_LENGTH * 8 , ivBytes );
3846 c .init (Cipher .ENCRYPT_MODE , k , gcmParameterSpec );
39- return Buffer .buffer ().appendBytes (ivBytes ).appendBytes (c .doFinal (b )).getBytes ();
47+
48+ // Pre-allocate output: IV + ciphertext + auth tag
49+ final byte [] result = new byte [GCM_IV_LENGTH + c .getOutputSize (b .length )];
50+ System .arraycopy (ivBytes , 0 , result , 0 , GCM_IV_LENGTH );
51+ c .doFinal (b , 0 , b .length , result , GCM_IV_LENGTH );
52+ return result ;
4053 } catch (Exception e ) {
4154 throw new RuntimeException ("Unable to Encrypt" , e );
4255 }
@@ -50,9 +63,10 @@ public static byte[] decrypt(byte[] encryptedBytes, int offset, byte[] secretByt
5063 try {
5164 final SecretKey key = new SecretKeySpec (secretBytes , "AES" );
5265 final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec (GCM_AUTHTAG_LENGTH * 8 , encryptedBytes , offset , GCM_IV_LENGTH );
53- final Cipher c = Cipher . getInstance ( cipherScheme );
66+ final Cipher c = CIPHER . get ( );
5467 c .init (Cipher .DECRYPT_MODE , key , gcmParameterSpec );
55- return c .doFinal (encryptedBytes , offset + GCM_IV_LENGTH , encryptedBytes .length - offset - GCM_IV_LENGTH );
68+ final int dataOffset = offset + GCM_IV_LENGTH ;
69+ return c .doFinal (encryptedBytes , dataOffset , encryptedBytes .length - dataOffset );
5670 } catch (Exception e ) {
5771 throw new RuntimeException ("Unable to Decrypt" , e );
5872 }
0 commit comments