MD5 (Message Digest Algorithm 5) 將任意長度數據生成 128 位(16 字節)指紋,特性:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
public static String md5(String input) {
try {
// 1. 獲取算法實例
MessageDigest md = MessageDigest.getInstance("MD5");
// 2. 輸入數據(指定編碼避免平台差異)
md.update(input.getBytes(StandardCharsets.UTF_8));
// 3. 計算摘要
byte[] digest = md.digest();
// 4. 轉十六進制字符串
return bytesToHex(digest);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("MD5 not available", e);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
public static String md5File(File file) throws IOException {
MessageDigest md = MessageDigest.getInstance("MD5");
try (InputStream is = Files.newInputStream(file.toPath())) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
md.update(buffer, 0, len);
}
}
return bytesToHex(md.digest());
}
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(part1);
md.update(part2);
// 可序列化狀態
byte[] state = md.digest(); // 此處為最終結果
// 若需中間狀態:無標準 API,需自定義實現
public static String md5WithSalt(String password, String salt) {
return md5(password + "{" + salt + "}"); // 簡單拼接
// 生產環境推薦 PBKDF2/Bcrypt/Argon2
}
| 錯誤寫法 | 問題 | 正確寫法 |
|---|---|---|
| md.update(input.getBytes()) | 依賴平台默認編碼 | md.update(input.getBytes(StandardCharsets.UTF_8)) |
| new String(digest) | 字節非合法字符串 | bytesToHex / Base64.getEncoder().encodeToString |
| String.format("%x", b) | 負數輸出如 ffffff8f | String.format("%02x", b & 0xff) |
| 算法 | 輸出長度 | 速度 | 安全性 | 用途 |
|---|---|---|---|---|
| MD5 | 128bit | 極快 | 已破解 | 文件校驗、非安全去重 |
| SHA-1 | 160bit | 快 | 已破解 | Git 提交ID、舊系統兼容 |
| SHA-256 | 256bit | 中等 | 安全 | 區塊鏈、證書、推薦替代 |
| SHA-512 | 512bit | 較慢 | 更安全 | 高安全需求 |
| Bcrypt | 184bit | 可調慢 | 極安全 | 密碼存儲(首選) |
| Argon2 | 可變 | 可調 | 最新標準 | 密碼存儲(推薦) |
// Spring Security BCrypt
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12); // strength 12
String hashed = encoder.encode(rawPassword);
boolean matches = encoder.matches(rawPassword, hashed);
// 或手動使用 jBCrypt
String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));
MessageDigest 非線程安全,用 ThreadLocal 或每次新建
public final class DigestUtils {
private static final ThreadLocal MD5_HOLDER = ThreadLocal.withInitial(() -> {
try { return MessageDigest.getInstance("MD5"); }
catch (NoSuchAlgorithmException e) { throw new IllegalStateException(e); }
});
private static final char[] HEX = "0123456789abcdef".toCharArray();
public static String md5(String input) {
return md5(input.getBytes(StandardCharsets.UTF_8));
}
public static String md5(byte[] input) {
MessageDigest md = MD5_HOLDER.get();
md.reset();
md.update(input);
return bytesToHex(md.digest());
}
public static String md5(File file) throws IOException {
MessageDigest md = MD5_HOLDER.get();
md.reset();
try (InputStream is = Files.newInputStream(file.toPath())) {
byte[] buf = new byte[8192];
int len;
while ((len = is.read(buf)) != -1) md.update(buf, 0, len);
}
return bytesToHex(md.digest());
}
private static String bytesToHex(byte[] bytes) {
char[] chars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
int b = bytes[i] & 0xff;
chars[i * 2] = HEX[b >>> 4];
chars[i * 2 + 1] = HEX[b & 0xf];
}
return new String(chars);
}
}
MD5 雖然不再安全,但在文件完整性校驗、緩存鍵生成、非敏感數據指紋等場景仍廣泛使用。掌握正確 API 用法、編碼處理、十六進制轉換是 Java 開發基本功。