MD5摘要算法介绍及其实现

版权声明:本人才疏学浅,欢迎大家批评指正,互相讨论学习。 https://blog.csdn.net/scuyxi/article/details/69055821

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++实现

参考:http://blog.csdn.net/hello_bravo_/article/details/53905860

#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;
}

猜你喜欢

转载自blog.csdn.net/scuyxi/article/details/69055821