mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 17:34:17 -04:00
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:
parent
0f7bbed37e
commit
c758dc7f4a
5 changed files with 271 additions and 63 deletions
|
@ -12,32 +12,53 @@ hashing algorithm by setting the <<static-cluster-setting,static>>
|
||||||
[[cache-hash-algo]]
|
[[cache-hash-algo]]
|
||||||
.Cache hash algorithms
|
.Cache hash algorithms
|
||||||
|=======================
|
|=======================
|
||||||
| Algorithm | | | Description
|
| Algorithm | | | Description
|
||||||
| `ssha256` | | | Uses a salted `sha-256` algorithm (default).
|
| `ssha256` | | | Uses a salted `sha-256` algorithm (default).
|
||||||
| `md5` | | | Uses `MD5` algorithm.
|
| `md5` | | | Uses `MD5` algorithm.
|
||||||
| `sha1` | | | Uses `SHA1` algorithm.
|
| `sha1` | | | Uses `SHA1` algorithm.
|
||||||
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
|
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
|
||||||
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
|
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
|
||||||
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
|
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
|
||||||
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
|
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
|
||||||
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
|
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
|
||||||
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
|
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
|
||||||
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
|
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
|
||||||
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
|
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
|
||||||
pseudorandom function using 10000 iterations.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
memory. CAUTION: keeping clear text is considered insecure
|
||||||
and can be compromised at the OS level (for example through
|
and can be compromised at the OS level (for example through
|
||||||
memory dumps and using `ptrace`).
|
memory dumps and using `ptrace`).
|
||||||
|
@ -52,34 +73,55 @@ following:
|
||||||
[[password-hashing-algorithms]]
|
[[password-hashing-algorithms]]
|
||||||
.Password hashing algorithms
|
.Password hashing algorithms
|
||||||
|=======================
|
|=======================
|
||||||
| Algorithm | | | Description
|
| Algorithm | | | Description
|
||||||
|
|
||||||
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds. (default)
|
| `bcrypt` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds. (default)
|
||||||
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
|
| `bcrypt4` | | | Uses `bcrypt` algorithm with salt generated in 16 rounds.
|
||||||
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
|
| `bcrypt5` | | | Uses `bcrypt` algorithm with salt generated in 32 rounds.
|
||||||
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
|
| `bcrypt6` | | | Uses `bcrypt` algorithm with salt generated in 64 rounds.
|
||||||
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
|
| `bcrypt7` | | | Uses `bcrypt` algorithm with salt generated in 128 rounds.
|
||||||
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
|
| `bcrypt8` | | | Uses `bcrypt` algorithm with salt generated in 256 rounds.
|
||||||
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
|
| `bcrypt9` | | | Uses `bcrypt` algorithm with salt generated in 512 rounds.
|
||||||
| `bcrypt10` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
|
| `bcrypt10` | | | Uses `bcrypt` algorithm with salt generated in 1024 rounds.
|
||||||
| `bcrypt11` | | | Uses `bcrypt` algorithm with salt generated in 2048 rounds.
|
| `bcrypt11` | | | Uses `bcrypt` algorithm with salt generated in 2048 rounds.
|
||||||
| `bcrypt12` | | | Uses `bcrypt` algorithm with salt generated in 4096 rounds.
|
| `bcrypt12` | | | Uses `bcrypt` algorithm with salt generated in 4096 rounds.
|
||||||
| `bcrypt13` | | | Uses `bcrypt` algorithm with salt generated in 8192 rounds.
|
| `bcrypt13` | | | Uses `bcrypt` algorithm with salt generated in 8192 rounds.
|
||||||
| `bcrypt14` | | | Uses `bcrypt` algorithm with salt generated in 16384 rounds.
|
| `bcrypt14` | | | Uses `bcrypt` algorithm with salt generated in 16384 rounds.
|
||||||
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
|
| `pbkdf2` | | | Uses `PBKDF2` key derivation function with `HMAC-SHA512` as a
|
||||||
pseudorandom function using 10000 iterations.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
||||||
|=======================
|
|=======================
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This MessageDigests class provides convenience methods for obtaining
|
* This MessageDigests class provides convenience methods for obtaining
|
||||||
* thread local {@link MessageDigest} instances for MD5, SHA-1, and
|
* thread local {@link MessageDigest} instances for MD5, SHA-1, SHA-256 and
|
||||||
* SHA-256 message digests.
|
* SHA-512 message digests.
|
||||||
*/
|
*/
|
||||||
public final class MessageDigests {
|
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> MD5_DIGEST = createThreadLocalMessageDigest("MD5");
|
||||||
private static final ThreadLocal<MessageDigest> SHA_1_DIGEST = createThreadLocalMessageDigest("SHA-1");
|
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_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
|
* Returns a {@link MessageDigest} instance for MD5 digests; note
|
||||||
|
@ -80,6 +81,18 @@ public final class MessageDigests {
|
||||||
return get(SHA_256_DIGEST);
|
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) {
|
private static MessageDigest get(ThreadLocal<MessageDigest> messageDigest) {
|
||||||
MessageDigest instance = messageDigest.get();
|
MessageDigest instance = messageDigest.get();
|
||||||
instance.reset();
|
instance.reset();
|
||||||
|
|
|
@ -185,12 +185,12 @@ public enum Hasher {
|
||||||
PBKDF2() {
|
PBKDF2() {
|
||||||
@Override
|
@Override
|
||||||
public char[] hash(SecureString data) {
|
public char[] hash(SecureString data) {
|
||||||
return getPbkdf2Hash(data, PBKDF2_DEFAULT_COST);
|
return getPbkdf2Hash(data, PBKDF2_DEFAULT_COST, PBKDF2_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(SecureString data, char[] hash) {
|
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() {
|
PBKDF2_1000() {
|
||||||
@Override
|
@Override
|
||||||
public char[] hash(SecureString data) {
|
public char[] hash(SecureString data) {
|
||||||
return getPbkdf2Hash(data, 1000);
|
return getPbkdf2Hash(data, 1000, PBKDF2_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(SecureString data, char[] hash) {
|
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() {
|
PBKDF2_10000() {
|
||||||
@Override
|
@Override
|
||||||
public char[] hash(SecureString data) {
|
public char[] hash(SecureString data) {
|
||||||
return getPbkdf2Hash(data, 10000);
|
return getPbkdf2Hash(data, 10000, PBKDF2_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(SecureString data, char[] hash) {
|
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() {
|
PBKDF2_50000() {
|
||||||
@Override
|
@Override
|
||||||
public char[] hash(SecureString data) {
|
public char[] hash(SecureString data) {
|
||||||
return getPbkdf2Hash(data, 50000);
|
return getPbkdf2Hash(data, 50000, PBKDF2_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(SecureString data, char[] hash) {
|
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() {
|
PBKDF2_100000() {
|
||||||
@Override
|
@Override
|
||||||
public char[] hash(SecureString data) {
|
public char[] hash(SecureString data) {
|
||||||
return getPbkdf2Hash(data, 100000);
|
return getPbkdf2Hash(data, 100000, PBKDF2_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(SecureString data, char[] hash) {
|
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() {
|
PBKDF2_500000() {
|
||||||
@Override
|
@Override
|
||||||
public char[] hash(SecureString data) {
|
public char[] hash(SecureString data) {
|
||||||
return getPbkdf2Hash(data, 500000);
|
return getPbkdf2Hash(data, 500000, PBKDF2_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(SecureString data, char[] hash) {
|
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() {
|
PBKDF2_1000000() {
|
||||||
@Override
|
@Override
|
||||||
public char[] hash(SecureString data) {
|
public char[] hash(SecureString data) {
|
||||||
return getPbkdf2Hash(data, 1000000);
|
return getPbkdf2Hash(data, 1000000, PBKDF2_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(SecureString data, char[] hash) {
|
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 MD5_PREFIX = "{MD5}";
|
||||||
private static final String SSHA256_PREFIX = "{SSHA256}";
|
private static final String SSHA256_PREFIX = "{SSHA256}";
|
||||||
private static final String PBKDF2_PREFIX = "{PBKDF2}";
|
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_DEFAULT_COST = 10000;
|
||||||
private static final int PBKDF2_KEY_LENGTH = 256;
|
private static final int PBKDF2_KEY_LENGTH = 256;
|
||||||
private static final int BCRYPT_DEFAULT_COST = 10;
|
private static final int BCRYPT_DEFAULT_COST = 10;
|
||||||
|
@ -442,6 +534,20 @@ public enum Hasher {
|
||||||
return PBKDF2_500000;
|
return PBKDF2_500000;
|
||||||
case "pbkdf2_1000000":
|
case "pbkdf2_1000000":
|
||||||
return 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":
|
case "sha1":
|
||||||
return SHA1;
|
return SHA1;
|
||||||
case "md5":
|
case "md5":
|
||||||
|
@ -468,6 +574,9 @@ public enum Hasher {
|
||||||
if (CharArrays.charsBeginsWith(BCRYPT_PREFIX, hash)) {
|
if (CharArrays.charsBeginsWith(BCRYPT_PREFIX, hash)) {
|
||||||
int cost = Integer.parseInt(new String(Arrays.copyOfRange(hash, BCRYPT_PREFIX.length(), hash.length - 54)));
|
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);
|
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)) {
|
} else if (CharArrays.charsBeginsWith(PBKDF2_PREFIX, hash)) {
|
||||||
int cost = Integer.parseInt(new String(Arrays.copyOfRange(hash, PBKDF2_PREFIX.length(), hash.length - 90)));
|
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);
|
return cost == PBKDF2_DEFAULT_COST ? Hasher.PBKDF2 : resolve("pbkdf2_" + cost);
|
||||||
|
@ -497,12 +606,12 @@ public enum Hasher {
|
||||||
return hasher.verify(data, hash);
|
return hasher.verify(data, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char[] getPbkdf2Hash(SecureString data, int cost) {
|
private static char[] getPbkdf2Hash(SecureString data, int cost, String prefix) {
|
||||||
try {
|
try {
|
||||||
// Base64 string length : (4*(n/3)) rounded up to the next multiple of 4 because of padding.
|
// 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.
|
// 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);
|
CharBuffer result = CharBuffer.allocate(prefix.length() + String.valueOf(cost).length() + 2 + 44 + 44);
|
||||||
result.put(PBKDF2_PREFIX);
|
result.put(prefix);
|
||||||
result.put(String.valueOf(cost));
|
result.put(String.valueOf(cost));
|
||||||
result.put("$");
|
result.put("$");
|
||||||
byte[] salt = generateSalt(32);
|
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.
|
// 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
|
// n is 32 (PBKDF2_KEY_LENGTH in bytes), so tokenLength is 44
|
||||||
final int tokenLength = 44;
|
final int tokenLength = 44;
|
||||||
|
@ -525,12 +634,12 @@ public enum Hasher {
|
||||||
char[] saltChars = null;
|
char[] saltChars = null;
|
||||||
char[] computedPwdHash = null;
|
char[] computedPwdHash = null;
|
||||||
try {
|
try {
|
||||||
if (CharArrays.charsBeginsWith(PBKDF2_PREFIX, hash) == false) {
|
if (CharArrays.charsBeginsWith(prefix, hash) == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
hashChars = Arrays.copyOfRange(hash, hash.length - tokenLength, hash.length);
|
hashChars = Arrays.copyOfRange(hash, hash.length - tokenLength, hash.length);
|
||||||
saltChars = Arrays.copyOfRange(hash, hash.length - (2 * tokenLength + 1), hash.length - (tokenLength + 1));
|
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");
|
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHMACSHA512");
|
||||||
PBEKeySpec keySpec = new PBEKeySpec(data.getChars(), Base64.getDecoder().decode(CharArrays.toUtf8Bytes(saltChars)),
|
PBEKeySpec keySpec = new PBEKeySpec(data.getChars(), Base64.getDecoder().decode(CharArrays.toUtf8Bytes(saltChars)),
|
||||||
cost, PBKDF2_KEY_LENGTH);
|
cost, PBKDF2_KEY_LENGTH);
|
||||||
|
@ -597,4 +706,10 @@ public enum Hasher {
|
||||||
SECURE_RANDOM.nextBytes(salt);
|
SECURE_RANDOM.nextBytes(salt);
|
||||||
return salt;
|
return salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static char[] hashSha512(SecureString text) {
|
||||||
|
MessageDigest md = MessageDigests.sha512();
|
||||||
|
md.update(CharArrays.toUtf8Bytes(text.getChars()));
|
||||||
|
return MessageDigests.toHexCharArray(md.digest());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,13 @@ public class HasherTests extends ESTestCase {
|
||||||
testHasherSelfGenerated(Hasher.PBKDF2_100000);
|
testHasherSelfGenerated(Hasher.PBKDF2_100000);
|
||||||
testHasherSelfGenerated(Hasher.PBKDF2_500000);
|
testHasherSelfGenerated(Hasher.PBKDF2_500000);
|
||||||
testHasherSelfGenerated(Hasher.PBKDF2_1000000);
|
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 {
|
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_100000"), sameInstance(Hasher.PBKDF2_100000));
|
||||||
assertThat(Hasher.resolve("pbkdf2_500000"), sameInstance(Hasher.PBKDF2_500000));
|
assertThat(Hasher.resolve("pbkdf2_500000"), sameInstance(Hasher.PBKDF2_500000));
|
||||||
assertThat(Hasher.resolve("pbkdf2_1000000"), sameInstance(Hasher.PBKDF2_1000000));
|
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("sha1"), sameInstance(Hasher.SHA1));
|
||||||
assertThat(Hasher.resolve("md5"), sameInstance(Hasher.MD5));
|
assertThat(Hasher.resolve("md5"), sameInstance(Hasher.MD5));
|
||||||
assertThat(Hasher.resolve("ssha256"), sameInstance(Hasher.SSHA256));
|
assertThat(Hasher.resolve("ssha256"), sameInstance(Hasher.SSHA256));
|
||||||
|
@ -132,11 +146,35 @@ public class HasherTests extends ESTestCase {
|
||||||
assertThat(Hasher.resolveFromHash(
|
assertThat(Hasher.resolveFromHash(
|
||||||
"{PBKDF2}1000000$UuyhtjDEzWmE2wyY80akZKPWWpy2r2X50so41YML82U=$WFasYLelqbjQwt3EqFlUcwHiC38EZC45Iu/Iz0xL1GQ=".toCharArray()),
|
"{PBKDF2}1000000$UuyhtjDEzWmE2wyY80akZKPWWpy2r2X50so41YML82U=$WFasYLelqbjQwt3EqFlUcwHiC38EZC45Iu/Iz0xL1GQ=".toCharArray()),
|
||||||
sameInstance(Hasher.PBKDF2_1000000));
|
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));
|
assertThat(Hasher.resolveFromHash("notavalidhashformat".toCharArray()), sameInstance(Hasher.NOOP));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testHasherSelfGenerated(Hasher hasher) {
|
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);
|
char[] hash = hasher.hash(passwd);
|
||||||
assertTrue(hasher.verify(passwd, hash));
|
assertTrue(hasher.verify(passwd, hash));
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class UsersToolTests extends CommandTestCase {
|
||||||
IOUtils.rm(homeDir);
|
IOUtils.rm(homeDir);
|
||||||
confDir = homeDir.resolve("config");
|
confDir = homeDir.resolve("config");
|
||||||
Files.createDirectories(confDir);
|
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);
|
: randomFrom(Hasher.PBKDF2_1000, Hasher.PBKDF2, Hasher.BCRYPT, Hasher.BCRYPT9);
|
||||||
String defaultPassword = SecuritySettingsSourceField.TEST_PASSWORD;
|
String defaultPassword = SecuritySettingsSourceField.TEST_PASSWORD;
|
||||||
Files.write(confDir.resolve("users"), Arrays.asList(
|
Files.write(confDir.resolve("users"), Arrays.asList(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue