首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java MD5 加密實戰:16位/32位實現、鹽值防碰撞與效能優化

     編輯:Java 相關知識

MD5 基礎認知

  • 本質:訊息摘要算法,生成 128-bit (16 字節) 指紋
  • 不可逆:單向函數,無法從摘要還原原文
  • 碰撞風險:已被證明存在碰撞,**不適合安全敏感場景** (密碼存儲請用 BCrypt/Argon2)
  • 適用場景:文件完整性校驗、緩存 Key 生成、非敏感數據指紋

標準 32 位實現

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Utils {
    
    private static final char[] HEX_DIGITS = 
        {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
    
    /**
     * 計算 MD5,返回 32 位十六進制字符串
     */
    public static String md5(String input) {
        return md5(input.getBytes(StandardCharsets.UTF_8));
    }
    
    public static String md5(byte[] input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input);
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("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);
    }
    
    /**
     * 16 位 MD5:取 32 位中間 16 位 (第 8-23 字符)
     */
    public static String md5_16(String input) {
        String md5_32 = md5(input);
        return md5_32.substring(8, 24);
    }
}

進階:帶鹽值的 MD5 (防彩虹表)

public class SaltedMD5 {
    
    private static final SecureRandom RANDOM = new SecureRandom();
    private static final int SALT_LENGTH = 16; // 16 字節鹽值
    
    /**
     * 生成鹽值 + 雜湊組合字符串: salt:hash
     */
    public static String generate(String input) {
        byte[] salt = new byte[SALT_LENGTH];
        RANDOM.nextBytes(salt);
        
        String hash = md5(concat(salt, input.getBytes(StandardCharsets.UTF_8)));
        return bytesToHex(salt) + ":" + hash;
    }
    
    /**
     * 驗證輸入是否匹配存儲的鹽值雜湊
     */
    public static boolean verify(String input, String stored) {
        String[] parts = stored.split(":");
        if (parts.length != 2) return false;
        
        byte[] salt = hexToBytes(parts[0]);
        String hash = parts[1];
        
        String computed = md5(concat(salt, input.getBytes(StandardCharsets.UTF_8)));
        return MessageDigest.isEqual(hash.getBytes(), computed.getBytes()); // 恆定時間比較
    }
    
    private static byte[] concat(byte[] a, byte[] b) {
        byte[] result = new byte[a.length + b.length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }
    
    // 復用上文 bytesToHex、hexToBytes、md5 方法...
}

效能優化:大文件流式計算

public static String md5File(Path filePath) throws IOException {
    try (InputStream is = Files.newInputStream(filePath);
         DigestInputStream dis = new DigestInputStream(is, MessageDigest.getInstance("MD5"))) {
        
        byte[] buffer = new byte[8192];
        while (dis.read(buffer) != -1) {
            // 僅為推進流,摘要自動累計
        }
        return bytesToHex(dis.getMessageDigest().digest());
    } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException(e);
    }
}

常見坑點與最佳實踐

坑點後果正確做法
直接 `new String(bytes)` 轉字符串平台編碼導致結果不一致顯式指定 UTF-8 或用十六進制編碼
用於密碼存儲極易被彩虹表/GPU 破解**必須用 BCrypt/SCrypt/Argon2 + 鹽值**
忽略大小寫比較潛在邏輯錯誤統一輸出小寫,比較時用 equalsIgnoreCase
頻繁創建 MessageDigest線程不安全、效能差ThreadLocal 復用或每次獲取新實例

現代替代方案對比

總結

MD5 在非安全場景仍有價值,但**絕不可用於密碼存儲**。掌握標準實現、鹽值機制、流式計算、十六進制轉換優化,並了解現代替代方案,是 Java 開發者的基本功。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
算法長度速度安全性推薦用途
MD5128-bit極快已破解校驗和、緩存Key、非安全指紋
SHA-256256-bit快安全數字簽名、區塊鏈、證書
SHA-3可變中等更強長期安全需求
BCrypt184-bit可調慢極強**密碼存儲首選**
Argon2可變可調慢最強**密碼存儲新標準**