Introduce an additional hasher (PBKDF2_STRETCH) (#65328)

* Introduce an additional hasher that is PBKDF2 but pads the input to > 14 chars before hashing to comply with FIPS Approve Only mode

* Introduce an additional hasher that is PBKDF2 but pads the input to > 14 chars before hashing to comply with FIPS Approve Only mode

* Addressing the PR feedback
adding doc changes

* Renaming the hash function + rephrasing the doc descriptions

* Removing leftover from the doc

* Return HexCharArray instead of Base64 encoding and avoid intermediate
String

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Lyudmila Fokina 2020-11-26 13:29:19 +01:00 committed by GitHub
parent 0f7bbed37e
commit c758dc7f4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 271 additions and 63 deletions

View file

@ -12,32 +12,53 @@ hashing algorithm by setting the <<static-cluster-setting,static>>
[[cache-hash-algo]]
.Cache hash algorithms
|=======================
| Algorithm | | | Description
| `ssha256` | | | Uses a salted `sha-256` algorithm (default).
| `md5` | | | Uses `MD5` algorithm.
| `sha1` | | | Uses `SHA1` algorithm.
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| Algorithm | | | Description
| `ssha256` | | | Uses a salted `sha-256` algorithm (default).
| `md5` | | | Uses `MD5` algorithm.
| `sha1` | | | Uses `SHA1` algorithm.
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations.
| `pbkdf2_1000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_1000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000 iterations.
| `pbkdf2_10000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_10000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations.
| `pbkdf2_50000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_50000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 50000 iterations.
| `pbkdf2_100000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_100000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 100000 iterations.
| `pbkdf2_500000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_500000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 500000 iterations.
| `pbkdf2_1000000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_1000000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000000 iterations.
| `noop`,`clear_text` | | | Doesn't hash the credentials and keeps it in clear text in
| `pbkdf2_stretch` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_1000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_10000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_50000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 50000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_100000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 100000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_500000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 500000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_1000000`| | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000000 iterations, after hashing the
initial input with SHA512 first.
| `noop`,`clear_text` | | | Doesn't hash the credentials and keeps it in clear text in
memory. CAUTION: keeping clear text is considered insecure
and can be compromised at the OS level (for example through
memory dumps and using `ptrace`).
@ -52,34 +73,55 @@ following:
[[password-hashing-algorithms]]
.Password hashing algorithms
|=======================
| Algorithm | | | Description
| Algorithm | | | Description
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds. (default)
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
| `bcrypt10` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
| `bcrypt11` | | | Uses `bcrypt` algorithm with salt generated in 2048 rounds.
| `bcrypt12` | | | Uses `bcrypt` algorithm with salt generated in 4096 rounds.
| `bcrypt13` | | | Uses `bcrypt` algorithm with salt generated in 8192 rounds.
| `bcrypt14` | | | Uses `bcrypt` algorithm with salt generated in 16384 rounds.
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds. (default)
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
| `bcrypt10` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
| `bcrypt11` | | | Uses `bcrypt` algorithm with salt generated in 2048 rounds.
| `bcrypt12` | | | Uses `bcrypt` algorithm with salt generated in 4096 rounds.
| `bcrypt13` | | | Uses `bcrypt` algorithm with salt generated in 8192 rounds.
| `bcrypt14` | | | Uses `bcrypt` algorithm with salt generated in 16384 rounds.
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations.
| `pbkdf2_1000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_1000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000 iterations.
| `pbkdf2_10000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_10000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations.
| `pbkdf2_50000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_50000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 50000 iterations.
| `pbkdf2_100000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_100000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 100000 iterations.
| `pbkdf2_500000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_500000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 500000 iterations.
| `pbkdf2_1000000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
| `pbkdf2_1000000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000000 iterations.
| `pbkdf2_stretch` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_1000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_10000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 10000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_50000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 50000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_100000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 100000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_500000` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 500000 iterations, after hashing the
initial input with SHA512 first.
| `pbkdf2_stretch_1000000`| | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
pseudorandom function using 1000000 iterations, after hashing the
initial input with SHA512 first.
|=======================

View file

@ -25,8 +25,8 @@ import java.util.Objects;
/**
* This MessageDigests class provides convenience methods for obtaining
* thread local {@link MessageDigest} instances for MD5, SHA-1, and
* SHA-256 message digests.
* thread local {@link MessageDigest} instances for MD5, SHA-1, SHA-256 and
* SHA-512 message digests.
*/
public final class MessageDigests {
@ -43,6 +43,7 @@ public final class MessageDigests {
private static final ThreadLocal<MessageDigest> MD5_DIGEST = createThreadLocalMessageDigest("MD5");
private static final ThreadLocal<MessageDigest> SHA_1_DIGEST = createThreadLocalMessageDigest("SHA-1");
private static final ThreadLocal<MessageDigest> SHA_256_DIGEST = createThreadLocalMessageDigest("SHA-256");
private static final ThreadLocal<MessageDigest> SHA_512_DIGEST = createThreadLocalMessageDigest("SHA-512");
/**
* Returns a {@link MessageDigest} instance for MD5 digests; note
@ -80,6 +81,18 @@ public final class MessageDigests {
return get(SHA_256_DIGEST);
}
/**
* Returns a {@link MessageDigest} instance for SHA-512 digests;
* note that the instance returned is thread local and must not be
* shared amongst threads.
*
* @return a thread local {@link MessageDigest} instance that
* provides SHA-512 message digest functionality.
*/
public static MessageDigest sha512() {
return get(SHA_512_DIGEST);
}
private static MessageDigest get(ThreadLocal<MessageDigest> messageDigest) {
MessageDigest instance = messageDigest.get();
instance.reset();

View file

@ -185,12 +185,12 @@ public enum Hasher {
PBKDF2() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(data, PBKDF2_DEFAULT_COST);
return getPbkdf2Hash(data, PBKDF2_DEFAULT_COST, PBKDF2_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(data, hash);
return verifyPbkdf2Hash(data, hash, PBKDF2_PREFIX);
}
},
@ -198,12 +198,12 @@ public enum Hasher {
PBKDF2_1000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(data, 1000);
return getPbkdf2Hash(data, 1000, PBKDF2_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(data, hash);
return verifyPbkdf2Hash(data, hash, PBKDF2_PREFIX);
}
},
@ -211,12 +211,12 @@ public enum Hasher {
PBKDF2_10000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(data, 10000);
return getPbkdf2Hash(data, 10000, PBKDF2_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(data, hash);
return verifyPbkdf2Hash(data, hash, PBKDF2_PREFIX);
}
},
@ -224,12 +224,12 @@ public enum Hasher {
PBKDF2_50000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(data, 50000);
return getPbkdf2Hash(data, 50000, PBKDF2_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(data, hash);
return verifyPbkdf2Hash(data, hash, PBKDF2_PREFIX);
}
},
@ -237,12 +237,12 @@ public enum Hasher {
PBKDF2_100000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(data, 100000);
return getPbkdf2Hash(data, 100000, PBKDF2_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(data, hash);
return verifyPbkdf2Hash(data, hash, PBKDF2_PREFIX);
}
},
@ -250,12 +250,12 @@ public enum Hasher {
PBKDF2_500000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(data, 500000);
return getPbkdf2Hash(data, 500000, PBKDF2_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(data, hash);
return verifyPbkdf2Hash(data, hash, PBKDF2_PREFIX);
}
},
@ -263,12 +263,103 @@ public enum Hasher {
PBKDF2_1000000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(data, 1000000);
return getPbkdf2Hash(data, 1000000, PBKDF2_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(data, hash);
return verifyPbkdf2Hash(data, hash, PBKDF2_PREFIX);
}
},
PBKDF2_STRETCH() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(new SecureString(hashSha512(data)), PBKDF2_DEFAULT_COST, PBKDF2_STRETCH_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(new SecureString(hashSha512(data)), hash, PBKDF2_STRETCH_PREFIX);
}
},
PBKDF2_STRETCH_1000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(new SecureString(hashSha512(data)), 1000, PBKDF2_STRETCH_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(new SecureString(hashSha512(data)), hash, PBKDF2_STRETCH_PREFIX);
}
},
PBKDF2_STRETCH_10000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(new SecureString(hashSha512(data)), 10000, PBKDF2_STRETCH_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(new SecureString(hashSha512(data)), hash, PBKDF2_STRETCH_PREFIX);
}
},
PBKDF2_STRETCH_50000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(new SecureString(hashSha512(data)), 50000, PBKDF2_STRETCH_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(new SecureString(hashSha512(data)), hash, PBKDF2_STRETCH_PREFIX);
}
},
PBKDF2_STRETCH_100000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(new SecureString(hashSha512(data)), 100000, PBKDF2_STRETCH_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(new SecureString(hashSha512(data)), hash, PBKDF2_STRETCH_PREFIX);
}
},
PBKDF2_STRETCH_500000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(new SecureString(hashSha512(data)), 500000, PBKDF2_STRETCH_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(new SecureString(hashSha512(data)), hash, PBKDF2_STRETCH_PREFIX);
}
},
PBKDF2_STRETCH_1000000() {
@Override
public char[] hash(SecureString data) {
return getPbkdf2Hash(new SecureString(hashSha512(data)), 1000000, PBKDF2_STRETCH_PREFIX);
}
@Override
public boolean verify(SecureString data, char[] hash) {
return verifyPbkdf2Hash(new SecureString(hashSha512(data)), hash, PBKDF2_STRETCH_PREFIX);
}
},
@ -388,6 +479,7 @@ public enum Hasher {
private static final String MD5_PREFIX = "{MD5}";
private static final String SSHA256_PREFIX = "{SSHA256}";
private static final String PBKDF2_PREFIX = "{PBKDF2}";
private static final String PBKDF2_STRETCH_PREFIX = "{PBKDF2_STRETCH}";
private static final int PBKDF2_DEFAULT_COST = 10000;
private static final int PBKDF2_KEY_LENGTH = 256;
private static final int BCRYPT_DEFAULT_COST = 10;
@ -442,6 +534,20 @@ public enum Hasher {
return PBKDF2_500000;
case "pbkdf2_1000000":
return PBKDF2_1000000;
case "pbkdf2_stretch":
return PBKDF2_STRETCH;
case "pbkdf2_stretch_1000":
return PBKDF2_STRETCH_1000;
case "pbkdf2_stretch_10000":
return PBKDF2_STRETCH_10000;
case "pbkdf2_stretch_50000":
return PBKDF2_STRETCH_50000;
case "pbkdf2_stretch_100000":
return PBKDF2_STRETCH_100000;
case "pbkdf2_stretch_500000":
return PBKDF2_STRETCH_500000;
case "pbkdf2_stretch_1000000":
return PBKDF2_STRETCH_1000000;
case "sha1":
return SHA1;
case "md5":
@ -468,6 +574,9 @@ public enum Hasher {
if (CharArrays.charsBeginsWith(BCRYPT_PREFIX, hash)) {
int cost = Integer.parseInt(new String(Arrays.copyOfRange(hash, BCRYPT_PREFIX.length(), hash.length - 54)));
return cost == BCRYPT_DEFAULT_COST ? Hasher.BCRYPT : resolve("bcrypt" + cost);
} else if (CharArrays.charsBeginsWith(PBKDF2_STRETCH_PREFIX, hash)) {
int cost = Integer.parseInt(new String(Arrays.copyOfRange(hash, PBKDF2_STRETCH_PREFIX.length(), hash.length - 90)));
return cost == PBKDF2_DEFAULT_COST ? Hasher.PBKDF2_STRETCH : resolve("pbkdf2_stretch_" + cost);
} else if (CharArrays.charsBeginsWith(PBKDF2_PREFIX, hash)) {
int cost = Integer.parseInt(new String(Arrays.copyOfRange(hash, PBKDF2_PREFIX.length(), hash.length - 90)));
return cost == PBKDF2_DEFAULT_COST ? Hasher.PBKDF2 : resolve("pbkdf2_" + cost);
@ -497,12 +606,12 @@ public enum Hasher {
return hasher.verify(data, hash);
}
private static char[] getPbkdf2Hash(SecureString data, int cost) {
private static char[] getPbkdf2Hash(SecureString data, int cost, String prefix) {
try {
// Base64 string length : (4*(n/3)) rounded up to the next multiple of 4 because of padding.
// n is 32 (PBKDF2_KEY_LENGTH in bytes) and 2 is because of the dollar sign delimiters.
CharBuffer result = CharBuffer.allocate(PBKDF2_PREFIX.length() + String.valueOf(cost).length() + 2 + 44 + 44);
result.put(PBKDF2_PREFIX);
CharBuffer result = CharBuffer.allocate(prefix.length() + String.valueOf(cost).length() + 2 + 44 + 44);
result.put(prefix);
result.put(String.valueOf(cost));
result.put("$");
byte[] salt = generateSalt(32);
@ -517,7 +626,7 @@ public enum Hasher {
}
}
private static boolean verifyPbkdf2Hash(SecureString data, char[] hash) {
private static boolean verifyPbkdf2Hash(SecureString data, char[] hash, String prefix) {
// Base64 string length : (4*(n/3)) rounded up to the next multiple of 4 because of padding.
// n is 32 (PBKDF2_KEY_LENGTH in bytes), so tokenLength is 44
final int tokenLength = 44;
@ -525,12 +634,12 @@ public enum Hasher {
char[] saltChars = null;
char[] computedPwdHash = null;
try {
if (CharArrays.charsBeginsWith(PBKDF2_PREFIX, hash) == false) {
if (CharArrays.charsBeginsWith(prefix, hash) == false) {
return false;
}
hashChars = Arrays.copyOfRange(hash, hash.length - tokenLength, hash.length);
saltChars = Arrays.copyOfRange(hash, hash.length - (2 * tokenLength + 1), hash.length - (tokenLength + 1));
int cost = Integer.parseInt(new String(Arrays.copyOfRange(hash, PBKDF2_PREFIX.length(), hash.length - (2 * tokenLength + 2))));
int cost = Integer.parseInt(new String(Arrays.copyOfRange(hash, prefix.length(), hash.length - (2 * tokenLength + 2))));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHMACSHA512");
PBEKeySpec keySpec = new PBEKeySpec(data.getChars(), Base64.getDecoder().decode(CharArrays.toUtf8Bytes(saltChars)),
cost, PBKDF2_KEY_LENGTH);
@ -597,4 +706,10 @@ public enum Hasher {
SECURE_RANDOM.nextBytes(salt);
return salt;
}
private static char[] hashSha512(SecureString text) {
MessageDigest md = MessageDigests.sha512();
md.update(CharArrays.toUtf8Bytes(text.getChars()));
return MessageDigests.toHexCharArray(md.digest());
}
}

View file

@ -36,6 +36,13 @@ public class HasherTests extends ESTestCase {
testHasherSelfGenerated(Hasher.PBKDF2_100000);
testHasherSelfGenerated(Hasher.PBKDF2_500000);
testHasherSelfGenerated(Hasher.PBKDF2_1000000);
testHasherSelfGenerated(Hasher.PBKDF2_STRETCH);
testHasherSelfGenerated(Hasher.PBKDF2_STRETCH_1000);
testHasherSelfGenerated(Hasher.PBKDF2_STRETCH_10000);
testHasherSelfGenerated(Hasher.PBKDF2_STRETCH_50000);
testHasherSelfGenerated(Hasher.PBKDF2_STRETCH_100000);
testHasherSelfGenerated(Hasher.PBKDF2_STRETCH_500000);
testHasherSelfGenerated(Hasher.PBKDF2_STRETCH_1000000);
}
public void testMd5SelfGenerated() throws Exception {
@ -78,6 +85,13 @@ public class HasherTests extends ESTestCase {
assertThat(Hasher.resolve("pbkdf2_100000"), sameInstance(Hasher.PBKDF2_100000));
assertThat(Hasher.resolve("pbkdf2_500000"), sameInstance(Hasher.PBKDF2_500000));
assertThat(Hasher.resolve("pbkdf2_1000000"), sameInstance(Hasher.PBKDF2_1000000));
assertThat(Hasher.resolve("pbkdf2_stretch"), sameInstance(Hasher.PBKDF2_STRETCH));
assertThat(Hasher.resolve("pbkdf2_stretch_1000"), sameInstance(Hasher.PBKDF2_STRETCH_1000));
assertThat(Hasher.resolve("pbkdf2_stretch_10000"), sameInstance(Hasher.PBKDF2_STRETCH_10000));
assertThat(Hasher.resolve("pbkdf2_stretch_50000"), sameInstance(Hasher.PBKDF2_STRETCH_50000));
assertThat(Hasher.resolve("pbkdf2_stretch_100000"), sameInstance(Hasher.PBKDF2_STRETCH_100000));
assertThat(Hasher.resolve("pbkdf2_stretch_500000"), sameInstance(Hasher.PBKDF2_STRETCH_500000));
assertThat(Hasher.resolve("pbkdf2_stretch_1000000"), sameInstance(Hasher.PBKDF2_STRETCH_1000000));
assertThat(Hasher.resolve("sha1"), sameInstance(Hasher.SHA1));
assertThat(Hasher.resolve("md5"), sameInstance(Hasher.MD5));
assertThat(Hasher.resolve("ssha256"), sameInstance(Hasher.SSHA256));
@ -132,11 +146,35 @@ public class HasherTests extends ESTestCase {
assertThat(Hasher.resolveFromHash(
"{PBKDF2}1000000$UuyhtjDEzWmE2wyY80akZKPWWpy2r2X50so41YML82U=$WFasYLelqbjQwt3EqFlUcwHiC38EZC45Iu/Iz0xL1GQ=".toCharArray()),
sameInstance(Hasher.PBKDF2_1000000));
assertThat(Hasher.resolveFromHash(
"{PBKDF2_STRETCH}1000$sTyix9e0zNINzq2aDZ+GD5+QlO94xVyf/bv4pWNhBxo=$4KuzGPy9HXnhY3ANHn8rcIRQuJHPB6cEtLwnOhDI5d4="
.toCharArray()),
sameInstance(Hasher.PBKDF2_STRETCH_1000));
assertThat(Hasher.resolveFromHash(
"{PBKDF2_STRETCH}10000$8M9+Ww0xkdY250CROEutsd8UP6CrJESw7ZAFu1NGORo=$ai0gxBPtHTfZU/nbNGwL5zjC+eo2/ANQM17L/tllVeo="
.toCharArray()),
sameInstance(Hasher.PBKDF2_STRETCH));
assertThat(Hasher.resolveFromHash(
"{PBKDF2_STRETCH}50000$uupwXiq8W0+jrLtC3/aqzuvyZlRarlmx1+CQGEnomlk=$by8q/+oRPPWwDE6an7B9/ndz7UZ1UQpaGY4CGurtPTI="
.toCharArray()),
sameInstance(Hasher.PBKDF2_STRETCH_50000));
assertThat(Hasher.resolveFromHash(
"{PBKDF2_STRETCH}100000$E9VqtV76PcrQuCZ6wOMMNvs4CMPcANTpzRw8Wjd24PU=$j56uKUvwbvmgQgNFkbV7SRQVZ2QOarokAgBeA8xcFD8="
.toCharArray()),
sameInstance(Hasher.PBKDF2_STRETCH_100000));
assertThat(Hasher.resolveFromHash(
"{PBKDF2_STRETCH}500000$4dpTEbu4jfjhDOjWY6xdsnxuQs4dg4QbNzZJ0Z1Tm4s=$Us/yrlCxVaW7mz0go1qIygFqGgcfUMgCZfIl2AvI4I8="
.toCharArray()),
sameInstance(Hasher.PBKDF2_STRETCH_500000));
assertThat(Hasher.resolveFromHash(
"{PBKDF2_STRETCH}1000000$eKeQvMztiIcqBynTNDFBseOBww3GBpHDZI6EPPVHYUw=$4587yrxUa02RZ1jeW1WOaMjRn5qT9iQ5/DIHk0nW2bE="
.toCharArray()),
sameInstance(Hasher.PBKDF2_STRETCH_1000000));
assertThat(Hasher.resolveFromHash("notavalidhashformat".toCharArray()), sameInstance(Hasher.NOOP));
}
private static void testHasherSelfGenerated(Hasher hasher) {
SecureString passwd = new SecureString(randomAlphaOfLength(10).toCharArray());
SecureString passwd = new SecureString(randomAlphaOfLength(between(6, 15)).toCharArray());
char[] hash = hasher.hash(passwd);
assertTrue(hasher.verify(passwd, hash));
}

View file

@ -70,7 +70,7 @@ public class UsersToolTests extends CommandTestCase {
IOUtils.rm(homeDir);
confDir = homeDir.resolve("config");
Files.createDirectories(confDir);
hasher = inFipsJvm() ? randomFrom(Hasher.PBKDF2, Hasher.PBKDF2_1000)
hasher = inFipsJvm() ? randomFrom(Hasher.PBKDF2, Hasher.PBKDF2_1000, Hasher.PBKDF2_STRETCH)
: randomFrom(Hasher.PBKDF2_1000, Hasher.PBKDF2, Hasher.BCRYPT, Hasher.BCRYPT9);
String defaultPassword = SecuritySettingsSourceField.TEST_PASSWORD;
Files.write(confDir.resolve("users"), Arrays.asList(