MD5算法
# MD5 是什么
MD5 全称为 消息摘要算法版本 5 (Message Digest Algorithm 5)
它是一种 Hash 算法。作用是为了信息安全。再具体点,MD5 值就是一串 128 位二进制数。也就是 32 位(当然还有 16 位, 一般默认为 32 位)的十六进制数。
MD5 的核心是 通过算法把任意长度的原始数据映射成 128 bit 的数据 。这一点跟 CRC 类似,都是把一串数据经过处理,得到另一个固定长度的数据。
MD5 的特点:
- 长度固定:任意长度的数据,算出的 MD5 值长度都是固定的 128 位
- 容易计算:从源数据计算出 MD5 值很容易
- 抗修改性:对原数据进行任何改动(哪怕一个字节),所得到的 MD5 值都有很大的区别
- 强抗碰撞:向找到两个不同的数据,使它们具有相同的 MD5 值,是非常困难的
- 不可逆(除非暴力破解)
MD5 到底算不算加密算法?仁者见仁智者见智吧。说它是加密,因为它确实把原始数据,比如用户密码,变成了一般人看不懂的 MD5 值;说它不是加密,因为它不能解密。可以把它理解为 单向加密算法。
# 工作原理
- 把消息分为 n 个分组
- 对最后一个消息分组进行填充
- 和输入量进行运算,运算结果位下一个分组的输入量
- 输出最终结果
js 实现
function md5(string) {
function md5_RotateLeft(lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits))
}
function md5_AddUnsigned(lX, lY) {
var lX4, lY4, lX8, lY8, lResult
lX8 = lX & 0x80000000
lY8 = lY & 0x80000000
lX4 = lX & 0x40000000
lY4 = lY & 0x40000000
lResult = (lX & 0x3fffffff) + (lY & 0x3fffffff)
if (lX4 & lY4) {
return lResult ^ 0x80000000 ^ lX8 ^ lY8
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return lResult ^ 0xc0000000 ^ lX8 ^ lY8
} else {
return lResult ^ 0x40000000 ^ lX8 ^ lY8
}
} else {
return lResult ^ lX8 ^ lY8
}
}
function md5_F(x, y, z) {
return (x & y) | (~x & z)
}
function md5_G(x, y, z) {
return (x & z) | (y & ~z)
}
function md5_H(x, y, z) {
return x ^ y ^ z
}
function md5_I(x, y, z) {
return y ^ (x | ~z)
}
function md5_FF(a, b, c, d, x, s, ac) {
a = md5_AddUnsigned(
a,
md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac)
)
return md5_AddUnsigned(md5_RotateLeft(a, s), b)
}
function md5_GG(a, b, c, d, x, s, ac) {
a = md5_AddUnsigned(
a,
md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac)
)
return md5_AddUnsigned(md5_RotateLeft(a, s), b)
}
function md5_HH(a, b, c, d, x, s, ac) {
a = md5_AddUnsigned(
a,
md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac)
)
return md5_AddUnsigned(md5_RotateLeft(a, s), b)
}
function md5_II(a, b, c, d, x, s, ac) {
a = md5_AddUnsigned(
a,
md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac)
)
return md5_AddUnsigned(md5_RotateLeft(a, s), b)
}
function md5_ConvertToWordArray(string) {
var lWordCount
var lMessageLength = string.length
var lNumberOfWords_temp1 = lMessageLength + 8
var lNumberOfWords_temp2 =
(lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64
var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16
var lWordArray = Array(lNumberOfWords - 1)
var lBytePosition = 0
var lByteCount = 0
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4
lBytePosition = (lByteCount % 4) * 8
lWordArray[lWordCount] =
lWordArray[lWordCount] |
(string.charCodeAt(lByteCount) << lBytePosition)
lByteCount++
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4
lBytePosition = (lByteCount % 4) * 8
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition)
lWordArray[lNumberOfWords - 2] = lMessageLength << 3
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29
return lWordArray
}
function md5_WordToHex(lValue) {
var WordToHexValue = '',
WordToHexValue_temp = '',
lByte,
lCount
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255
WordToHexValue_temp = '0' + lByte.toString(16)
WordToHexValue =
WordToHexValue +
WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2)
}
return WordToHexValue
}
function md5_Utf8Encode(string) {
string = string.replace(/\r\n/g, '\n')
var utftext = ''
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n)
if (c < 128) {
utftext += String.fromCharCode(c)
} else if (c > 127 && c < 2048) {
utftext += String.fromCharCode((c >> 6) | 192)
utftext += String.fromCharCode((c & 63) | 128)
} else {
utftext += String.fromCharCode((c >> 12) | 224)
utftext += String.fromCharCode(((c >> 6) & 63) | 128)
utftext += String.fromCharCode((c & 63) | 128)
}
}
return utftext
}
var x = Array()
var k, AA, BB, CC, DD, a, b, c, d
var S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22
var S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20
var S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23
var S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21
string = md5_Utf8Encode(string)
x = md5_ConvertToWordArray(string)
a = 0x67452301
b = 0xefcdab89
c = 0x98badcfe
d = 0x10325476
for (k = 0; k < x.length; k += 16) {
AA = a
BB = b
CC = c
DD = d
a = md5_FF(a, b, c, d, x[k + 0], S11, 0xd76aa478)
d = md5_FF(d, a, b, c, x[k + 1], S12, 0xe8c7b756)
c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070db)
b = md5_FF(b, c, d, a, x[k + 3], S14, 0xc1bdceee)
a = md5_FF(a, b, c, d, x[k + 4], S11, 0xf57c0faf)
d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787c62a)
c = md5_FF(c, d, a, b, x[k + 6], S13, 0xa8304613)
b = md5_FF(b, c, d, a, x[k + 7], S14, 0xfd469501)
a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098d8)
d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8b44f7af)
c = md5_FF(c, d, a, b, x[k + 10], S13, 0xffff5bb1)
b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895cd7be)
a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6b901122)
d = md5_FF(d, a, b, c, x[k + 13], S12, 0xfd987193)
c = md5_FF(c, d, a, b, x[k + 14], S13, 0xa679438e)
b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49b40821)
a = md5_GG(a, b, c, d, x[k + 1], S21, 0xf61e2562)
d = md5_GG(d, a, b, c, x[k + 6], S22, 0xc040b340)
c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265e5a51)
b = md5_GG(b, c, d, a, x[k + 0], S24, 0xe9b6c7aa)
a = md5_GG(a, b, c, d, x[k + 5], S21, 0xd62f105d)
d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453)
c = md5_GG(c, d, a, b, x[k + 15], S23, 0xd8a1e681)
b = md5_GG(b, c, d, a, x[k + 4], S24, 0xe7d3fbc8)
a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21e1cde6)
d = md5_GG(d, a, b, c, x[k + 14], S22, 0xc33707d6)
c = md5_GG(c, d, a, b, x[k + 3], S23, 0xf4d50d87)
b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455a14ed)
a = md5_GG(a, b, c, d, x[k + 13], S21, 0xa9e3e905)
d = md5_GG(d, a, b, c, x[k + 2], S22, 0xfcefa3f8)
c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676f02d9)
b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8d2a4c8a)
a = md5_HH(a, b, c, d, x[k + 5], S31, 0xfffa3942)
d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771f681)
c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6d9d6122)
b = md5_HH(b, c, d, a, x[k + 14], S34, 0xfde5380c)
a = md5_HH(a, b, c, d, x[k + 1], S31, 0xa4beea44)
d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4bdecfa9)
c = md5_HH(c, d, a, b, x[k + 7], S33, 0xf6bb4b60)
b = md5_HH(b, c, d, a, x[k + 10], S34, 0xbebfbc70)
a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289b7ec6)
d = md5_HH(d, a, b, c, x[k + 0], S32, 0xeaa127fa)
c = md5_HH(c, d, a, b, x[k + 3], S33, 0xd4ef3085)
b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881d05)
a = md5_HH(a, b, c, d, x[k + 9], S31, 0xd9d4d039)
d = md5_HH(d, a, b, c, x[k + 12], S32, 0xe6db99e5)
c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1fa27cf8)
b = md5_HH(b, c, d, a, x[k + 2], S34, 0xc4ac5665)
a = md5_II(a, b, c, d, x[k + 0], S41, 0xf4292244)
d = md5_II(d, a, b, c, x[k + 7], S42, 0x432aff97)
c = md5_II(c, d, a, b, x[k + 14], S43, 0xab9423a7)
b = md5_II(b, c, d, a, x[k + 5], S44, 0xfc93a039)
a = md5_II(a, b, c, d, x[k + 12], S41, 0x655b59c3)
d = md5_II(d, a, b, c, x[k + 3], S42, 0x8f0ccc92)
c = md5_II(c, d, a, b, x[k + 10], S43, 0xffeff47d)
b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845dd1)
a = md5_II(a, b, c, d, x[k + 8], S41, 0x6fa87e4f)
d = md5_II(d, a, b, c, x[k + 15], S42, 0xfe2ce6e0)
c = md5_II(c, d, a, b, x[k + 6], S43, 0xa3014314)
b = md5_II(b, c, d, a, x[k + 13], S44, 0x4e0811a1)
a = md5_II(a, b, c, d, x[k + 4], S41, 0xf7537e82)
d = md5_II(d, a, b, c, x[k + 11], S42, 0xbd3af235)
c = md5_II(c, d, a, b, x[k + 2], S43, 0x2ad7d2bb)
b = md5_II(b, c, d, a, x[k + 9], S44, 0xeb86d391)
a = md5_AddUnsigned(a, AA)
b = md5_AddUnsigned(b, BB)
c = md5_AddUnsigned(c, CC)
d = md5_AddUnsigned(d, DD)
}
return (
md5_WordToHex(a) +
md5_WordToHex(b) +
md5_WordToHex(c) +
md5_WordToHex(d)
).toLowerCase()
}
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
参考 百度百科
# 应用
# 1. 密码认证
一般我们在一些“正规”网站上的注册账号密码时,一般不会在数据库直接保存用户的密码, 而是保存的密码的 MD5 值
用户登陆时,将用户输入的密码在客户端转换为 MD5,传输到后端,后端拿着 MD5 值与数据库存储的 MD5 值匹配, 如果一致就认为密码正确,允许登陆。
上图可以看出,腾讯微信公众号后台就是采用的这种模式。
虽然 md5 加密能提供一定的安全性,但是如果你的密码足够简单,那么一样可能被攻击,比如你的密码设为了 88888888, 那么它转换后的 md5 为 8ddcff3a80f4189ca1c9d4d902c3c909, 如果是 66666666 转换后等于 7c497868c9e6d3e4cf2e87396372cd3b
如果黑客手上维护了一张 MD5 常见密码表
MD5 | 密码 |
---|---|
8ddcff3a80f4189ca1c9d4d902c3c909 | 88888888 |
7c497868c9e6d3e4cf2e87396372cd3b | 66666666 |
25d55ad283aa400af464c76d713c07ad | 12345678 |
dd4b21e9ef71e1291183a46b913ae6f2 | 00000000 |
。。。。 | 。。。。 |
那么黑客拿着这些 MD5 值循环试探密码,你的账户就会被破解。 所以很多网站强制要求密码为数字+字母+符号的组合,且必须大于多少位, 就是为了防止这种攻击。
提供一个可以在线查询 MD5 的网站: MD5 查询 (opens new window)
# 2. MD5 有一个很大的用途就是确保文件完整性。
开发前端项目有时需要把前端打包成 zip 文件用邮件发给别人, 为了保证完整性我会在文件名尾拼上这个 zip 包的 md5, 接收文件的人收到后也对 zip 包进行 md5 计算,两个 md5 做对比,如果一致证明文件完整,不一致则表示传输过程有丢包,文件不可用
生成的 zip 文件