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

MD5 演算法原理與 C# 完整實作教學

     編輯:關於C#

什麼是 MD5?

MD5 (Message-Digest Algorithm 5) 由 Ronald L. Rivest 於 1991 年設計,RFC 1321 定義。它將任意長度的位元組串列轉換為固定 128 位元(16 位元組)的雜湊值,常用於:

  • 檔案完整性驗證(下載檔案核對)
  • 密碼雜湊儲存(需配合 Salt)
  • 數位簽名基礎
  • 快速資料比對(如資料庫行雜湊)

特性:單向不可逆、極低碰撞機率、免授權費用。但已知存在碰撞攻擊,不建議用於新系統的安全性敏感場合(請改用 SHA-256 或 SHA-3)。

演算法流程圖解

  1. 填補:在訊息後附加 0x80,再補 0x00 直到長度 ≡ 448 (mod 512) bits(即 56 bytes)。
  2. 附加長度:以 64 位元小端序整數記錄原始訊息長度(bits),補至 512 bits 對齊。
  3. 初始化緩衝區:四個 32 位元暫存器 A=0x67452301, B=0xefcdab89, C=0x98badcfe, D=0x10325476(小端序)。
  4. 四輪非線性函數運算:每輪 16 步,共 64 步,使用常數表 T[i] = floor(2^32 × |sin(i)|)。
  5. 輸出:A‖B‖C‖D 連接成 128 位元雜湊。

核心常數與函數

// 位元旋轉常數
private readonly int[] S = {
    7,12,17,22,  7,12,17,22,  7,12,17,22,  7,12,17,22, // Round 1
    5, 9,14,20,  5, 9,14,20,  5, 9,14,20,  5, 9,14,20, // Round 2
    4,11,16,23,  4,11,16,23,  4,11,16,23,  4,11,16,23, // Round 3
    6,10,15,21,  6,10,15,21,  6,10,15,21,  6,10,15,21  // Round 4
};

// 非線性函數
private static uint F(uint x, uint y, uint z) => (x & y) | (~x & z);
private static uint G(uint x, uint y, uint z) => (x & z) | (y & ~z);
private static uint H(uint x, uint y, uint z) => x ^ y ^ z;
private static uint I(uint x, uint y, uint z) => y ^ (x | ~z);

// 左循環位移
private static uint RotateLeft(uint x, int n) => (x << n) | (x >> (32 - n));

C# 完整實作(教學用,非生產環境建議)

public static class Md5Helper
{
    // T[i] = floor(2^32 * |sin(i)|), i=1..64
    private static readonly uint[] T = new uint[64];
    static Md5Helper()
    {
        for (int i = 0; i < 64; i++)
            T[i] = (uint)(uint.MaxValue * Math.Abs(Math.Sin(i + 1)));
    }

