MD5加密算法详解(c和c++环境下)

来源:互联网 发布:淘宝负责人电话号码 编辑:程序博客网 时间:2024/06/02 02:21

MD5加密算法简介

MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要,也就是产生一个3216进制的数。

以下讲解会综合字节和位两个来讲解,同时提醒一下,字典序是小端,即小地址存放低位,高地址存放高位,具体会在后续讲解中说明

数据填充

首先针对给出的数据,我们首先要进行数据填充,所谓的数据填充,就是让这个数据的字节对64个字节取模恰好等于56个字节。
如果我们设给定的数据字节数为K,那么我们需要将这个给定的数据字节数进行扩充,让其满足K%64==56。那么如何进行扩充是关键。

扩充规则:
在数据后面进行填充,填充第一个字节为0x80,其余为0x00。假设我们扩充了X个字节,那么最终达到的结果必须是(K+X)%64==56,接着我们还要处理8个字节,因为MD5真实处理的是64个字节的整数倍(这里,可能不懂,先放下,总之就是要将最后的8个字节补上,满足(K+X+8)%64==0)。最后8个字节保存了没扩充钱位数的多少,记住是位数的个数不是字节的个数,同时是按照小端规则.

数据处理
四个默认渐变变量(自己姑且这么说):

A = 0x67452301;B = 0xEFCDAB89;C = 0x98BADCFE;D = 0x10325476;

四个数据处理函数:

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));

