讲得不清楚,或者说很费解。最后不得不拿出C语言的源程序来调试,这对于理解算法是很不
利的。于是就总结了一下我摸索到的一些要点。
1.来历
MD5的全称是message-digest algorithm 5(信息-摘要算法,在90年代初由mit laboratory
for computer science和rsa data security inc的ronald l. rivest开发出来,
经md2、md3和md4发展而来。http://www.ietf.org/rfc/rfc1321.txt,是一份最权威的文档,
由ronald l. rivest在1992年8月向ieft提交。
2.用途
MD5的作用是对一段信息(message)生成信息摘要(message-digest),该摘要对该信息具有
唯一性,可以作为数字签名。用于验证文件的有效性(是否有丢失或损坏的数据),对用户
密码的加密,在哈希函数中计算散列值。
3.特点
输入一个任意长度的字节串,生成一个128位的整数。由于算法的某些不可逆特征,在加密应用
上有较好的安全性。并且,MD5算法的使用不需要支付任何版权费用。
4.说明
唯一性和不可逆性都不是绝对的,从理论上分析是一种多对一的关系,但两个不同的信息产生
相同摘要的概率很小。不可逆是指从输出反推输入所需的运算量和计算时间太大,使用穷搜字
典的方法又需要太多的存储空间。
5.算法描述
算法输入是一个字节串,每个字节是8个bit.
算法的执行分为以下几个步骤:
第一步,补位:
MD5算法先对输入的数据进行补位,使得数据的长度(以byte为单位)对64求余的结果是56。
即数据扩展至LEN=K*64+56个字节,K为整数。
补位方法:补一个1,然后补0至满足上述要求。相当于补一个0x80的字节,再补值
为0的字节。这一步里总共补充的字节数为0~63个。
第二步,附加数据长度:
用一个64位的整数表示数据的原始长度(以bit为单位),将这个数字的8个字节按低位的在前,
高位在后的顺序附加在补位后的数据后面。这时,数据被填补后的总长度为:
LEN = K*64+56+8=(K+1)*64 Bytes。
※注意那个64位整数是输入数据的原始长度而不是填充字节后的长度,我就在这里栽了跟头.
第三步,初始化MD5参数:
有四个32位整数变量 (A,B,C,D) 用来计算信息摘要,每一个变量被初始化成以下
以十六进制数表示的数值,低位的字节在前面。
word A: 01 23 45 67
word B: 89 ab cd ef
word C: fe dc ba 98
word D: 76 54 32 10
※注意低位的字节在前面指的是Little Endian平台上内存中字节的排列方式,
而在程序中书写时,要写成:
A=0x67452301
B=0xefcdab89
C=0x98badcfe
D=0x10325476
第四步,定义四个MD5基本的按位操作函数:
X,Y,Z为32位整数。
F(X,Y,Z) = (X and Y) or (not(X) and Z)
G(X,Y,Z) = (X and Z) or (Y and not(Z))
H(X,Y,Z) = X xor Y xor Z
I(X,Y,Z) = Y xor (X or not(Z))
再定义四个分别用于四轮变换的函数。
设Mj表示消息的第j个子分组(从0到15),<<<s表示循环左移s位,则四种操作为:
FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti)<<<s)
GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti)<<<s)
HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti)<<<s)
II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)<<<s)
第五步,对输入数据作变换。
处理数据,N是总的字节数,以64个字节为一组,每组作一次循环,每次循环进行四轮操作。
要变换的64个字节用16个32位的整数数组M[0 ...15]表示。而数组T[1 ... 64]表示一组常数,
T[i]为4294967296*abs(sin(i))的32位整数部分,i的单位是弧度,i的取值从1到64。
具体过程如下:
/* 设置主循环变量 */
For i = 0 to N/16-1 do
/*每循环一次,把数据原文存放在16个元素的数组X中. */
For j = 0 to 15 do
Set X[j] to M[i*16+j].
end /结束对J的循环
/* Save A as AA, B as BB, C as CC, and D as DD.
*/
AA = A
BB = B
CC = C
DD = D
/* 第1轮*/
/* 以 [abcd k s i]表示如下操作
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4]
[ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8]
[ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12]
[ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16]
/* 第2轮* */
/* 以 [abcd k s i]表示如下操作
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20]
[ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24]
[ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28]
[ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32]
/* 第3轮*/
/* 以 [abcd k s i]表示如下操作
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36]
[ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40]
[ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44]
[ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48]
/* 第4轮*/
/* 以 [abcd k s i]表示如下操作
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */
[ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52]
[ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56]
[ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60]
[ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64]
/* 然后进行如下操作 */
A = A + AA
B = B + BB
C = C + CC
D = D + DD
Next i /* 结束对I的循环*/
第六步,输出结果。
A,B,C,D连续存放,共16个字节,128位。按十六进制依次输出这个16个字节。
最后,用程序语言实现算法后,可以输入以下几个信息对程序作一个简单的测试,
看看程序有没有错误。
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
d174ab98d277d9f5a5611c2c9f419d9f
MD5 ("123456789012345678901234567890123456789012345678901234567890123456789
01234567890") = 57edf4a22be3c955ac49da2e2107b67a
MD5算法之C#程序
MD5算法比较特别,最适合用汇编语言来写,好多高级语言对之无能无力或效率极低。
比如我最开始尝试用Python和Euphoria编写,发现不太容易。相比而言,C#作为C家簇
中新兴的一门.net语言,功能比较全面。花了一晚上的工夫终于用C#最先实现了MD5。
主要是由于对算法的一些细节不太注意,结果输出总是不对,调试了好长时间。
[code]
1
//源文件:md5.cs
2
// MD5 Alogrithm
3
// by rufi 2004.6.20 http://rufi.yculblog.com/
4
using System;
5
using System.Collections;
6
using System.IO;
7
8
public class MD5 {
9
//static state variables
10
private static UInt32 A;
11
private static UInt32 B;
12
private static UInt32 C;
13
private static UInt32 D;
14
15
//number of bits to rotate in tranforming
16
private const int S11 = 7;
17
private const int S12 = 12;
18
private const int S13 = 17;
19
private const int S14 = 22;
20
private const int S21 = 5;
21
private const int S22 = 9;
22
private const int S23 = 14;
23
private const int S24 = 20;
24
private const int S31 = 4;
25
private const int S32 = 11;
26
private const int S33 = 16;
27
private const int S34 = 23;
28
private const int S41 = 6;
29
private const int S42 = 10;
30
private const int S43 = 15;
31
private const int S44 = 21;
32
33
34
/* F, G, H and I are basic MD5 functions.
35
* 四个非线性函数:
36
*
37
* F(X,Y,Z) =(X&Y)|((~X)&Z)
38
* G(X,Y,Z) =(X&Z)|(Y&(~Z))
39
* H(X,Y,Z) =X^Y^Z
40
* I(X,Y,Z)=Y^(X|(~Z))
41
*
42
* (&与,|或,~非,^异或)
43
*/
44
private static UInt32 F(UInt32 x,UInt32 y,UInt32 z){
45
return (x&y)|((~x)&z);
46
}
47
private static UInt32 G(UInt32 x,UInt32 y,UInt32 z){
48
return (x&z)|(y&(~z));
49
}
50
private static UInt32 H(UInt32 x,UInt32 y,UInt32 z){
51
return x^y^z;
52
}
53
private static UInt32 I(UInt32 x,UInt32 y,UInt32 z){
54
return y^(x|(~z));
55
}
56
57
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
58
* Rotation is separate from addition to prevent recomputation.
59
*/
60
private static void FF(ref UInt32 a,UInt32 b,UInt32 c,UInt32 d,UInt32 mj,int s,UInt32 ti){
61
a = a + F(b,c,d) + mj + ti;
62
a = a << s | a >> (32-s);
63
a += b;
64
}
65
private static void GG(ref UInt32 a,UInt32 b,UInt32 c,UInt32 d,UInt32 mj,int s,UInt32 ti){
66
a = a + G(b,c,d) + mj + ti;
67
a = a << s | a >> (32-s);
68
a += b;
69
}
70
private static void HH(ref UInt32 a,UInt32 b,UInt32 c,UInt32 d,UInt32 mj,int s,UInt32 ti){
71
a = a + H(b,c,d) + mj + ti;
72
a = a << s | a >> (32-s);
73
a += b;
74
}
75
private static void II(ref UInt32 a,UInt32 b,UInt32 c,UInt32 d,UInt32 mj,int s,UInt32 ti){
76
a = a + I(b,c,d) + mj + ti;
77
a = a << s | a >> (32-s);
78
a += b;
79
}
80
81
private static void MD5_Init(){
82
A=0x67452301; //in memory, this is 0x01234567
83
B=0xefcdab89; //in memory, this is 0x89abcdef
84
C=0x98badcfe; //in memory, this is 0xfedcba98
85
D=0x10325476; //in memory, this is 0x76543210
86
}
87
88
private static UInt32[] MD5_Append(byte[] input){
89
int zeros=0;
90
int ones =1;
91
int size=0;
92
int n = input.Length;
93
int m = n%64;
94
if( m < 56 ){
95
zeros = 55-m;
96
size=n-m+64;
97
}
98
else if (m==56){
99
zeros = 0;
100
ones = 0;
101
size=n+8;
102
}
103
else{
104
zeros = 63-m+56;
105
size=n+64-m+64;
106
}
107
108
ArrayList bs = new ArrayList(input);
109
if(ones==1){
110
bs.Add( (byte)0x80 ); // 0x80 = $10000000
111
}
112
for(int i=0;i<zeros;i++){
113
bs.Add( (byte)0 );
114
}
115
116
UInt64 N = (UInt64) n * 8;
117
byte h1=(byte)(N&0xFF);
118
byte h2=(byte)((N>>8)&0xFF);
119
byte h3=(byte)((N>>16)&0xFF);
120
byte h4=(byte)((N>>24)&0xFF);
121
byte h5=(byte)((N>>32)&0xFF);
122
byte h6=(byte)((N>>40)&0xFF);
123
byte h7=(byte)((N>>48)&0xFF);
124
byte h8=(byte)(N>>56);
125
bs.Add(h1);
126
bs.Add(h2);
127
bs.Add(h3);
128
bs.Add(h4);
129
bs.Add(h5);
130
bs.Add(h6);
131
bs.Add(h7);
132
bs.Add(h8);
133
byte[] ts=(byte[])bs.ToArray(typeof(byte));
134
135
/* Decodes input (byte[]) into output (UInt32[]). Assumes len is
136
* a multiple of 4.
137
*/
138
UInt32[] output = new UInt32[size/4];
139
for(Int64 i=0,j=0;i<size;j++,i+=4){
140
output[j]=(UInt32)(ts[i] | ts[i+1]<<8 | ts[i+2]<<16 | ts[i+3]<<24);
141
}
142
return output;
143
}
144
private static UInt32[] MD5_Trasform(UInt32[] x){
145
146
UInt32 a,b,c,d;
147
148
for(int k=0;k<x.Length;k+=16){
149
a=A;
150
b=B;
151
c=C;
152
d=D;
153
154
/* Round 1 */
155
FF (ref a, b, c, d, x[k+ 0], S11, 0xd76aa478); /* 1 */
156
FF (ref d, a, b, c, x[k+ 1], S12, 0xe8c7b756); /* 2 */
157
FF (ref c, d, a, b, x[k+ 2], S13, 0x242070db); /* 3 */
158
FF (ref b, c, d, a, x[k+ 3], S14, 0xc1bdceee); /* 4 */
159
FF (ref a, b, c, d, x[k+ 4], S11, 0xf57c0faf); /* 5 */
160
FF (ref d, a, b, c, x[k+ 5], S12, 0x4787c62a); /* 6 */
161
FF (ref c, d, a, b, x[k+ 6], S13, 0xa8304613); /* 7 */
162
FF (ref b, c, d, a, x[k+ 7], S14, 0xfd469501); /* 8 */
163
FF (ref a, b, c, d, x[k+ 8], S11, 0x698098d8); /* 9 */
164
FF (ref d, a, b, c, x[k+ 9], S12, 0x8b44f7af); /* 10 */
165
FF (ref c, d, a, b, x[k+10], S13, 0xffff5bb1); /* 11 */
166
FF (ref b, c, d, a, x[k+11], S14, 0x895cd7be); /* 12 */
167
FF (ref a, b, c, d, x[k+12], S11, 0x6b901122); /* 13 */
168
FF (ref d, a, b, c, x[k+13], S12, 0xfd987193); /* 14 */
169
FF (ref c, d, a, b, x[k+14], S13, 0xa679438e); /* 15 */
170
FF (ref b, c, d, a, x[k+15], S14, 0x49b40821); /* 16 */
171
172
/* Round 2 */
173
GG (ref a, b, c, d, x[k+ 1], S21, 0xf61e2562); /* 17 */
174
GG (ref d, a, b, c, x[k+ 6], S22, 0xc040b340); /* 18 */
175
GG (ref c, d, a, b, x[k+11], S23, 0x265e5a51); /* 19 */
176
GG (ref b, c, d, a, x[k+ 0], S24, 0xe9b6c7aa); /* 20 */
177
GG (ref a, b, c, d, x[k+ 5], S21, 0xd62f105d); /* 21 */
178
GG (ref d, a, b, c, x[k+10], S22, 0x2441453); /* 22 */
179
GG (ref c, d, a, b, x[k+15], S23, 0xd8a1e681); /* 23 */
180
GG (ref b, c, d, a, x[k+ 4], S24, 0xe7d3fbc8); /* 24 */
181
GG (ref a, b, c, d, x[k+ 9], S21, 0x21e1cde6); /* 25 */
182
GG (ref d, a, b, c, x[k+14], S22, 0xc33707d6); /* 26 */
183
GG (ref c, d, a, b, x[k+ 3], S23, 0xf4d50d87); /* 27 */
184
GG (ref b, c, d, a, x[k+ 8], S24, 0x455a14ed); /* 28 */
185
GG (ref a, b, c, d, x[k+13], S21, 0xa9e3e905); /* 29 */
186
GG (ref d, a, b, c, x[k+ 2], S22, 0xfcefa3f8); /* 30 */
187
GG (ref c, d, a, b, x[k+ 7], S23, 0x676f02d9); /* 31 */
188
GG (ref b, c, d, a, x[k+12], S24, 0x8d2a4c8a); /* 32 */
189
190
/* Round 3 */
191
HH (ref a, b, c, d, x[k+ 5], S31, 0xfffa3942); /* 33 */
192
HH (ref d, a, b, c, x[k+ 8], S32, 0x8771f681); /* 34 */
193
HH (ref c, d, a, b, x[k+11], S33, 0x6d9d6122); /* 35 */
194
HH (ref b, c, d, a, x[k+14], S34, 0xfde5380c); /* 36 */
195
HH (ref a, b, c, d, x[k+ 1], S31, 0xa4beea44); /* 37 */
196
HH (ref d, a, b, c, x[k+ 4], S32, 0x4bdecfa9); /* 38 */
197
HH (ref c, d, a, b, x[k+ 7], S33, 0xf6bb4b60); /* 39 */
198
HH (ref b, c, d, a, x[k+10], S34, 0xbebfbc70); /* 40 */
199
HH (ref a, b, c, d, x[k+13], S31, 0x289b7ec6); /* 41 */
200
HH (ref d, a, b, c, x[k+ 0], S32, 0xeaa127fa); /* 42 */
201
HH (ref c, d, a, b, x[k+ 3], S33, 0xd4ef3085); /* 43 */
202
HH (ref b, c, d, a, x[k+ 6], S34, 0x4881d05); /* 44 */
203
HH (ref a, b, c, d, x[k+ 9], S31, 0xd9d4d039); /* 45 */
204
HH (ref d, a, b, c, x[k+12], S32, 0xe6db99e5); /* 46 */
205
HH (ref c, d, a, b, x[k+15], S33, 0x1fa27cf8); /* 47 */
206
HH (ref b, c, d, a, x[k+ 2], S34, 0xc4ac5665); /* 48 */
207
208
/* Round 4 */
209
II (ref a, b, c, d, x[k+ 0], S41, 0xf4292244); /* 49 */
210
II (ref d, a, b, c, x[k+ 7], S42, 0x432aff97); /* 50 */
211
II (ref c, d, a, b, x[k+14], S43, 0xab9423a7); /* 51 */
212
II (ref b, c, d, a, x[k+ 5], S44, 0xfc93a039); /* 52 */
213
II (ref a, b, c, d, x[k+12], S41, 0x655b59c3); /* 53 */
214
II (ref d, a, b, c, x[k+ 3], S42, 0x8f0ccc92); /* 54 */
215
II (ref c, d, a, b, x[k+10], S43, 0xffeff47d); /* 55 */
216
II (ref b, c, d, a, x[k+ 1], S44, 0x85845dd1); /* 56 */
217
II (ref a, b, c, d, x[k+ 8], S41, 0x6fa87e4f); /* 57 */
218
II (ref d, a, b, c, x[k+15], S42, 0xfe2ce6e0); /* 58 */
219
II (ref c, d, a, b, x[k+ 6], S43, 0xa3014314); /* 59 */
220
II (ref b, c, d, a, x[k+13], S44, 0x4e0811a1); /* 60 */
221
II (ref a, b, c, d, x[k+ 4], S41, 0xf7537e82); /* 61 */
222
II (ref d, a, b, c, x[k+11], S42, 0xbd3af235); /* 62 */
223
II (ref c, d, a, b, x[k+ 2], S43, 0x2ad7d2bb); /* 63 */
224
II (ref b, c, d, a, x[k+ 9], S44, 0xeb86d391); /* 64 */
225
226
A+=a;
227
B+=b;
228
C+=c;
229
D+=d;
230
}
231
return new UInt32[]{A,B,C,D};
232
}
233
public static byte[] MD5Array(byte[] input){
234
MD5_Init();
235
UInt32[] block = MD5_Append(input);
236
UInt32[] bits = MD5_Trasform(block);
237
238
/* Encodes bits (UInt32[]) into output (byte[]). Assumes len is
239
* a multiple of 4.
240
*/
241
byte[] output=new byte[bits.Length*4];
242
for(int i=0,j=0;i<bits.Length;i++,j+=4){
243
output[j] = (byte)(bits[i] & 0xff);
244
output[j+1] = (byte)((bits[i] >> 8) & 0xff);
245
output[j+2] = (byte)((bits[i] >> 16) & 0xff);
246
output[j+3] = (byte)((bits[i] >> 24) & 0xff);
247
}
248
return output;
249
}
250
251
public static string ArrayToHexString(byte[] array,bool uppercase){
252
string hexString="";
253
string format="x2";
254
if(uppercase){
255
format="X2";
256
}
257
foreach(byte b in array){
258
hexString += b.ToString(format);
259
}
260
return hexString;
261
}
262
263
public static string MDString(string message){
264
char[] c = message.ToCharArray();
265
byte[] b = new byte[c.Length];
266
for(int i=0;i<c.Length;i++){
267
b[i]=(byte)c[i];
268
}
269
byte[] digest = MD5Array(b);
270
return ArrayToHexString(digest,false);
271
}
272
public static string MDFile(string fileName){
273
FileStream fs=File.Open(fileName,FileMode.Open,FileAccess.Read);
274
byte[] array=new byte[fs.Length];
275
fs.Read(array,0,(int)fs.Length);
276
byte[] digest = MD5Array(array);
277
fs.Close();
278
return ArrayToHexString(digest,false);
279
}
280
281
public static string Test(string message){
282
return "rnMD5 (""+message+"") = " + MD5.MDString(message);
283
}
284
public static string TestSuite(){
285
string s = "";
286
s+=Test("");
287
s+=Test("a");
288
s+=Test("abc");
289
s+=Test("message digest");
290
s+=Test("abcdefghijklmnopqrstuvwxyz");
291
s+=Test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
292
s+=Test("12345678901234567890123456789012345678901234567890123456789012345678901234567890");
293
return s;
294
}
295
}
296
[/code] 
2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296
