diff --git a/docs/reference/settings/security-hash-settings.asciidoc b/docs/reference/settings/security-hash-settings.asciidoc index 9cc2d960db7c..a0639203469b 100644 --- a/docs/reference/settings/security-hash-settings.asciidoc +++ b/docs/reference/settings/security-hash-settings.asciidoc @@ -12,32 +12,53 @@ hashing algorithm by setting the <> [[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. |======================= diff --git a/server/src/main/java/org/elasticsearch/common/hash/MessageDigests.java b/server/src/main/java/org/elasticsearch/common/hash/MessageDigests.java index df8f3e2fa7f4..46eb462720c4 100644 --- a/server/src/main/java/org/elasticsearch/common/hash/MessageDigests.java +++ b/server/src/main/java/org/elasticsearch/common/hash/MessageDigests.java @@ -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 MD5_DIGEST = createThreadLocalMessageDigest("MD5"); private static final ThreadLocal SHA_1_DIGEST = createThreadLocalMessageDigest("SHA-1"); private static final ThreadLocal SHA_256_DIGEST = createThreadLocalMessageDigest("SHA-256"); + private static final ThreadLocal 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 instance = messageDigest.get(); instance.reset(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/Hasher.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/Hasher.java index 1b5b65e60c25..2d9b3e7c3e9e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/Hasher.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/Hasher.java @@ -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()); + } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/HasherTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/HasherTests.java index e51945cd9041..a0a931130ea6 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/HasherTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/HasherTests.java @@ -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)); } diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java index b43d206a5c1a..d0258d8494d7 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java +++ b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java @@ -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(