四个过程处理函数(M数组和T数组稍后会讲解):

    FF(a,b,c,d,M[i],s,t[j])表示a=b+((a+(F(b,c,d)+M[i]+T[j])<<< s) ;    GG(a,b,c,d,M[i],s,t[j])表示a=b+((a+(G(b,c,d)+M[i]+T[j])<<< s) ;    HH(a,b,c,d,M[i],s,t[j])表示a=b+((a+(H(b,c,d)+M[i]+T[j])<<< s) ;    II(a,b,c,d,M[i],s,t[j])表示a=b+((a+(I(b,c,d)+M[i]+T[j])<<< s) ;

基本数据处理步骤

上一步的数据扩充已经将数据扩充为64的倍数了,然后我们就是先取第一个64字节,以默认的四个渐变变量为基础进行处理,产生另外四个渐变变量,这四个渐变变量会改变改变默认的渐变变量,然后取第二个64字节,以已经改变了的默认渐变变量为基础进行处理,如此反复,得到最终的128位数据。

数据处理具体步骤

首先我们取到了第一个64字节,我们还要将这64个字节进行分组,分为16组,每组4个字节,并且这四个字节是一个数,也就是说,我们要将这四个字节用一个数来代替,很明显的32位,用无符号的int(后续使用uint来代替unsigned int类型)来处理就可以了,接着从数据开头开始,第一组四个字节一个数,第二组四个字节一个数,最简单的例子如下:

如果数据是0xab,0xfb,0xdd,0xdd,0x80,0x00,0x00….,那么[0xab,0xfb,0xdd,0xdd]就是第一组,[0x80,0x00,0x00,0x00]就是第二组,如此反复…

然而,虽然是这么分组的,但是将他们组成一个数字却另外有规则,就是前面稍微有点提到的小端,也许一些接触过嵌入式的朋友会对此比较了解,就是一个数的高位部分存储在高地址,也就是说,我们将要进行MD5加密的数据从开头开始编号0123...接着分组后,第一组就是[0123],拿前面分组的例子来讲,那么最终形成的数据就是0xdd dd fb ab,有没有发现数据和之前分组的方向反过来了,也就是说编号越大的字节数据在构成一个数的时候就在一个数较高位的位置。

通过上述这一步就会形成16个数,这些数我在代码里用数据M存储起来了,也是过程处理函数中用到的M数组。

接着就是过程处理函数,数据处理函数是包含在过程处理函数里面的,我们不要深入了解数据处理函数和过程处理函数,因为这是MD5加密规则而已,并没有什么用,我们要知道的就是他的参数是什么。

首先就是T数组是通过T[i]=(uint)(1<<32)×abs(sin(i)){1i64}
在无符号处理的时候,对于32位无符号整型,由于无法直接左移32位得到想要的数,所以可以用0xffffffff+1LL来代替。

过程处理函数中的位移数s是已经确定好了的,对于FF函数操作时移位的数是[7,12,17,22]四个数进行循环,对GG函数操作时则是[5,9,14,20],接着是HH[4,11,16,23],然后II[6,10,15,21]

然后对于M数组的数据处理的过程函数操作如下:

第一轮

FF(a,b,c,d,M0,7,0xd76aa478) FF(d,a,b,c,M1,12,0xe8c7b756) FF(c,d,a,b,M2,17,0x242070db) FF(b,c,d,a,M3,22,0xc1bdceee) FF(a,b,c,d,M4,7,0xf57c0faf) FF(d,a,b,c,M5,12,0x4787c62a) FF(c,d,a,b,M6,17,0xa8304613) FF(b,c,d,a,M7,22,0xfd469501) FF(a,b,c,d,M8,7,0x698098d8) FF(d,a,b,c,M9,12,0x8b44f7af) FF(c,d,a,b,M10,17,0xffff5bb1) FF(b,c,d,a,M11,22,0x895cd7be) FF(a,b,c,d,M12,7,0x6b901122) FF(d,a,b,c,M13,12,0xfd987193) FF(c,d,a,b,M14,17,0xa679438e) FF(b,c,d,a,M15,22,0x49b40821) 

第二轮

GG(a,b,c,d,M1,5,0xf61e2562) GG(d,a,b,c,M6,9,0xc040b340) GG(c,d,a,b,M11,14,0x265e5a51) GG(b,c,d,a,M0,20,0xe9b6c7aa) GG(a,b,c,d,M5,5,0xd62f105d) GG(d,a,b,c,M10,9,0x02441453) GG(c,d,a,b,M15,14,0xd8a1e681) GG(b,c,d,a,M4,20,0xe7d3fbc8) GG(a,b,c,d,M9,5,0x21e1cde6) GG(d,a,b,c,M14,9,0xc33707d6) GG(c,d,a,b,M3,14,0xf4d50d87) GG(b,c,d,a,M8,20,0x455a14ed) GG(a,b,c,d,M13,5,0xa9e3e905) GG(d,a,b,c,M2,9,0xfcefa3f8) GG(c,d,a,b,M7,14,0x676f02d9) GG(b,c,d,a,M12,20,0x8d2a4c8a) 

第三轮

HH(a,b,c,d,M5,4,0xfffa3942) HH(d,a,b,c,M8,11,0x8771f681) HH(c,d,a,b,M11,16,0x6d9d6122) HH(b,c,d,a,M14,23,0xfde5380c) HH(a,b,c,d,M1,4,0xa4beea44) HH(d,a,b,c,M4,11,0x4bdecfa9) HH(c,d,a,b,M7,16,0xf6bb4b60) HH(b,c,d,a,M10,23,0xbebfbc70) HH(a,b,c,d,M13,4,0x289b7ec6) HH(d,a,b,c,M0,11,0xeaa127fa) HH(c,d,a,b,M3,16,0xd4ef3085) HH(b,c,d,a,M6,23,0x04881d05) HH(a,b,c,d,M9,4,0xd9d4d039) HH(d,a,b,c,M12,11,0xe6db99e5) HH(c,d,a,b,M15,16,0x1fa27cf8) HH(b,c,d,a,M2,23,0xc4ac5665) 

第四轮

II(a,b,c,d,M0,6,0xf4292244) II(d,a,b,c,M7,10,0x432aff97) II(c,d,a,b,M14,15,0xab9423a7) II(b,c,d,a,M5,21,0xfc93a039) II(a,b,c,d,M12,6,0x655b59c3) II(d,a,b,c,M3,10,0x8f0ccc92) II(c,d,a,b,M10,15,0xffeff47d) II(b,c,d,a,M1,21,0x85845dd1) II(a,b,c,d,M8,6,0x6fa87e4f) II(d,a,b,c,M15,10,0xfe2ce6e0) II(c,d,a,b,M6,15,0xa3014314) II(b,c,d,a,M13,21,0x4e0811a1) II(a,b,c,d,M4,6,0xf7537e82) II(d,a,b,c,M11,10,0xbd3af235) II(c,d,a,b,M2,15,0x2ad7d2bb) II(b,c,d,a,M9,21,0xeb86d391) 

所以我们需要构造两个数组:

uint MD5::rolarray[4][4] = {//用于位移数处理    { 7, 12, 17, 22 },    { 5, 9, 14, 20 },    { 4, 11, 16, 23 },    { 6, 10, 15, 21 }};uint MD5::mN[4][16] = {//用于M数组处理    { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },    { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },    { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },    { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }};

其中参数中的a,b,c,d就是最开始的四个渐变变量,他们在上述过程函数中分别处理的顺序是:

a,b,c,dd,a,b,cc,d,a,bb,c,d,a

就是将最后一个数移到第一个,我们可以用数组来处理比较方便,因为上述这么长的处理,太多了,可以通过循环来代替。

经过上述的过程函数处理后会得到新的a,b,c,d数据,然后我们用原来的A,B,C,D分别加上这a,b,c,d几个新数据。

接着就是进行第二组64字节处理…

当处理完后,我们会得到最终的A,B,C,D,然后进行小端处理,就是将A的数据按照分组M数组的处理一样,比如说A=0xabcdefdb,那么我们输出则是dbefcdab,依次输出A,B,C,D就是最后的MD5加密结果。

到此,原理讲解结束,接下来就是代码讲解了。

代码

#include <cmath>#include <cstdio>#include <vector>#include <string>#include <cstring>#include <iostream>using namespace std;typedef unsigned int uint;typedef long long LL;const int MAXN = 1e6 + 5;const int mod = 1e9 + 7;struct MD5 {    typedef void (MD5::*deal_fun)(uint&, uint, uint, uint, uint, uint, uint);//用于定义函数指针数组    string init_str;//数据字符串    uint init_arr[1000];//最终的数据数组{进行扩充处理后的数据}    const static int MAXN = 1e2;    static uint s_state[4];//最开始的默认静态渐变变量    uint state[4];//这个也是默认渐变变量,但是会改变    static uint rolarray[4][4];//位移数组    static uint mN[4][16];//对M数组的处理    uint curM;//当前处理的直接在整个数据中的位置    uint lenZ;//数据的总长{进行扩充处理后的数据总长,这个数是64的倍数}    uint offset;//需要从第几组开始处理    uint Tarr[64];//当前保存的T数组数据    uint Memory[64 + 5];//当前要处理的64个字节数据    uint M[16];//将64个字节数据分为16个数    MD5();    MD5(string str, int noffset);    //数据处理函数    inline uint F(uint X, uint Y, uint Z);    inline uint G(uint X, uint Y, uint Z);    inline uint H(uint X, uint Y, uint Z);    inline uint I(uint X, uint Y, uint Z);    //循环左移函数    uint ROL(uint s, uint ws);    //过程处理函数    inline void FF(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);    inline void GG(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);    inline void HH(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);    inline void II(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac);    //生成T数组单个数据的函数    inline uint T(uint i);    //将总数据中的64个字节移到Memory数组中    void data_Init();    //建立M数组    void create_M_arr();    //移动a,b,c,d,规则在前面介绍了    void l_data_change(uint *buf);    //产生T数组    void create_T_arr();    //得到最终MD5值    string get_MD5();    //过程处理    void processing();};uint MD5::rolarray[4][4] = {    { 7, 12, 17, 22 },    { 5, 9, 14, 20 },    { 4, 11, 16, 23 },    { 6, 10, 15, 21 }};uint MD5::mN[4][16] = {    { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },    { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },    { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },    { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }};/*传统渐变变量0x67452301,0xefcdab89,0x98badcfe,0x10325476这四个东西是可以根据要求更改的,如果取上述几个数则和经常用的MD5算出的结果是一样的对了,由于有些数据是静态的,改变之后不会进行需要重新进行复制*/uint MD5::s_state[4] = {    0x67452301,    0xefcdab89,    0x98badcfe,    0x10325476};MD5::MD5() {}MD5::MD5(string str, int noffset = 1) {    offset = noffset;    curM = (noffset - 1) * 64;    init_str = str;    lenZ = init_str.length();    memset(init_arr, 0, sizeof(init_arr));    for(int i = 0; i < lenZ; i ++) {        init_arr[i] = str[i];    }    /*        将数据扩充到取模64个字节等于56个字节        第一个填充0x80,然后就是0x00了    */    if(lenZ % 64 != 56) init_arr[lenZ ++] = 0x80;    while(lenZ % 64 != 56) {        init_arr[lenZ ++] = 0x00;    }    /*        最后8个字节保存了没扩充钱位数的多少,记住是位数的个数不是字节的个数,同时是按照小端规则    */    uint lengthbits = init_str.length() * 8;    init_arr[lenZ ++] = lengthbits & 0xff;    init_arr[lenZ ++] = lengthbits >> 8 & 0xff;    init_arr[lenZ ++] = lengthbits >> 16 & 0xff;    init_arr[lenZ ++] = lengthbits >> 24 & 0xff;    //因为uint最多32位所以我们只要考虑四个字节就可以了,虽然实际上要考虑64位,嘿    lenZ += 4;    for(int i = 0;i < 4;i ++){        state[i] = s_state[i];    }}inline uint MD5::F(uint X, uint Y, uint Z) {    return (X & Y) | ((~X) & Z);}inline uint MD5::G(uint X, uint Y, uint Z) {    return (X & Z) | (Y & (~Z));}inline uint MD5::H(uint X, uint Y, uint Z) {    return X ^ Y ^ Z;}inline uint MD5::I(uint X, uint Y, uint Z) {    return Y ^ (X | (~Z));}uint MD5::ROL(uint s, uint ws) {    return (s << ws) | (s >> (32 - ws));}inline void MD5::FF(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {    a = ROL(a + F(b, c, d) + x + ac, s) + b;    //printf("ff\n");}inline void MD5::GG(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {    a = ROL(a + G(b, c, d) + x + ac, s) + b;    //printf("gg\n");}inline void MD5::HH(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {    a = ROL(a + H(b, c, d) + x + ac, s) + b;    //printf("hh\n");}inline void MD5::II(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) {    a = ROL(a + I(b, c, d) + x + ac, s) + b;    //printf("ii\n");}//这里前面讲了inline uint MD5::T(uint i) {    return (uint)((0xffffffff + 1LL) * abs(sin(i)));}//取64个字节放在Memory数组中void MD5::data_Init() {    uint tmp = 0;    for(int i = 0; i < 64; i ++) {        Memory[i] = init_arr[curM + i];    }    curM += 64;//变化位置}void MD5::create_T_arr() {    for(int i = 1; i <= 64; i ++) {        Tarr[i - 1] = T(i);    }}/*这里使用了小端将数据存在M数组中,可以稍微思考一下*/void MD5::create_M_arr() {    uint tmp = 0;    int cnt = 0;    for(int i = 0; i < 64; i += 4) {        tmp = 0;        for(int j = 3; j >= 0; j --) {            tmp |= Memory[i + j];            if(j == 0) break;            tmp <<= 8;        }        M[cnt ++] = tmp;    }}//移动a,b,c,d,最后一个移到第一个void MD5::l_data_change(uint *buf) {    uint buftmp[4] = {buf[3], buf[0], buf[1], buf[2]};    for(int i = 0; i < 4; i ++) {        buf[i] = buftmp[i];    }}void MD5::processing() {    uint statetmp[4];    for(int i = 0; i < 4; i ++) {        statetmp[i] = state[i];    }    /*        这里的处理只是为了更方便的循环    */    uint * a = &statetmp[0];    uint * b = &statetmp[1];    uint * c = &statetmp[2];    uint * d = &statetmp[3];    /*        产生M数组和T数组    */    create_M_arr();    create_T_arr();    /*        建立函数指针数组        循环处理    */    deal_fun d_fun[4] = {        &MD5::FF, &MD5::GG, &MD5::HH, &MD5::II    };    for(int i = 0; i < 4; i ++) {        for(int j = 0; j < 16; j ++) {            (this ->* d_fun[i])(*a, *b, *c, *d, M[mN[i][j]], rolarray[i][j % 4], Tarr[i * 16 + j]);            l_data_change(statetmp);//交换a,b,c,d        }    }    for(int i = 0; i < 4; i ++) {        state[i] += statetmp[i];    }}string MD5::get_MD5() {    string result;    char tmp[15];    for(int i = 0;i < (lenZ - (offset - 1) * 64) / 64;i ++){        data_Init();        processing();    }    /*        最终显示也是用小端    */    for(int i = 0; i < 4; i ++) {        sprintf(tmp, "%02x", state[i] & 0xff);        result += tmp;        sprintf(tmp, "%02x", state[i] >> 8 & 0xff);        result += tmp;        sprintf(tmp, "%02x", state[i] >> 16 & 0xff);        result += tmp;        sprintf(tmp, "%02x", state[i] >> 24 & 0xff);        result += tmp;    }    return result;}int main() {    MD5 md1("aaaaaaaaaaaaaaaadminadminabcddddddddddddddddddddddddddddddddddddadmin",2);    cout << md1.get_MD5() << endl;    MD5 md2("2");    cout << md2.get_MD5() << endl;    MD5 md3("1");    cout << md3.get_MD5() << endl;    return 0;}

可能写的不太好,如果有什么问题,请提出,谢谢!

1 0