3
3
import com .softwareverde .logging .Logger ;
4
4
import com .softwareverde .util .ByteUtil ;
5
5
import com .softwareverde .util .bytearray .ByteArrayBuilder ;
6
+ import com .softwareverde .util .bytearray .ByteArrayReader ;
6
7
7
8
import javax .crypto .Cipher ;
8
9
import javax .crypto .KeyGenerator ;
17
18
public class AesKey {
18
19
public static final int DEFAULT_KEY_SIZE = 256 ;
19
20
21
+ protected static final byte FORMAT_VERSION = 1 ;
22
+
20
23
private static final String KEY_ALGORITHM = "AES" ;
21
24
private static final String ENCRYPTION_CIPHER = "AES/GCM/NoPadding" ; // Using GCM instead of CBC as it provides authentication
22
- private static final int LEGACY_AUTHENTICATION_TAG_LENGTH = 12 ; // default is tag length is not specified
23
25
private static final int AUTHENTICATION_TAG_LENGTH = 16 ; // max allowed value
24
26
private static final int INITIALIZATION_VECTOR_LENGTH = 12 ; // IV size of 12-bytes is specifically recommended for AES-GCM (more efficient than other lengths)
25
27
@@ -81,17 +83,19 @@ public byte[] encrypt(byte[] plainText) {
81
83
final byte [] initializationVectorBytes = _createInitializationVector ();
82
84
final AlgorithmParameterSpec initializationVector = new GCMParameterSpec (AUTHENTICATION_TAG_LENGTH * Byte .SIZE , initializationVectorBytes );
83
85
aesCipher .init (Cipher .ENCRYPT_MODE , _key , initializationVector );
84
- final byte [] cipherText = aesCipher .doFinal (plainText );
86
+ final byte [] javaCipherText = aesCipher .doFinal (plainText );
87
+ final byte [] cipherText = Arrays .copyOfRange (javaCipherText , 0 , javaCipherText .length - AUTHENTICATION_TAG_LENGTH );
88
+ final byte [] authenticationTag = Arrays .copyOfRange (javaCipherText , javaCipherText .length - AUTHENTICATION_TAG_LENGTH , javaCipherText .length );
85
89
86
90
// prefix cipher text with initialization vector
87
91
final ByteArrayBuilder byteArrayBuilder = new ByteArrayBuilder ();
88
- byteArrayBuilder .appendByte (( byte ) ( 0x80 | initializationVectorBytes . length ) );
89
- byteArrayBuilder .appendByte ((byte ) AUTHENTICATION_TAG_LENGTH );
92
+ byteArrayBuilder .appendByte (FORMAT_VERSION );
93
+ byteArrayBuilder .appendByte ((byte ) initializationVectorBytes . length );
90
94
byteArrayBuilder .appendBytes (initializationVectorBytes );
95
+ byteArrayBuilder .appendByte ((byte ) AUTHENTICATION_TAG_LENGTH );
96
+ byteArrayBuilder .appendBytes (authenticationTag );
91
97
byteArrayBuilder .appendBytes (cipherText );
92
98
93
- Arrays .fill (initializationVectorBytes , (byte ) 0 );
94
-
95
99
return byteArrayBuilder .build ();
96
100
}
97
101
catch (final Exception e ) {
@@ -103,24 +107,23 @@ public byte[] encrypt(byte[] plainText) {
103
107
public byte [] decrypt (byte [] cipherText ) {
104
108
try {
105
109
// remove initialization vector from cipher text
106
- byte initializationVectorLength = cipherText [0 ];
107
- int authenticationTagLength = LEGACY_AUTHENTICATION_TAG_LENGTH ;
108
- int ivStartIndex = 1 ;
109
- if ((initializationVectorLength & 0x80 ) != 0 ) {
110
- initializationVectorLength = (byte ) (initializationVectorLength ^ 0x80 );
111
- authenticationTagLength = cipherText [1 ];
112
- ivStartIndex = 2 ;
113
- }
114
- int cipherTextOffset = ByteUtil .byteToInteger (initializationVectorLength ) + ivStartIndex ;
115
- final byte [] initializationVectorBytes = Arrays .copyOfRange (cipherText , ivStartIndex , cipherTextOffset );
110
+ final ByteArrayReader byteArrayReader = new ByteArrayReader (cipherText );
111
+ byte formatVersion = byteArrayReader .readByte ();
112
+ byte initializationVectorLength = byteArrayReader .readByte ();
113
+ final byte [] initializationVectorBytes = byteArrayReader .readBytes ((int ) initializationVectorLength );
114
+ byte authenticationTagLength = byteArrayReader .readByte ();
115
+ byte [] authenticationTag = byteArrayReader .readBytes ((int ) authenticationTagLength );
116
+ final byte [] encryptedData = byteArrayReader .readBytes (byteArrayReader .remainingByteCount ());
117
+
118
+ final byte [] javaCipherText = new byte [encryptedData .length + authenticationTagLength ];
119
+ ByteUtil .setBytes (javaCipherText , encryptedData , 0 );
120
+ ByteUtil .setBytes (javaCipherText , authenticationTag , encryptedData .length );
121
+
116
122
final AlgorithmParameterSpec initializationVector = new GCMParameterSpec (authenticationTagLength * Byte .SIZE , initializationVectorBytes );
117
- final byte [] encryptedData = Arrays .copyOfRange (cipherText , cipherTextOffset , cipherText .length );
118
123
119
124
final Cipher aesCipher = Cipher .getInstance (ENCRYPTION_CIPHER );
120
125
aesCipher .init (Cipher .DECRYPT_MODE , _key , initializationVector );
121
- final byte [] plainText = aesCipher .doFinal (encryptedData );
122
-
123
- Arrays .fill (initializationVectorBytes , (byte ) 0 );
126
+ final byte [] plainText = aesCipher .doFinal (javaCipherText );
124
127
125
128
return plainText ;
126
129
}
0 commit comments