MD5算法讲解 – 劫幻
MD5分析与代码实现
一、 MD5密码算法的特点
(1) 输出总为16字节
(2) 不可逆性
(3) 高度离散性
(4) 抗碰撞性
二、 常用实例
(1) 密码保护
(2) 文件完整性校验【用于抗碰撞性高,可用于下载文件时,查看文件的md5于下载后,在检验一次md5值,如果相同,则文件没有被修改,否则文件被修改了。从而实现文件完整性校验】
(3) 数字签名
(4) 云盘秒传【有时云盘上传时,在传输大文件的情况下,并不是真正实现秒传。其实只需计算一次文件的Md5值,然后再数据库中进行寻找,如果有相同的Md5,即直接引用即可】
三、 MD5算法的过程实现如下
(1) 数据补充
MD5密码算法,需要明文的长度为N*512+448bit,即数据的长度除以512位为448bit。但总长度要是512bit。其实很好奇,最后64bit去哪了。其实最后64bit需要单独补充,作用是用来计算原始的数据长度。其次,补充的位数为1-521之间。补充的第一位为1,其次补充0.直到满足mod512的结果为448。图示如下。
(2)标准幻数
标准幻数总共为4*4=16字节,看到这个16字节,是不是感觉到很熟悉。对的,它就跟MD5输出16字节息息相关。
A = 01234567 B = 89ABCDEF
C = FEDCBA98 D = 76543210
这就是开始的标准幻数,这些就是作为循环计算的初始值用的。但需要注意的是,在程序中用的是小端字节序,也就是说高字节在前,低字节在后。即为如下所示
A = 0x67452301 B = 0xEFCDB89
C = 0x98BADCFE D = 0x10325476
(3) 加密步骤
1.原始数据分为n个512位,即n个64字节
2.然后对每个512bit,即64字节分为16个4字节的数据。
4字节x[0] |
4字节x[1] |
4字节x[2] |
4字节x[3] |
4字节x[4] |
4字节x[5] |
4字节x[6] |
4字节x[7] |
4字节x[8] |
4字节x[9] |
4字节x[10] |
4字节x[11] |
4字节x[12] |
4字节x[13] |
4字节x[14] |
4字节x[15] |
3.准备4个函数
FF(A,B,C,D, X, S,AC);GG(A,B,C,D, X, S,AC);
HH(A,B,C,D, X, S,AC);II(A,B,C,D, X, S,AC);
4个标准幻数 分组的每4字节 固定常数
通过4个函数的逻辑运算于移位运算,改变第一个标准幻数的值,对于每4个字节,4个函数分别执行16次。
FF (A,B,C,D,x[0],S11,AC)
FF (D,A,B,C,x[1],S12,AC)
……..
GG (A,B,C,D,x[0],S21,AC)
……
HH (A,B,C,D,x[0],S31,AC)
……
II (A,B,C,D,x[0],S41,AC)
……
GG (A,B,C,D,x[0],S44,AC)
这就是一轮计算,一轮计算后,改变了4个标准幻数的值。形成新的A,B,C,D。然后新的A,B,C,D于原来的A,B,C,D相加,从而形成新的标准幻数。
整个过程重复进行,知道所有的64字节处理完,得到最后的标准幻数。得到的最后的标准幻数即为MD5值。
例:
A = 0Xbfcbfgce B = 0Xc4105a80
C = 0X02599277 D = 0X5c099ee2
MD5 = cefgcbbf805a10c477925202e29e095c
一、 C++代码实现如下
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 #define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移的时候,高位一定要补零,而不是补充符号位 5 #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) 6 #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) 7 #define H(x, y, z) ((x) ^ (y) ^ (z)) 8 #define I(x, y, z) ((y) ^ ((x) | (~z))) 9 #define A 0x67452301 10 #define B 0xefcdab89 11 #define C 0x98badcfe 12 #define D 0x10325476 13 //strBaye的长度 14 unsigned int strlength; 15 //A,B,C,D的临时变量 16 unsigned int atemp; 17 unsigned int btemp; 18 unsigned int ctemp; 19 unsigned int dtemp; 20 //常量ti unsigned int(abs(sin(i+1))*(2pow32)) 21 const unsigned int k[]={ 22 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee, 23 0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8, 24 0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193, 25 0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51, 26 0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8, 27 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905, 28 0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681, 29 0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60, 30 0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05, 31 0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244, 32 0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92, 33 0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314, 34 0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391}; 35 //向左位移数 36 const unsigned int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7, 37 12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, 38 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10, 39 15,21,6,10,15,21,6,10,15,21,6,10,15,21}; 40 const char str16[]="0123456789abcdef"; 41 void mainLoop(unsigned int M[]) 42 { 43 unsigned int f,g; 44 unsigned int a=atemp; 45 unsigned int b=btemp; 46 unsigned int c=ctemp; 47 unsigned int d=dtemp; 48 for (unsigned int i = 0; i < 64; i++) 49 { 50 if(i<16){ 51 f=F(b,c,d); 52 g=i; 53 }else if (i<32) 54 { 55 f=G(b,c,d); 56 g=(5*i+1)%16; 57 }else if(i<48){ 58 f=H(b,c,d); 59 g=(3*i+5)%16; 60 }else{ 61 f=I(b,c,d); 62 g=(7*i)%16; 63 } 64 unsigned int tmp=d; 65 d=c; 66 c=b; 67 b=b+shift((a+f+k[i]+M[g]),s[i]); 68 a=tmp; 69 } 70 atemp=a+atemp; 71 btemp=b+btemp; 72 ctemp=c+ctemp; 73 dtemp=d+dtemp; 74 } 75 /* 76 *填充函数 77 *处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64) 78 *填充方式为先加一个1,其它位补零 79 *最后加上64位的原来长度 80 */ 81 unsigned int* add(string str) 82 { 83 unsigned int num=((str.length()+8)/64)+1;//以512位,64个字节为一组 84 unsigned int *strByte=new unsigned int[num*16]; //64/4=16,所以有16个整数 85 strlength=num*16; 86 for (unsigned int i = 0; i < num*16; i++) 87 strByte[i]=0; 88 for (unsigned int i=0; i <str.length(); i++) 89 { 90 strByte[i>>2]|=(str[i])<<((i%4)*8);//一个整数存储四个字节,i>>2表示i/4 一个unsigned int对应4个字节,保存4个字符信息 91 } 92 strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);//尾部添加1 一个unsigned int保存4个字符信息,所以用128左移 93 /* 94 *添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位 95 */ 96 strByte[num*16-2]=str.length()*8; 97 return strByte; 98 } 99 string changeHex(int a) 100 { 101 int b; 102 string str1; 103 string str=""; 104 for(int i=0;i<4;i++) 105 { 106 str1=""; 107 b=((a>>i*8)%(1<<8))&0xff; //逆序处理每个字节 108 for (int j = 0; j < 2; j++) 109 { 110 str1.insert(0,1,str16[b%16]); 111 b=b/16; 112 } 113 str+=str1; 114 } 115 return str; 116 } 117 string getMD5(string source) 118 { 119 atemp=A; //初始化 120 btemp=B; 121 ctemp=C; 122 dtemp=D; 123 unsigned int *strByte=add(source); 124 for(unsigned int i=0;i<strlength/16;i++) 125 { 126 unsigned int num[16]; 127 for(unsigned int j=0;j<16;j++) 128 num[j]=strByte[i*16+j]; 129 mainLoop(num); 130 } 131 return changeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp)); 132 } 133 134 int main() 135 { 136 string ss; 137 // cin>>ss; 138 string s=getMD5("abc"); 139 cout<<s; 140 return 0; 141 }