转自MD5算法步骤详解

之前要写一个MD5程序,但是从网络上看到的资料基本上一样,只是讲了一个大概。经过我自己的实践,我决定写一个心得,给需要实现MD5,但又不要求很高深的编程知识的童鞋参考。不多说了,直接进入正题。

MD5算法是什么,MD5的历史由来等等我都不介绍了,想要了解的童鞋直接百度吧,见谅~~我们直接讲算法步骤。我的事例是对一个字符串进行MD5加密,没有实现对文件的MD5加密,大家看了这个事例之后应该自己能抛砖引玉了。如果想参考完整代码,可以进此查看:http://blog.csdn.net/wjl15989/article/details/8606997

步骤1:我们是对一个字符串进行MD5加密,所以我们先从字符串的处理开始。首先我们要知道一个字符的长度是8位(bit),即一个字节的长度。现在我们要做的就是将一个字符串Str1分割成每512位为一个分组,形如N*512+R,最后多出来的不足512位的R部分先填充一个1,再接无数个0,直到补足512位。这里要注意,R为0时也要补位,这时候补512位,最高位1,形如1000…00;如果R超出448,除了要补满这个分组外,还要再补上一个512位的分组(因为超过448位则不能留64位出来存放字符串的原长)。

接着,讲讲将字符串分块保存部分。一个512位的字符串分组要分成16个32位的子分组,在每个32位中,以字节为单位通过小端规则存入一个32位的变量中,可以考虑用int类型的变量(一个int变量32位),也可以考虑用unsigned
int,这样之后涉及的循环移位就不用考虑符号位了,这里还是以int为例。因为一个字符就是一个字节(8位),所以一个int类型变量能存放4个字符,假设一个字符串abcd,那么存在一个int类型变量中就是cdab。因此这里我们将字符串每4个字符分成一块,每一个块都以小端规则存放在一个int类型的变量中。估计有的人不清楚小端规则(Little-Endian),可以上网查,这里就不详细说了,见谅~

补充好后的Str2长度为(N+1)*512位(如果R超出448,则是(N+2)*512),此时最低的64位预留,用来存放之前str1的长度length(长度为字符个数*8
bit)的值,如果这个length值的二进制位数大于64位,则只保留最低的64位。将这个64位的length放入之前填充好的str2的最后64位又要注意了:将length的64位分成4个16位,相当于4位2^16进制的数,再对这个数用小端规则排列,分别填入预留的64位。之前我就是这点没有领悟,估计大家也不是很懂,我举个例子:假设64位分成ABCD(A,B,C,D分别表示16位的二进制数,A是高位,D是低位),按小端规则排列后就是CDAB,将形如CDAB的64位按C(高位)到B(低位)的顺序填入str2预留的64位。

至此,补位的思想已经讲完了,这里再讲讲我的具体实现。我的思路是用一个长度为16的int类型的数组int
M[16]。因为一个int类型数据有32位,16个加起来刚好一共512位,是一个分组的长度。我刚好就按顺序M[0]…M[15]表示一个512位的数。我再声明一个容器vector,用来存放每个M[16],因为分组个数不一定只有一个。

最后我举个例子方便大家理解。首先介绍一些常识:a – 61, b – 62, c – 63, d – 64, e – 65。这里“a
– 61”表示a的ASCII码十六进制表示是0x61,其他以此类推。

好,假设一个字符串abcde,一共5个字符,长度length 为 5*
8 = 40 = 0x28。512位转化成十六进制就是64位。原字符串十六进制表示:61
62 63 64 65 00 00…00。完成补位后共512位,只有1个分组,形如: 61
62 63 64 65 80 00… 00(“80”的二进制是1000
0000,即之前的先补一个1,再补很多0的做法)。一个int
M[16]的数组就够存了,即

M[0] = 64 63 62 61,

M[1] = 00 00 80 65,

M[2] = 0,

M[3] = 0

M[14] = 00 00 00 28,

M[15] = 0

M[0]~M[15]设好之后,在内存中就是这样存的61
62 63 64 65 80 00…00(注意这里我们用MD5处理字符串时都考虑内存中的数据的排列顺序,得出的MD5也是需要按内存中的数据输出,所以经常要用小端规则转换)

看完这个例子,大家应该对步骤1的内容有比较全面的了解了

