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

Java MD5 加密實現方案匯總:核心代碼、工具類與最佳實踐

     編輯:Java 相關知識

Java 中 MD5 摘要算法完整實現指南

MD5(Message Digest Algorithm 5)雖已不適合密碼學安全場景,但在文件完整性校驗、非敏感數據指紋生成、緩存鍵計算等領域仍被廣泛使用。本文提供兩種核心實現方案、生產級工具類封裝及安全使用建議。

一、核心實現方案對比

方案一:標準流式處理(推薦)


import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;

public class Md5Utils {
    
    /**
     * 計算字符串 MD5(十六進制大寫)
     * @param input 原始字符串
     * @return 32 位十六進制字符串,失敗返回 null
     */
    public static String md5Hex(String input) {
        if (input == null) return null;
        try {
            // 1. 獲取 MessageDigest 實例
            MessageDigest md = MessageDigest.getInstance("MD5");
            
            // 2. 更新輸入數據(支持分塊更新大文件)
            md.update(input.getBytes(StandardCharsets.UTF_8));
            
            // 3. 完成摘要計算
            byte[] digest = md.digest();
            
            // 4. 字節數組轉十六進制字符串
            return bytesToHexUpper(digest);
        } catch (NoSuchAlgorithmException e) {
            // JDK 必備算法,理論不會發生
            throw new IllegalStateException("MD5 算法不可用", e);
        }
    }
    
    /**
     * 字節數組轉大寫十六進制(高性能位運算)
     */
    private static String bytesToHexUpper(byte[] bytes) {
        char[] hexDigits = "0123456789ABCDEF".toCharArray();
        char[] result = new char[bytes.length * 2];
        int index = 0;
        for (byte b : bytes) {
            result[index++] = hexDigits[(b >>> 4) & 0xF];  // 高 4 位
            result[index++] = hexDigits[b & 0xF];          // 低 4 位
        }
        return new String(result);
    }
}

方案二:BigInteger 簡潔版(教學用)


import java.security.MessageDigest;
import java.math.BigInteger;

public class Md5Simple {
    public static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            // 1 轉為正數,16 進制,不足 32 位左補 0
            return String.format("%032x", new BigInteger(1, digest)).toUpperCase();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}

性能對比:方案一約快 3-5 倍,避免了 BigInteger 創建與格式化開銷,適合高頻調用。

二、文件/流 MD5 計算(大文件分塊處理)


import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class FileMd5 {
    
    /**
     * 計算文件 MD5(流式處理,內存常數級)
     * @param filePath 文件路徑
     * @return MD5 十六進制字符串
     */
    public static String fileMd5(String filePath) throws IOException {
        try (InputStream is = new FileInputStream(filePath)) {
            return streamMd5(is);
        }
    }
    
    /**
     * 計算輸入流 MD5
     * @param inputStream 輸入流(調用者負責關閉或使用 try-with-resources)
     */
    public static String streamMd5(InputStream inputStream) throws IOException {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192]; // 8KB 緩衝區
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                md.update(buffer, 0, bytesRead);
            }
            return Md5Utils.bytesToHexUpper(md.digest());
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException(e);
        }
    }
}

三、生產級工具類封裝


import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.io.InputStream;
import java.io.IOException;

public final class DigestUtils {
    private DigestUtils() {} // 工具類防實例化
    
    private static final char[] HEX_UPPER = "0123456789ABCDEF".toCharArray();
    private static final char[] HEX_LOWER = "0123456789abcdef".toCharArray();
    
    // ===== 字符串 MD5 =====
    public static String md5Hex(String input) {
        return md5Hex(input, StandardCharsets.UTF_8, true);
    }
    
    public static String md5Hex(String input, Charset charset) {
        return md5Hex(input, charset, true);
    }
    
    public static String md5Hex(String input, boolean upperCase) {
        return md5Hex(input, StandardCharsets.UTF_8, upperCase);
    }
    
    public static String md5Hex(String input, Charset charset, boolean upperCase) {
        if (input == null) return null;
        return bytesToHex(md5Bytes(input.getBytes(charset)), upperCase);
    }
    
    // ===== 字節數組 MD5 =====
    public static byte[] md5Bytes(byte[] input) {
        if (input == null) return null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            return md.digest(input);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("MD5 not available", e);
        }
    }
    
    // ===== 文件/流 MD5 =====
    public static String md5File(String filePath) throws IOException {
        try (InputStream is = new java.io.FileInputStream(filePath)) {
            return md5Stream(is);
        }
    }
    
    public static String md5Stream(InputStream is) throws IOException {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192];
            int read;
            while ((read = is.read(buffer)) != -1) {
                md.update(buffer, 0, read);
            }
            return bytesToHex(md.digest(), true);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException(e);
        }
    }
    
    // ===== 核心轉換 =====
    private static String bytesToHex(byte[] bytes, boolean upperCase) {
        if (bytes == null) return null;
        char[] hex = upperCase ? HEX_UPPER : HEX_LOWER;
        char[] result = new char[bytes.length * 2];
        int idx = 0;
        for (byte b : bytes) {
            result[idx++] = hex[(b >>> 4) & 0xF];
            result[idx++] = hex[b & 0xF];
        }
        return new String(result);
    }
}

四、Spring Boot 整合示例


@Component
public class SignatureService {
    
    /**
     * 生成 API 簽名:參數排序 + 密鑰 + MD5
     */
    public String generateSign(Map params, String secretKey) {
        // 1. 參數按鍵名 ASCII 碼排序
        StringBuilder sb = new StringBuilder();
        params.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .forEach(e -> sb.append(e.getKey()).append('=').append(e.getValue()).append('&'));
        
        // 2. 拼接密鑰
        sb.append("key=").append(secretKey);
        
        // 3. MD5 大寫
        return DigestUtils.md5Hex(sb.toString(), true);
    }
    
    /**
     * 驗證簽名
     */
    public boolean verifySign(Map params, String secretKey, String sign) {
        String expected = generateSign(params, secretKey);
        return expected.equalsIgnoreCase(sign);
    }
}

五、安全使用警示與替代方案

場景MD5 適用性推薦替代
密碼存儲❌ 極不安全(彩虹表、GPU 破解)BCrypt、PBKDF2、Argon2、Scrypt
Token 簽名❌ 可碰撞偽造HMAC-SHA256、RSA/ECDSA 簽名、JWT
文件完整性校驗⚠️ 可接受(非惡意篡改場景)SHA-256、SHA-3、BLAKE3
緩存鍵生成✅ 適用(性能好、長度固定)同左
數據去重指紋✅ 適用同左

六、現代替代:Spring Security BCrypt


@Component
public class PasswordEncoder {
    private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12); // 強度 12
    
    public String encode(CharSequence rawPassword) {
        return encoder.encode(rawPassword);
    }
    
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encoder.matches(rawPassword, encodedPassword);
    }
}

總結:MD5 仍有其工程價值,但必須明確適用邊界。涉及認證授權、簽名防篡改等安全敏感場景,請堅決使用現代加密算法。工具類封裝應支持多字符集、大小寫、流式處理,滿足不同業務需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved