@@ -120,22 +120,17 @@ public void writeTo(StreamOutput out) throws IOException {
120120 /** The version where lucene directory API changed from BE to LE. */
121121 public static final int LE_VERSION = 5 ;
122122 public static final int HIGHER_KDF_ITERATION_COUNT_VERSION = 6 ;
123- public static final int CURRENT_VERSION = HIGHER_KDF_ITERATION_COUNT_VERSION ;
123+ public static final int CIPHER_KEY_BITS_256_VERSION = 7 ;
124+ public static final int CURRENT_VERSION = CIPHER_KEY_BITS_256_VERSION ;
124125
125126 /** The algorithm used to derive the cipher key from a password. */
126127 private static final String KDF_ALGO = "PBKDF2WithHmacSHA512" ;
127128
128129 /** The number of iterations to derive the cipher key. */
129130 private static final int KDF_ITERS = 210000 ;
130131
131- /**
132- * The number of bits for the cipher key.
133- *
134- * Note: The Oracle JDK 8 ships with a limited JCE policy that restricts key length for AES to 128 bits.
135- * This can be increased to 256 bits once minimum java 9 is the minimum java version.
136- * See http://www.oracle.com/technetwork/java/javase/terms/readme/jdk9-readme-3852447.html#jce
137- * */
138- private static final int CIPHER_KEY_BITS = 128 ;
132+ /** The number of bits for the cipher key (256 bits are supported as of Java 9).*/
133+ private static final int CIPHER_KEY_BITS = 256 ;
139134
140135 /** The number of bits for the GCM tag. */
141136 private static final int GCM_TAG_BITS = 128 ;
@@ -156,6 +151,7 @@ public void writeTo(StreamOutput out) throws IOException {
156151 // 4: remove distinction between string/files, ES 6.8/7.1
157152 // 5: Lucene directory API changed to LE, ES 8.0
158153 // 6: increase KDF iteration count, ES 8.14
154+ // 7: increase cipher key length to 256 bits, ES 9.0
159155
160156 /** The metadata format version used to read the current keystore wrapper. */
161157 private final int formatVersion ;
@@ -318,8 +314,9 @@ public boolean hasPassword() {
318314 return hasPassword ;
319315 }
320316
321- private static Cipher createCipher (int opmode , char [] password , byte [] salt , byte [] iv , int kdfIters ) throws GeneralSecurityException {
322- PBEKeySpec keySpec = new PBEKeySpec (password , salt , kdfIters , CIPHER_KEY_BITS );
317+ private static Cipher createCipher (int opmode , char [] password , byte [] salt , byte [] iv , int kdfIters , int cipherKeyBits )
318+ throws GeneralSecurityException {
319+ PBEKeySpec keySpec = new PBEKeySpec (password , salt , kdfIters , cipherKeyBits );
323320 SecretKeyFactory keyFactory = SecretKeyFactory .getInstance (KDF_ALGO );
324321 SecretKey secretKey ;
325322 try {
@@ -343,6 +340,11 @@ private static int getKdfIterationCountForVersion(int formatVersion) {
343340 return formatVersion < HIGHER_KDF_ITERATION_COUNT_VERSION ? 10000 : KDF_ITERS ;
344341 }
345342
343+ private static int getCipherKeyBitsForVersion (int formatVersion ) {
344+ // cipher key length was increased in version 7; it was 128 bits in previous versions
345+ return formatVersion < CIPHER_KEY_BITS_256_VERSION ? 128 : CIPHER_KEY_BITS ;
346+ }
347+
346348 /**
347349 * Decrypts the underlying keystore data.
348350 *
@@ -371,7 +373,14 @@ public void decrypt(char[] password) throws GeneralSecurityException, IOExceptio
371373 throw new SecurityException ("Keystore has been corrupted or tampered with" , e );
372374 }
373375
374- Cipher cipher = createCipher (Cipher .DECRYPT_MODE , password , salt , iv , getKdfIterationCountForVersion (formatVersion ));
376+ Cipher cipher = createCipher (
377+ Cipher .DECRYPT_MODE ,
378+ password ,
379+ salt ,
380+ iv ,
381+ getKdfIterationCountForVersion (formatVersion ),
382+ getCipherKeyBitsForVersion (formatVersion )
383+ );
375384 try (
376385 ByteArrayInputStream bytesStream = new ByteArrayInputStream (encryptedBytes );
377386 CipherInputStream cipherStream = new CipherInputStream (bytesStream , cipher );
@@ -409,11 +418,12 @@ private static byte[] readByteArray(DataInput input) throws IOException {
409418 }
410419
411420 /** Encrypt the keystore entries and return the encrypted data. */
412- private byte [] encrypt (char [] password , byte [] salt , byte [] iv , int kdfIterationCount ) throws GeneralSecurityException , IOException {
421+ private byte [] encrypt (char [] password , byte [] salt , byte [] iv , int kdfIterationCount , int cipherKeyBits )
422+ throws GeneralSecurityException , IOException {
413423 assert isLoaded ();
414424
415425 ByteArrayOutputStream bytes = new ByteArrayOutputStream ();
416- Cipher cipher = createCipher (Cipher .ENCRYPT_MODE , password , salt , iv , kdfIterationCount );
426+ Cipher cipher = createCipher (Cipher .ENCRYPT_MODE , password , salt , iv , kdfIterationCount , cipherKeyBits );
417427 try (
418428 CipherOutputStream cipherStream = new CipherOutputStream (bytes , cipher );
419429 DataOutputStream output = new DataOutputStream (cipherStream )
@@ -456,7 +466,13 @@ public synchronized void save(Path configDir, char[] password, boolean preserveP
456466 byte [] iv = new byte [12 ];
457467 random .nextBytes (iv );
458468 // encrypted data
459- byte [] encryptedBytes = encrypt (password , salt , iv , getKdfIterationCountForVersion (CURRENT_VERSION ));
469+ byte [] encryptedBytes = encrypt (
470+ password ,
471+ salt ,
472+ iv ,
473+ getKdfIterationCountForVersion (CURRENT_VERSION ),
474+ getCipherKeyBitsForVersion (CURRENT_VERSION )
475+ );
460476
461477 // size of data block
462478 output .writeInt (4 + salt .length + 4 + iv .length + 4 + encryptedBytes .length );
0 commit comments