|
@@ -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());
|
|
|
+ }
|
|
|
}
|