MD5 (Message-Digest Algorithm 5) 由 Ronald L. Rivest 於 1991 年設計,RFC 1321 定義。它將任意長度的位元組串列轉換為固定 128 位元(16 位元組)的雜湊值,常用於:
特性:單向不可逆、極低碰撞機率、免授權費用。但已知存在碰撞攻擊,不建議用於新系統的安全性敏感場合(請改用 SHA-256 或 SHA-3)。
0x80,再補 0x00 直到長度 ≡ 448 (mod 512) bits(即 56 bytes)。A=0x67452301, B=0xefcdab89, C=0x98badcfe, D=0x10325476(小端序)。T[i] = floor(2^32 × |sin(i)|)。// 位元旋轉常數
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));
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()(內建最佳化、硬體加速)。