1. 程式人生 > >MD5摘要演算法介紹及其實現

MD5摘要演算法介紹及其實現

Message Digest Algorithm MD5(訊息摘要演算法第五版)為電腦保安領域廣泛使用的一種雜湊函式,用以提供訊息的完整性保護。

演算法特點

MD5演算法,符合一般摘要演算法的特點:
1、壓縮性:任意長度的資料,算出的MD5值長度都是固定的。
2、容易計算:從原資料計算出MD5值很容易。
3、抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。
4、強抗碰撞:已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。

演算法流程

1.填充

MD5演算法填充必須進行,即使其位長對512求餘的結果等於448。
資訊的位長(Bits Length)將被擴充套件至N*512+448,N為一個非負整數,N可以是零。

填充的方法如下:
1) 在資訊的後面填充一個1和無數個0,直到滿足上面的條件時才停止用0對資訊的填充。
2) 在這個結果後面附加一個以64位(長度大小)二進位制表示的填充前資訊長度(單位為Bit),如果二進位制表示的填充前資訊長度超過64位,則取低64位。
經過這兩步的處理,資訊的位長=N*512+448+64=(N+1)*512,即長度恰好是512(區塊大小)的整數倍。這樣做的原因是為滿足後面處理中對資訊長度的要求。

2.初始化變數

初始的128位值為初始連結變數,這些引數用於第一輪的運算,以大端位元組序來表示,他們分別為: A=0x01234567,B=0x89ABCDEF,C=0xFEDCBA98,D=0x76543210。
(每一個變數給出的數值是高位元組存於記憶體低地址,低位元組存於記憶體高地址,即大端位元組序。在程式中變數A、B、C、D的值分別為0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476)

3. 處理分組資料

每一分組的演算法流程如下:
第一分組需要將上面四個連結變數複製到另外四個變數中:A到a,B到b,C到c,D到d。從第二分組開始的變數為上一分組的運算結果,即A = a, B = b, C = c, D = d。
將每一個分組(64位元組,128位——內部大小)分成16組(每組4位元組,32位——字元尺寸

主迴圈有四輪(MD4只有三輪),每輪迴圈都很相似。第一輪進行16次操作。每次操作對a、b、c和d中的其中三個作一次非線性函式運算,然後將所得結果加上第四個變數,文字的一個子分組和一個常數。再將所得結果向左環移一個不定的數,並加上a、b、c或d中之一。最後用該結果取代a、b、c或d中之一。

所有這些完成之後,將a、b、c、d分別在原來基礎上再加上A、B、C、D。
即a = a + A,b = b + B,c = c + C,d = d + D
然後用下一分組資料繼續執行以上演算法。

輸出

最後的輸出是a、b、c和d的級聯(128位——輸出大小)。

演算法的C++實現

#include<iostream>
#include<string>
#include<cmath>
using namespace std;
//填充操作,使得資訊位長恰好是512的倍數
#define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移的時候,高位一定要補零,而不是補充符號位
//MD5有四輪運算,將原先資料分為4個32位分組組成,選其中三個進行運算
#define F(x, y, z) (((x) & (y)) | ((~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 A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476//初始化變數,用於第一輪   
//strBaye的長度
unsigned int strlength;
//A,B,C,D的臨時變數
unsigned int atemp;
unsigned int btemp;
unsigned int ctemp;
unsigned int dtemp;
//常量ti unsigned int(abs(sin(i+1))*(2pow32))
const unsigned int k[] = {
    0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
    0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
    0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
    0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
    0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
    0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
    0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
    0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
    0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
    0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
    0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
    0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
    0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391 };
//向左位移數
const unsigned int s[] = { 7,12,17,22,7,12,17,22,7,12,17,22,7,
12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
15,21,6,10,15,21,6,10,15,21,6,10,15,21 };

const char str16[] = "0123456789abcdef";

void mainLoop(unsigned int M[])
{
    unsigned int f, g;
    unsigned int a = atemp;
    unsigned int b = btemp;
    unsigned int c = ctemp;
    unsigned int d = dtemp;
    for (unsigned int i = 0; i < 64; i++)
    {
        if (i < 16) {
            f = F(b, c, d);
            g = i;
        }
        else if (i < 32)
        {
            f = G(b, c, d);
            g = (5 * i + 1) % 16;
        }
        else if (i < 48) {
            f = H(b, c, d);
            g = (3 * i + 5) % 16;
        }
        else {
            f = I(b, c, d);
            g = (7 * i) % 16;
        }
        unsigned int tmp = d;
        d = c;
        c = b;
        b = b + shift((a + f + k[i] + M[g]), s[i]);
        a = tmp;
    }
    atemp = a + atemp;
    btemp = b + btemp;
    ctemp = c + ctemp;
    dtemp = d + dtemp;
}
/*
*填充函式
*處理後應滿足bits≡448(mod512),位元組就是bytes≡56(mode64)
*填充方式為先加一個1,其它位補零
*最後加上64位的原來長度
*/
unsigned int* add(string str)
{
    unsigned int num = ((str.length() + 8) / 64) + 1;//以512位,64個位元組為一組
    unsigned int *strByte = new unsigned int[num * 16];    //64/4=16,所以有16個整數
    strlength = num * 16;
    for (unsigned int i = 0; i < num * 16; i++)
        strByte[i] = 0;
    for (unsigned int i = 0; i < str.length(); i++)
    {
        strByte[i >> 2] |= (str[i]) << ((i % 4) * 8);//一個整數儲存四個位元組,i>>2表示i/4 一個unsigned int對應4個位元組,儲存4個字元資訊
    }
    strByte[str.length() >> 2] |= 0x80 << (((str.length() % 4)) * 8);//尾部新增1 一個unsigned int儲存4個字元資訊,所以用128左移
                                                                     /*
                                                                     *新增原長度,長度指位的長度,所以要乘8,然後是小端序,所以放在倒數第二個,這裡長度只用了32位
                                                                     */
    strByte[num * 16 - 2] = str.length() * 8;
    return strByte;
}

string changeHex(int a)
{
    int b;
    string str1;
    string str = "";
    for (int i = 0; i < 4; i++)
    {
        str1 = "";
        b = ((a >> i * 8) % (1 << 8)) & 0xff;   //逆序處理每個位元組
        for (int j = 0; j < 2; j++)
        {
            str1.insert(0, 1, str16[b % 16]);
            b = b / 16;
        }
        str += str1;
    }
    return str;
}

string getMD5(string source)
{
    atemp = A;    //初始化
    btemp = B;
    ctemp = C;
    dtemp = D;
    unsigned int *strByte = add(source);
    for (unsigned int i = 0; i < strlength / 16; i++)
    {
        unsigned int num[16];
        for (unsigned int j = 0; j < 16; j++)
            num[j] = strByte[i * 16 + j];
        mainLoop(num);
    }
    return changeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp));
}
unsigned int main()
{
    string s = getMD5(""); //d41d8cd98f00b204e9800998ecf8427e
    string s1 = getMD5("message digest"); //f96b697d7cb7938d525a2f31aaf161d0
    cout << s << endl;
    cout << s1 << endl;

    cin.get();
    return 0;
}