64 lines
2.3 KiB
Java
64 lines
2.3 KiB
Java
package com.mzh.library.util;
|
|
|
|
import java.security.GeneralSecurityException;
|
|
import java.security.MessageDigest;
|
|
import java.security.SecureRandom;
|
|
import java.util.Arrays;
|
|
import java.util.Base64;
|
|
|
|
import javax.crypto.SecretKeyFactory;
|
|
import javax.crypto.spec.PBEKeySpec;
|
|
|
|
public final class PasswordHasher {
|
|
private static final String ALGORITHM = "PBKDF2WithHmacSHA256";
|
|
private static final String PREFIX = "pbkdf2_sha256";
|
|
private static final int DEFAULT_ITERATIONS = 60000;
|
|
private static final int SALT_BYTES = 16;
|
|
private static final int HASH_BYTES = 32;
|
|
|
|
private PasswordHasher() {
|
|
}
|
|
|
|
public static String hash(String password) {
|
|
byte[] salt = new byte[SALT_BYTES];
|
|
new SecureRandom().nextBytes(salt);
|
|
byte[] hash = derive(password, salt, DEFAULT_ITERATIONS);
|
|
return PREFIX + "$" + DEFAULT_ITERATIONS + "$"
|
|
+ Base64.getEncoder().encodeToString(salt) + "$"
|
|
+ Base64.getEncoder().encodeToString(hash);
|
|
}
|
|
|
|
public static boolean verify(String password, String storedHash) {
|
|
if (password == null || storedHash == null || storedHash.trim().isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
String[] parts = storedHash.split("\\$");
|
|
if (parts.length != 4 || !PREFIX.equals(parts[0])) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
int iterations = Integer.parseInt(parts[1]);
|
|
byte[] salt = Base64.getDecoder().decode(parts[2]);
|
|
byte[] expected = Base64.getDecoder().decode(parts[3]);
|
|
byte[] actual = derive(password, salt, iterations);
|
|
return MessageDigest.isEqual(expected, actual);
|
|
} catch (IllegalArgumentException ex) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static byte[] derive(String password, byte[] salt, int iterations) {
|
|
char[] passwordChars = password.toCharArray();
|
|
try {
|
|
PBEKeySpec spec = new PBEKeySpec(passwordChars, salt, iterations, HASH_BYTES * 8);
|
|
return SecretKeyFactory.getInstance(ALGORITHM).generateSecret(spec).getEncoded();
|
|
} catch (GeneralSecurityException ex) {
|
|
throw new IllegalStateException("Unable to hash password", ex);
|
|
} finally {
|
|
Arrays.fill(passwordChars, '\0');
|
|
}
|
|
}
|
|
}
|