Files
Book-management-system/src/main/java/com/mzh/library/util/PasswordHasher.java
T
2026-04-27 18:40:30 +08:00

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');
}
}
}