MD5 算法本質
- 全稱:Message-Digest Algorithm 5
- 輸出:128 位 (16 字節) 雜湊值,通常表示為 32 位十六進制字符串
- 特性:單向、定長、雪崩效應 (輸入微小變化導致輸出巨大差異)、抗弱碰撞 (難找兩輸入同輸出)
- 現狀:已被破解 (強碰撞攻擊),嚴禁用於數字簽名、SSL 證書、密碼存儲等安全敏感場景
標準實現代碼 (線程安全、無狀態)
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class Md5Util {
private static final char[] HEX_DIGITS =
{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
public static String md5Hex(String input) {
return md5Hex(input.getBytes(StandardCharsets.UTF_8));
}
public static String md5Hex(byte[] input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input);
return bytesToHex(digest);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("JVM 缺少 MD5 實現", e);
}
}
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_DIGITS[b >>> 4];
chars[i * 2 + 1] = HEX_DIGITS[b & 0x0F];
}
return new String(chars);
}
// 測試
public static void main(String[] args) {
System.out.println(md5Hex("caidao")); // 5b8e...
System.out.println(md5Hex("https://www.jb51.net/"));
}
}
核心 API 解析
| 方法 | 說明 |
MessageDigest.getInstance("MD5") | 獲取實現實例,JDK 內建提供者 |
md.update(bytes) | 流式輸入數據,可多次調用 (大文件分塊) |
md.digest() | 完成雜湊計算並返回字節數組,調用後實例重置 |
md.digest(bytes) | 一次性計算並返回 |
大文件流式計算 (避免 OOM)
public static String md5File(Path file) throws IOException {
try (InputStream is = Files.newInputStream(file)) {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
md.update(buffer, 0, len);
}
return bytesToHex(md.digest());
}
}
典型應用場景 (非安全)
- 文件完整性校驗:下載後比對官方發布的 MD5,防範傳輸損壞/篡改 (非惡意)
- 緩存 Key 生成:將長字符串/對象序列化為 32 位 Key,如 Redis Key、本地緩存文件名
- 去重指紋:海量文本/圖片快速去重,接受極低誤判率
- 非敏感標識生成:如生成唯一請求 ID、臨時文件名
安全替代方案對比
| 算法 | 輸出長度 | 安全性 | Java 實現 | 推薦用途 |
| SHA-256 | 256 位 (64 hex) | 強 | MessageDigest.getInstance("SHA-256") | 數字簽名、證書、區塊鏈、密碼雜湊 (配合鹽) |
| SHA-3 / Keccak | 224~512 位 | 最強 | JDK 9+ "SHA3-256" | 長期機密、後量子預備 |
| Bcrypt | 184 位 | 專為密碼設計 | 第三方 jbcrypt / Spring Security | 密碼存儲首選 (自帶鹽、可調成本) |
| PBKDF2 | 可配置 | 強 | SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256") | 密碼派生密鑰、加密存儲 |
| Argon2 | 可配置 | 最強 (記憶體難度) | 第三方 argon2-jvm | 現代密碼雜湊黃金標準 |
密碼存儲正確姿勢 (Spring Security 示例)
// 依賴: spring-security-crypto
PasswordEncoder encoder = new BCryptPasswordEncoder(12); // 強度 12
// 註冊/修改密碼
String encoded = encoder.encode(rawPassword); // 自帶隨機鹽
userRepository.save(new User(username, encoded));
// 登錄驗證
boolean matches = encoder.matches(rawPasswordInput, storedEncoded);
總結
MD5 作為「數字指紋」在非安全完整性校驗領域仍有價值,但絕不能用於任何認證、授權、機密性保護。現代應用請直接使用 Bcrypt/Argon2/PBKDF2 存儲密碼,SHA-256/384/512 做數字簽名與完整性保護。Java MessageDigest API 線程安全、易用,配合十六進制編碼工具即可滿足所有雜湊需求。