    public static byte[] ComputeHash(byte[] input)
    {
        // 1. 填補與附加長度
        var padded = PadMessage(input); // 回傳 byte[],長度為 512 bits 的倍數

        // 2. 初始化
        uint A = 0x67452301, B = 0xefcdab89, C = 0x98badcfe, D = 0x10325476;

        // 3. 處理每個 512-bit 區塊 (16 x 32-bit words)
        for (int offset = 0; offset < padded.Length; offset += 64)
        {
            uint[] X = new uint[16];
            for (int i = 0; i < 16; i++)
                X[i] = BitConverter.ToUInt32(padded, offset + i * 4);

            uint AA = A, BB = B, CC = C, DD = D;

            // Round 1: F
            for (int i = 0; i < 16; i += 4)
            {
                A = RoundOp(A, B, C, D, X[i + 0], S[i + 0], T[i + 0], F); // 步驟 1,5,9,13
                D = RoundOp(D, A, B, C, X[i + 1], S[i + 1], T[i + 1], F);
                C = RoundOp(C, D, A, B, X[i + 2], S[i + 2], T[i + 2], F);
                B = RoundOp(B, C, D, A, X[i + 3], S[i + 3], T[i + 3], F);
            }
            // Round 2: G (存取順序不同)
            for (int i = 0; i < 16; i += 4)
            {
                A = RoundOp(A, B, C, D, X[(1 + 5 * i) % 16], S[16 + i], T[16 + i], G);
                D = RoundOp(D, A, B, C, X[(1 + 5 * (i+1)) % 16], S[17 + i], T[17 + i], G);
                C = RoundOp(C, D, A, B, X[(1 + 5 * (i+2)) % 16], S[18 + i], T[18 + i], G);
                B = RoundOp(B, C, D, A, X[(1 + 5 * (i+3)) % 16], S[19 + i], T[19 + i], G);
            }
            // Round 3: H
            for (int i = 0; i < 16; i += 4)
            {
                A = RoundOp(A, B, C, D, X[(5 + 3 * i) % 16], S[32 + i], T[32 + i], H);
                D = RoundOp(D, A, B, C, X[(5 + 3 * (i+1)) % 16], S[33 + i], T[33 + i], H);
                C = RoundOp(C, D, A, B, X[(5 + 3 * (i+2)) % 16], S[34 + i], T[34 + i], H);
                B = RoundOp(B, C, D, A, X[(5 + 3 * (i+3)) % 16], S[35 + i], T[35 + i], H);
            }
            // Round 4: I
            for (int i = 0; i < 16; i += 4)
            {
                A = RoundOp(A, B, C, D, X[(7 * i) % 16], S[48 + i], T[48 + i], I);
                D = RoundOp(D, A, B, C, X[(7 * (i+1)) % 16], S[49 + i], T[49 + i], I);
                C = RoundOp(C, D, A, B, X[(7 * (i+2)) % 16], S[50 + i], T[50 + i], I);
                B = RoundOp(B, C, D, A, X[(7 * (i+3)) % 16], S[51 + i], T[51 + i], I);
            }

            A += AA; B += BB; C += CC; D += DD;
        }

        // 4. 輸出小端序位元組陣列
        var result = new byte[16];
        Buffer.BlockCopy(BitConverter.GetBytes(A), 0, result, 0, 4);
        Buffer.BlockCopy(BitConverter.GetBytes(B), 0, result, 4, 4);
        Buffer.BlockCopy(BitConverter.GetBytes(C), 0, result, 8, 4);
        Buffer.BlockCopy(BitConverter.GetBytes(D), 0, result, 12, 4);
        return result;
    }

    private static uint RoundOp(uint a, uint b, uint c, uint d, uint x, int s, uint t, Func f)
    {
        a += f(b, c, d) + x + t;
        a = RotateLeft(a, s);
        a += b;
        return a;
    }

    private static byte[] PadMessage(byte[] input)
    {
        long bitLen = (long)input.Length * 8;
        int padLen = (input.Length % 64 < 56) ? (56 - input.Length % 64) : (120 - input.Length % 64);
        var padded = new byte[input.Length + padLen + 8];
        Buffer.BlockCopy(input, 0, padded, 0, input.Length);
        padded[input.Length] = 0x80;
        // 長度以 little-endian 64-bit 寫入最後 8 bytes
        var lenBytes = BitConverter.GetBytes(bitLen);
        Buffer.BlockCopy(lenBytes, 0, padded, padded.Length - 8, 8);
        return padded;
    }

    public static string ToHexString(byte[] hash, bool upper = false)
        => BitConverter.ToString(hash).Replace("-", "").ToLower(); // 簡易實作
}

標準測試向量驗證

輸入MD5 雜湊 (小寫)
""d41d8cd98f00b204e9800998ecf8427e
"a"0cc175b9c0f1b6a831c399e269772661
"abc"900150983cd24fb0d6963f7d28e17f72
"message digest"f96b697d7cb7938d525a2f31aaf161d0
// 驗證用法
var hash = Md5Helper.ComputeHash(Encoding.UTF8.GetBytes("abc"));
Console.WriteLine(Md5Helper.ToHexString(hash)); // 輸出 900150983cd24fb0d6963f7d28e17f72

生產環境建議

  • 直接使用 System.Security.Cryptography.MD5.Create()(內建最佳化、硬體加速)。
  • 密碼儲存請改用 PBKDF2 / Argon2 / bcrypt + 隨機 Salt。
  • 完整性驗證優先選用 SHA-256 或 HMAC-SHA256。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved