MD5哈希算法

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)存中可表示為下圖:

圖1-0 "abcde" MD5的過程在內(nèi)存中的表現(xiàn)形式

總結(jié)

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

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容