支持SHA-256/SHA-512、盐值哈希、HMAC、流式处理等高级特性
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* 企业级SHA哈希工具类
* 支持SHA-256/SHA-512、盐值哈希、HMAC、流式处理等高级特性
*
* @author Kent
* @version 2.0
*/
public final class KentSha {
// ==================== 常量定义 ====================
private static final String SHA_256 = "SHA-256";
private static final String SHA_512 = "SHA-512";
private static final String HMAC_SHA_256 = "HmacSHA256";
private static final String HMAC_SHA_512 = "HmacSHA512";
private static final int SALT_LENGTH = 16; // 盐值长度(字节)
private static final int STREAM_BUFFER_SIZE = 8192; // 流处理缓冲区大小
// 十六进制字符查表,避免重复创建
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
// 线程安全的MessageDigest缓存
private static final Map<String, MessageDigest> DIGEST_CACHE = new ConcurrentHashMap<>();
// 安全随机数生成器(单例)
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
// ==================== 核心哈希方法 ====================
/**
* 计算字符串的SHA-256哈希值
* @param input 输入字符串,不能为null
* @return SHA-256哈希值的十六进制字符串
* @throws IllegalArgumentException 如果输入为null
* @throws CryptoException 如果哈希计算失败
*/
public String sha256(final String input) {
return computeHash(input, SHA_256);
}
/**
* 计算字符串的SHA-512哈希值
* @param input 输入字符串,不能为null
* @return SHA-512哈希值的十六进制字符串
* @throws IllegalArgumentException 如果输入为null
* @throws CryptoException 如果哈希计算失败
*/
public String sha512(final String input) {
return computeHash(input, SHA_512);
}
/**
* 通用哈希计算方法(线程安全)
*/
private String computeHash(final String input, final String algorithm) {
validateInput(input);
if (input.isEmpty()) {
return getEmptyHash(algorithm);
}
try {
MessageDigest digest = getMessageDigest(algorithm);
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
} catch (Exception e) {
throw new CryptoException("哈希计算失败: " + algorithm, e);
}
}
// ==================== 盐值哈希方法 ====================
/**
* 使用固定盐值计算SHA-256哈希
* @param input 输入字符串
* @param salt 盐值
* @return 加盐后的哈希值
*/
public String sha256WithSalt(final String input, final String salt) {
validateInput(input);
validateInput(salt);
return sha256(input + salt);
}
/**
* 使用随机盐值计算SHA-256哈希
* @param input 输入字符串
* @return 包含哈希值和盐值的结果对象
*/
public SaltedHashResult sha256WithRandomSalt(final String input) {
validateInput(input);
String salt = generateRandomSalt();
String hash = sha256WithSalt(input, salt);
return new SaltedHashResult(hash, salt);
}
/**
* 验证加盐哈希
*/
public boolean verifySaltedHash(String input, String storedHash, String salt) {
if (input == null || storedHash == null || salt == null) {
return false;
}
String computedHash = sha256WithSalt(input, salt);
return constantTimeEquals(computedHash, storedHash);
}
// ==================== HMAC方法 ====================
/**
* 计算HMAC-SHA256
* @param input 输入数据
* @param secretKey 密钥
* @return HMAC哈希值
*/
public String hmac256(final String input, final String secretKey) {
validateInput(input);
validateInput(secretKey);
try {
Mac mac = Mac.getInstance(HMAC_SHA_256);
SecretKeySpec keySpec = new SecretKeySpec(
secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA_256);
mac.init(keySpec);
byte[] hash = mac.doFinal(input.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
} catch (Exception e) {
throw new CryptoException("HMAC计算失败", e);
}
}
/**
* 计算HMAC-SHA512
*/
public String hmac512(final String input, final String secretKey) {
validateInput(input);
validateInput(secretKey);
try {
Mac mac = Mac.getInstance(HMAC_SHA_512);
SecretKeySpec keySpec = new SecretKeySpec(
secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA_512);
mac.init(keySpec);
byte[] hash = mac.doFinal(input.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
} catch (Exception e) {
throw new CryptoException("HMAC计算失败", e);
}
}
// ==================== 流式处理方法 ====================
/**
* 计算输入流的SHA-256哈希(适用于大文件)
* @param inputStream 输入流
* @return 哈希值
* @throws IOException 如果读取流失败
*/
public String sha256Stream(final InputStream inputStream) throws IOException {
Objects.requireNonNull(inputStream, "输入流不能为null");
return computeStreamHash(inputStream, SHA_256);
}
/**
* 计算输入流的SHA-512哈希
*/
public String sha512Stream(final InputStream inputStream) throws IOException {
Objects.requireNonNull(inputStream, "输入流不能为null");
return computeStreamHash(inputStream, SHA_512);
}
private String computeStreamHash(final InputStream inputStream, final String algorithm)
throws IOException {
try {
MessageDigest digest = getMessageDigest(algorithm);
byte[] buffer = new byte[STREAM_BUFFER_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead);
}
return bytesToHex(digest.digest());
} catch (IOException e) {
throw e; // 重新抛出IO异常
} catch (Exception e) {
throw new CryptoException("流哈希计算失败: " + algorithm, e);
}
}
// ==================== 批量处理方法 ====================
/**
* 批量计算多个字符串的哈希值
* @param inputs 输入字符串数组
* @param algorithm 算法名称
* @return 哈希值数组
*/
public String[] batchHash(final String[] inputs, final String algorithm) {
if (inputs == null) {
return new String[0];
}
String[] results = new String[inputs.length];
for (int i = 0; i < inputs.length; i++) {
try {
results[i] = computeHash(inputs[i], algorithm);
} catch (CryptoException e) {
results[i] = null; // 单个失败不影响其他
}
}
return results;
}
// ==================== 工具方法 ====================
/**
* 验证两个哈希值是否相等(恒定时间比较,防止时序攻击)
*/
public boolean constantTimeEquals(String hash1, String hash2) {
if (hash1 == null || hash2 == null) {
return false;
}
if (hash1.length() != hash2.length()) {
return false;
}
int result = 0;
for (int i = 0; i < hash1.length(); i++) {
result |= hash1.charAt(i) ^ hash2.charAt(i);
}
return result == 0;
}
/**
* 生成随机盐值
*/
public String generateRandomSalt() {
byte[] saltBytes = new byte[SALT_LENGTH];
SECURE_RANDOM.nextBytes(saltBytes);
return bytesToHex(saltBytes);
}
/**
* 获取支持的算法列表
*/
public Set<String> getSupportedAlgorithms() {
Set<String> algorithms = new HashSet<>();
try {
for (String algorithm : Arrays.asList(SHA_256, SHA_512)) {
MessageDigest.getInstance(algorithm);
algorithms.add(algorithm);
}
} catch (NoSuchAlgorithmException e) {
// 忽略不支持的算法
}
return algorithms;
}
// ==================== 私有辅助方法 ====================
private void validateInput(final String input) {
Objects.requireNonNull(input, "输入参数不能为null");
}
private MessageDigest getMessageDigest(String algorithm) {
return DIGEST_CACHE.computeIfAbsent(algorithm, algo -> {
try {
return MessageDigest.getInstance(algo);
} catch (NoSuchAlgorithmException e) {
throw new CryptoException("不支持的算法: " + algo, e);
}
});
}
private String getEmptyHash(String algorithm) {
// 预计算的空字符串哈希值,避免重复计算
if (SHA_256.equals(algorithm)) {
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
} else if (SHA_512.equals(algorithm)) {
return "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
}
return computeHash("", algorithm);
}
private String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
hexString.append(HEX_ARRAY[b >>> 4 & 0xF]);
hexString.append(HEX_ARRAY[b & 0xF]);
}
return hexString.toString();
}
// ==================== 内部类 ====================
/**
* 加盐哈希结果封装类
*/
public static class SaltedHashResult {
private final String hash;
private final String salt;
public SaltedHashResult(String hash, String salt) {
this.hash = Objects.requireNonNull(hash, "哈希值不能为null");
this.salt = Objects.requireNonNull(salt, "盐值不能为null");
}
public String getHash() { return hash; }
public String getSalt() { return salt; }
@Override
public String toString() {
return String.format("SaltedHashResult{hash='%s', salt='%s'}",
hash.substring(0, Math.min(8, hash.length())) + "...",
salt.substring(0, Math.min(8, salt.length())) + "...");
}
}
/**
* 自定义加密异常
*/
public static class CryptoException extends RuntimeException {
public CryptoException(String message) {
super(message);
}
public CryptoException(String message, Throwable cause) {
super(message, cause);
}
}
}
支持 Ctrl+A 全选,Ctrl+C 复制