步骤2:MD5有四个32位的被称作链接变量的整数参数,我们进行如下设置:

A=0x67452301,

B=0xefcdab89,

C=0x98badcfe,

D=0x10325476。

数据这样设置之后,存在内存中就按小端规则排列:01 23 45 67 89 ab cd ef …32 10

再声明四个中间变量a,b,c,d,赋值:a
= A, b = B, c = C, d = D。

接着再设置四个非线性函数:

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

(&是与,|是或,~是非,^是异或)

这四个函数的说明:如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。

假设M[j]表示消息的第j个子分组(从0到15),<<<s表示循环左移s,常数ti是4294967296*abs(sin(i))的整数部分,i取值从1到64,单位是弧度。(4294967296等于2的32次方)

FF(a, b, c, d, M[j], s, ti)表示 a
= b + ((a + F(b, c, d) + Mj + ti) <<< s)

GG(a, b, c, d, M[j], s, ti)表示 a
= b + ((a + G(b, c, d) + Mj + ti) <<< s)

HH(a, b, c, d, M[j], s, ti)表示 a
= b + ((a + H(b, c, d) + Mj + ti) <<< s)

II(a, b, c, d, M[j], s, ti)表示 a
= b + ((a + I(b, c, d) + Mj + ti) <<< s)

要确保形参a在内存中的值改变了,可以在形参中用按引用调用(&a),或返回a值取代原来a值。

步骤3:接下来就是要进行一个MD5算法的主要循环了,这个循环的循环次数为512位分组的个数(即之前提到的N+1或者N+2)。每次循环执行以下的步骤,我就不用文字表述了,直接用代码展示,相信大家能理解:

a = A; b = B; c = C; d = D;

//传说中的对M[j]的第一轮循环

FF(a,b,c,d,M[0],7,0xd76aa478);

FF(d,a,b,c,M[1],12,0xe8c7b756);

FF(c,d,a,b,M[2],17,0x242070db);

FF(b,c,d,a,M[3],22,0xc1bdceee);

FF(a,b,c,d,M[4],7,0xf57c0faf);

FF(d,a,b,c,M[5],12,0x4787c62a);

FF(c,d,a,b,M[6],17,0xa8304613);

FF(b,c,d,a,M[7],22,0xfd469501) ;

FF(a,b,c,d,M[8],7,0x698098d8) ;

FF(d,a,b,c,M[9],12,0x8b44f7af) ;

FF(c,d,a,b,M[10],17,0xffff5bb1) ;

FF(b,c,d,a,M[11],22,0x895cd7be) ;

FF(a,b,c,d,M[12],7,0x6b901122) ;

FF(d,a,b,c,M[13],12,0xfd987193) ;

FF(c,d,a,b,M[14],17,0xa679438e) ;

FF(b,c,d,a,M[15],22,0x49b40821);

//传说中对M[j]的第二轮循环

GG(a,b,c,d,M[1],5,0xf61e2562);

GG(d,a,b,c,M[6],9,0xc040b340);

GG(c,d,a,b,M[11],14,0x265e5a51);

GG(b,c,d,a,M[0],20,0xe9b6c7aa) ;

GG(a,b,c,d,M[5],5,0xd62f105d) ;

GG(d,a,b,c,M[10],9,0x02441453) ;

GG(c,d,a,b,M[15],14,0xd8a1e681);

GG(b,c,d,a,M[4],20,0xe7d3fbc8) ;

GG(a,b,c,d,M[9],5,0x21e1cde6) ;

GG(d,a,b,c,M[14],9,0xc33707d6) ;

GG(c,d,a,b,M[3],14,0xf4d50d87) ;

GG(b,c,d,a,M[8],20,0x455a14ed);

GG(a,b,c,d,M[13],5,0xa9e3e905);

GG(d,a,b,c,M[2],9,0xfcefa3f8) ;

GG(c,d,a,b,M[7],14,0x676f02d9) ;

GG(b,c,d,a,M[12],20,0x8d2a4c8a);

//传说中对M[j]的第三轮循环

HH(a,b,c,d,M[5],4,0xfffa3942);

HH(d,a,b,c,M[8],11,0x8771f681);

HH(c,d,a,b,M[11],16,0x6d9d6122);

HH(b,c,d,a,M[14],23,0xfde5380c) ;

HH(a,b,c,d,M[1],4,0xa4beea44) ;

