diff --git a/core/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java b/core/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java index 86cdbcee6d..8d72edea37 100644 --- a/core/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java +++ b/core/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java @@ -46,13 +46,17 @@ final class AesBytesEncryptor implements BytesEncryptor { private final BytesKeyGenerator ivGenerator; + public AesBytesEncryptor(String password, CharSequence salt) { + this(password, salt, null); + } + public AesBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator) { PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), Hex.decode(salt), 1024, 256); SecretKey secretKey = newSecretKey("PBKDF2WithHmacSHA1", keySpec); this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES"); encryptor = newCipher(AES_ALGORITHM); decryptor = newCipher(AES_ALGORITHM); - this.ivGenerator = ivGenerator; + this.ivGenerator = ivGenerator != null ? ivGenerator : NULL_IV_GENERATOR; } public byte[] encrypt(byte[] bytes) { @@ -60,27 +64,41 @@ final class AesBytesEncryptor implements BytesEncryptor { byte[] iv = ivGenerator.generateKey(); initCipher(encryptor, Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); byte[] encrypted = doFinal(encryptor, bytes); - return concatenate(iv, encrypted); + return ivGenerator != NULL_IV_GENERATOR ? concatenate(iv, encrypted) : encrypted; } } public byte[] decrypt(byte[] encryptedBytes) { synchronized (decryptor) { - byte[] iv = ivPart(encryptedBytes); + byte[] iv = iv(encryptedBytes); initCipher(decryptor, Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); - return doFinal(decryptor, cipherPart(encryptedBytes, iv)); + return doFinal(decryptor, ivGenerator != NULL_IV_GENERATOR ? encrypted(encryptedBytes, iv.length) : encryptedBytes); } } // internal helpers - private byte[] ivPart(byte[] encrypted) { - return subArray(encrypted, 0, ivGenerator.getKeyLength()); + private byte[] iv(byte[] encrypted) { + return ivGenerator != NULL_IV_GENERATOR ? subArray(encrypted, 0, ivGenerator.getKeyLength()) : NULL_IV_GENERATOR.generateKey(); } - private byte[] cipherPart(byte[] encrypted, byte[] iv) { - return subArray(encrypted, iv.length, encrypted.length); + private byte[] encrypted(byte[] encryptedBytes, int ivLength) { + return subArray(encryptedBytes, ivLength, encryptedBytes.length); } private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding"; + + private static final BytesKeyGenerator NULL_IV_GENERATOR = new BytesKeyGenerator() { + + private final byte[] VALUE = new byte[16]; + + public int getKeyLength() { + return VALUE.length; + } + + public byte[] generateKey() { + return VALUE; + } + + }; } diff --git a/core/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java b/core/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java index af4f056b56..2dbc14e44e 100644 --- a/core/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java +++ b/core/src/main/java/org/springframework/security/crypto/encrypt/Encryptors.java @@ -52,7 +52,7 @@ public class Encryptors { /** * Creates an encryptor for queryable text strings that uses standard password-based encryption. - * Uses a shared, or constant 16 byte initialization vector so encrypting the same data results in the same encryption result. + * Uses a 16-byte all-zero initialization vector so encrypting the same data results in the same encryption result. * This is done to allow encrypted data to be queried against. * Encrypted text is hex-encoded. * @@ -60,7 +60,7 @@ public class Encryptors { * @param salt a hex-encoded, random, site-global salt value to use to generate the secret key */ public static TextEncryptor queryableText(CharSequence password, CharSequence salt) { - return new HexEncodingTextEncryptor(new AesBytesEncryptor(password.toString(), salt, KeyGenerators.shared(16))); + return new HexEncodingTextEncryptor(new AesBytesEncryptor(password.toString(), salt)); } /**