MD5(Message-Digest Algorithm 5)是一種哈希算法,哈希后的結(jié)果固定為128位的二進(jìn)制數(shù)。1996年后被證實存在弱點,可以被加以破解,2004年,證實無法防止碰撞,因此MD5無法適用于安全性認(rèn)證。但是在數(shù)據(jù)一致性檢驗還是有著廣泛的用途。
MD5算法步驟
補位
- 將需要處理的數(shù)據(jù)以
512bit為單位分組 - 在最后一組的數(shù)據(jù)后追加
1位值為1的終止標(biāo)志位;若追加終止位前,此組已滿512bit,則終止位追加在下一組 - 判斷追加終止位后,當(dāng)前組的剩余位數(shù),若剩余位不足64位,則需另起一組,并在組尾留出
64bit(留做他用),其他未填充空位補0 - 將原始數(shù)據(jù)的長度信息(以
bit為單位)放在留出的64bit位置,若數(shù)據(jù)長度信息超過64bit,則只取長度信息的低64bit
最終,數(shù)據(jù)被加工成 N*512 bit,每組數(shù)據(jù)在處理的時候,又會被分成 32bit
的 16 個小組,每一大組數(shù)據(jù)進(jìn)行一次處理,有 N 組,則進(jìn)行 N 次循環(huán)。
計算
MD5基本操作:
FF(a, b, c, d, Mj, s, ti)表示a = b + ((a + (F(b, c, d) + Mj + ti) <<< s)
GG(a, b, c, d, Mj, s, ti)表示a = b + ((a + (G(b, c, d) + Mj + ti) <<< s)
HH(a, b, c, d, Mj, s, ti)表示a = b + ((a + (H(b, c, d) + Mj + ti) <<< s)
II(a, b, c, d, Mj, s, ti)表示a = b + ((a + (I(b, c, d) + Mj + ti) <<< s)
其中:
F(X, Y, Z) = (X & Y) | ((~X) & Z)
G(X, Y, Z) = (X & Z) | (Y & (~Z))
H(X, Y, Z) = X ^ Y ^ Z
I(X, Y, Z) = Y ^ (X | (~Z))
-
<<<表示循環(huán)左移,注意區(qū)分<<操作符 -
s為常數(shù),取值參考后續(xù)完整過程 -
ti值為4294967296*abs(sin(i))的整數(shù)部分,i的單位是弧度;取值范圍1 ~ 16,故可視為常數(shù) -
Mj每個小組數(shù)據(jù)的值位32bit,需按小端方式讀取
再定義一組常數(shù),作為 a, b, c, d 的初始值。
A = 0×01234567
B = 0×89abcdef
C = 0xfedcba98
D = 0×76543210
a = A, b = B, c = C, d = D
每組內(nèi)的完整過程如下
第一輪
FF(a, b, c, d, M0, 7, 0xd76aa478)
FF(d, a, b, c, M1, 12, 0xe8c7b756)
FF(c, d, a, b, M2, 17, 0×242070db)
FF(b, c, d, a, M3, 22, 0xc1bdceee)
FF(a, b, c, d, M4, 7, 0xf57c0faf)
FF(d, a, b, c, M5, 12, 0×4787c62a)
FF(c, d, a, b, M6, 17, 0xa8304613)
FF(b, c, d, a, M7, 22, 0xfd469501)
FF(a, b, c, d, M8, 7, 0×698098d8)
FF(d, a, b, c, M9, 12, 0×8b44f7af)
FF(c, d, a, b, M10,17, 0xffff5bb1)
FF(b, c, d, a, M11,22, 0×895cd7be)
FF(a, b, c, d, M12, 7, 0×6b901122)
FF(d, a, b, c, M13,12, 0xfd987193)
FF(c, d, a, b, M14,17, 0xa679438e)
FF(b, c, d, a, M15,22, 0×49b40821)
第二輪
GG(a, b, c, d, M1, 5, 0xf61e2562)
GG(d, a, b, c, M6, 9, 0xc040b340)
GG(c, d, a, b, M11,14, 0×265e5a51)
GG(b, c, d, a, M0, 20, 0xe9b6c7aa)
GG(a, b, c, d, M5, 5, 0xd62f105d)
GG(d, a, b, c, M10, 9, 0×02441453)
GG(c, d, a, b, M15,14, 0xd8a1e681)
GG(b, c, d, a, M4, 20, 0xe7d3fbc8)
GG(a, b, c, d, M9, 5, 0×21e1cde6)
GG(d, a, b, c, M14, 9, 0xc33707d6)
GG(c, d, a, b, M3, 14, 0xf4d50d87)
GG(b, c, d, a, M8, 20, 0×455a14ed)
GG(a, b, c, d, M13, 5, 0xa9e3e905)
GG(d, a, b, c, M2, 9, 0xfcefa3f8)
GG(c, d, a, b, M7, 14, 0×676f02d9)
GG(b, c, d, a, M12,20, 0×8d2a4c8a)
第三輪
HH(a, b, c, d, M5, 4, 0xfffa3942)
HH(d, a, b, c, M8, 11, 0×8771f681)
HH(c, d, a, b, M11,16, 0×6d9d6122)
HH(b, c, d, a, M14,23, 0xfde5380c)
HH(a, b, c, d, M1, 4, 0xa4beea44)
HH(d, a, b, c, M4, 11, 0×4bdecfa9)
HH(c, d, a, b, M7, 16, 0xf6bb4b60)
HH(b, c, d, a, M10,23, 0xbebfbc70)
HH(a, b, c, d, M13, 4, 0×289b7ec6)
HH(d, a, b, c, M0, 11, 0xeaa127fa)
HH(c, d, a, b, M3, 16, 0xd4ef3085)
HH(b, c, d, a, M6, 23, 0×04881d05)
HH(a, b, c, d, M9, 4, 0xd9d4d039)
HH(d, a, b, c, M12,11, 0xe6db99e5)
HH(c, d, a, b, M15,16, 0×1fa27cf8)
HH(b, c, d, a, M2, 23, 0xc4ac5665)
第四輪
II(a, b, c, d, M0, 6, 0xf4292244)
II(d, a, b, c, M7, 10, 0×432aff97)
II(c, d, a, b, M14,15, 0xab9423a7)
II(b, c, d, a, M5, 21, 0xfc93a039)
II(a, b, c, d, M12, 6, 0×655b59c3)
II(d, a, b, c, M3, 10, 0×8f0ccc92)
II(c, d, a, b, M10,15, 0xffeff47d)
II(b, c, d, a, M1, 21, 0×85845dd1)
II(a, b, c, d, M8, 6, 0×6fa87e4f)
II(d, a, b, c, M15,10, 0xfe2ce6e0)
II(c, d, a, b, M6, 15, 0xa3014314)
II(b, c, d, a, M13,21, 0×4e0811a1)
II(a, b, c, d, M4, 6, 0xf7537e82)
II(d, a, b, c, M11,10, 0xbd3af235)
II(c, d, a, b, M2, 15, 0×2ad7d2bb)
II(b, c, d, a, M9, 21, 0xeb86d391)
最后分別于四個常數(shù)相加,作為下一輪輸入或作為最終結(jié)果輸出
a += A
b += B
c += C
d += D
輸出
在完成所有數(shù)據(jù)計算后,將 a、b、c、d 分別輸出。輸出的時候需要注意低位自己先輸出,高位字節(jié)后輸出。比如 a =0xd9b456ab,則輸出 ab56b4d9,因為 0xd9b456ab 在內(nèi)存中為小端存儲。
舉例及實現(xiàn)
下面以字符串 "abcde" 為例進(jìn)行講解,先看完整算法如下所示:
#include <stdio.h>
#include <string.h>
#define ROTL32(dword, n) ((dword) << (n) ^ ((dword) >> (32 - (n))))
#define F(x, y, z) ((((y) ^ (z)) & (x)) ^ (z))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
#define FF(a, b, c, d, x, s, ac) { \
(a) += F((b), (c), (d)) + (x) + (ac); \
(a) = ROTL32((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G((b), (c), (d)) + (x) + (ac); \
(a) = ROTL32((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H((b), (c), (d)) + (x) + (ac); \
(a) = ROTL32((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I((b), (c), (d)) + (x) + (ac); \
(a) = ROTL32((a), (s)); \
(a) += (b); \
}
int main(int argc, const char * argv[]) {
// "abcde"
uint32_t src[16] = {
0x64636261,
0x8065,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x00000028,
0,
};
uint32_t state[4] = {
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476
};
unsigned a, b, c, d;
a = state[0];
b = state[1];
c = state[2];
d = state[3];
const uint32_t *x = src;
FF(a, b, c, d, x[ 0], 7, 0xd76aa478);
FF(d, a, b, c, x[ 1], 12, 0xe8c7b756);
FF(c, d, a, b, x[ 2], 17, 0x242070db);
FF(b, c, d, a, x[ 3], 22, 0xc1bdceee);
FF(a, b, c, d, x[ 4], 7, 0xf57c0faf);
FF(d, a, b, c, x[ 5], 12, 0x4787c62a);
FF(c, d, a, b, x[ 6], 17, 0xa8304613);
FF(b, c, d, a, x[ 7], 22, 0xfd469501);
FF(a, b, c, d, x[ 8], 7, 0x698098d8);
FF(d, a, b, c, x[ 9], 12, 0x8b44f7af);
FF(c, d, a, b, x[10], 17, 0xffff5bb1);
FF(b, c, d, a, x[11], 22, 0x895cd7be);
FF(a, b, c, d, x[12], 7, 0x6b901122);
FF(d, a, b, c, x[13], 12, 0xfd987193);
FF(c, d, a, b, x[14], 17, 0xa679438e);
FF(b, c, d, a, x[15], 22, 0x49b40821);
GG(a, b, c, d, x[ 1], 5, 0xf61e2562);
GG(d, a, b, c, x[ 6], 9, 0xc040b340);
GG(c, d, a, b, x[11], 14, 0x265e5a51);
GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);
GG(a, b, c, d, x[ 5], 5, 0xd62f105d);
GG(d, a, b, c, x[10], 9, 0x2441453);
GG(c, d, a, b, x[15], 14, 0xd8a1e681);
GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);
GG(a, b, c, d, x[ 9], 5, 0x21e1cde6);
GG(d, a, b, c, x[14], 9, 0xc33707d6);
GG(c, d, a, b, x[ 3], 14, 0xf4d50d87);
GG(b, c, d, a, x[ 8], 20, 0x455a14ed);
GG(a, b, c, d, x[13], 5, 0xa9e3e905);
GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8);
GG(c, d, a, b, x[ 7], 14, 0x676f02d9);
GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
HH(a, b, c, d, x[ 5], 4, 0xfffa3942);
HH(d, a, b, c, x[ 8], 11, 0x8771f681);
HH(c, d, a, b, x[11], 16, 0x6d9d6122);
HH(b, c, d, a, x[14], 23, 0xfde5380c);
HH(a, b, c, d, x[ 1], 4, 0xa4beea44);
HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9);
HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60);
HH(b, c, d, a, x[10], 23, 0xbebfbc70);
HH(a, b, c, d, x[13], 4, 0x289b7ec6);
HH(d, a, b, c, x[ 0], 11, 0xeaa127fa);
HH(c, d, a, b, x[ 3], 16, 0xd4ef3085);
HH(b, c, d, a, x[ 6], 23, 0x4881d05);
HH(a, b, c, d, x[ 9], 4, 0xd9d4d039);
HH(d, a, b, c, x[12], 11, 0xe6db99e5);
HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
HH(b, c, d, a, x[ 2], 23, 0xc4ac5665);
II(a, b, c, d, x[ 0], 6, 0xf4292244);
II(d, a, b, c, x[ 7], 10, 0x432aff97);
II(c, d, a, b, x[14], 15, 0xab9423a7);
II(b, c, d, a, x[ 5], 21, 0xfc93a039);
II(a, b, c, d, x[12], 6, 0x655b59c3);
II(d, a, b, c, x[ 3], 10, 0x8f0ccc92);
II(c, d, a, b, x[10], 15, 0xffeff47d);
II(b, c, d, a, x[ 1], 21, 0x85845dd1);
II(a, b, c, d, x[ 8], 6, 0x6fa87e4f);
II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
II(c, d, a, b, x[ 6], 15, 0xa3014314);
II(b, c, d, a, x[13], 21, 0x4e0811a1);
II(a, b, c, d, x[ 4], 6, 0xf7537e82);
II(d, a, b, c, x[11], 10, 0xbd3af235);
II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);
II(b, c, d, a, x[ 9], 21, 0xeb86d391);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
unsigned char result1[32] ={0};
memcpy(result1, state, 32);
for (int i = 0; i < 16; i++) {
printf("%02x", result1[i]);
} // ab56b4d92b40713acc5af89985d4b786
printf("\n");
return 0;
}
對上述算法進(jìn)行講解,首先定義算法用到的宏定義 ROTL32 表示循環(huán)左移動 F,F(xiàn)F 為下面用到的運算。數(shù)組中的數(shù)據(jù)則為處理后的原始數(shù)據(jù) a b c d e 的編碼分別 0x61 0x62 0x63 0x64 0x65。故數(shù)組src[0] = 0x64636261,src[1] = 0x8065(65為符號e,80添加了結(jié)束位) ,0x28 位原始信息的長度。整個運算過程在內(nèi)存中可表示為下圖:

總結(jié)
此篇文章主要講解了 MD5 的算法過程,以及代碼實現(xiàn),但是并沒有給出通用版的實現(xiàn),通用版的實現(xiàn)可以在上例代碼中稍加修改實現(xiàn)?,F(xiàn)在計算機(jī)一般都是小端存儲方式,MD5 在計算的時候也使用了小端規(guī)則,如果是大端的存儲方式,上述代碼運行的結(jié)果并不正確,需要注意。此篇文章大部分都為我好友@gojan所寫,在此表示感謝。這是一系列文章的其中一篇,你可以在這兒Encode & Decode集序找到他其他的兄弟。