HH(d,a,b,c,M[4],11,0x4bdecfa9) ;

HH(c,d,a,b,M[7],16,0xf6bb4b60) ;

HH(b,c,d,a,M[10],23,0xbebfbc70);

HH(a,b,c,d,M[13],4,0x289b7ec6);

HH(d,a,b,c,M[0],11,0xeaa127fa);

HH(c,d,a,b,M[3],16,0xd4ef3085);

HH(b,c,d,a,M[6],23,0x04881d05);

HH(a,b,c,d,M[9],4,0xd9d4d039);

HH(d,a,b,c,M[12],11,0xe6db99e5);

HH(c,d,a,b,M[15],16,0x1fa27cf8) ;

HH(b,c,d,a,M[2],23,0xc4ac5665);

//传说中对M[j]的第四轮循环

II(a,b,c,d,M[0],6,0xf4292244) ;

II(d,a,b,c,M[7],10,0x432aff97) ;

II(c,d,a,b,M[14],15,0xab9423a7);

II(b,c,d,a,M[5],21,0xfc93a039) ;

II(a,b,c,d,M[12],6,0x655b59c3) ;

II(d,a,b,c,M[3],10,0x8f0ccc92) ;

II(c,d,a,b,M[10],15,0xffeff47d);

II(b,c,d,a,M[1],21,0x85845dd1) ;

II(a,b,c,d,M[8],6,0x6fa87e4f) ;

II(d,a,b,c,M[15],10,0xfe2ce6e0);

II(c,d,a,b,M[6],15,0xa3014314) ;

II(b,c,d,a,M[13],21,0x4e0811a1);

II(a,b,c,d,M[4],6,0xf7537e82) ;

II(d,a,b,c,M[11],10,0xbd3af235);

II(c,d,a,b,M[2],15,0x2ad7d2bb);

II(b,c,d,a,M[9],21,0xeb86d391);

A += a;

B += b;

C += c;

D += d;

步骤4:处理完所有的512位的分组后,得到一组新的A,B,C,D的值,将这些值按ABCD的顺序级联,然后输出。这里还要注意,输出的MD5是按内存中数值的排列顺序,所以我们要分别对A,B,C,D的值做一个小端规则的转换。举个例子:A有32位,分成4个字节A1A2A3A4。输出A的时候,要这样输出:A4A3 A2A1。这样就能输出正确的MD5了。

下面给一些例子大家测试:

MD5 ("") = d41d8cd98f00b204e9800998ecf8427e

MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661

MD5 (“ab”) = 187ef4436122d1cc2f40dc2b92f0eba0

MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72

MD5 (“abcde”) = ab56b4d92b40713acc5af89985d4b786

MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0

MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b

MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") =

    f29939a25efabaef3b87e2cbfe641315

最新文章

  1. CQRS, Task Based UIs, Event Sourcing agh!
  2. 关于ActionBar
  3. 深入理解Java:String
  4. Android 短信广播接收相关问题
  5. MAC地址泛洪攻击测试
  6. Hadoop 2.4.1 登录认证配置小结
  7. MongoDB学习笔记——数据库安装及配置
  8. hdu5314 Happy King
  9. CSS的优先级规则
  10. 《Linux命令行与shell脚本编程大全》 第一、二章 学习笔记
  11. Rollup.js 实践
  12. MVC输出缓存(OutputCache参数详解)
  13. 【Java】Java初始化过程总结
  14. XAMPP中MySQL无法启动解决办法
  15. docker cp 和docker exec 查看docker 运行的容器信息
  16. Mysql索引详解及优化(key和index区别)
  17. CSU1911 Card Game 【FWT】
  18. JS 操作iframe
  19. python统计文本中每个单词出现的次数
  20. 【javascript基础】运算符优先级

热门文章

  1. 到目前为止,Linux下最完整的Samba服务器配置攻略
  2. Each child in an array or iterator should have a unique &quot;key&quot; prop. Check the render method of `CreditCategoryModal`
  3. Extjs combo赋值与刷新的先后顺序
  4. 多个html编辑器在同一页面加载
  5. android事件分发介绍
  6. SQL访问EXCEL错误集合
  7. 九度OJ 1514 数值的整数次方【算法】
  8. Linux负载均衡概念与实践(二)
  9. A-Frame 简介03
  10. NSSpeechSynthesizer 文字变语音