MD5
简介
MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16个字符(BYTES))的散列值(hash value),用于确保信息传输完整一致。
1996年后被证实存在弱点,可以被加以破解,对于需要高度安全性的资料,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞攻击,因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
实现过程
md5强化
首先将消息扩充为512bit(64byte)的整数倍。填充规则为:
在要处理的信息x的二进制表示之后先填入一个1,然后再添加若干个0(不超过511bit),使得message digest的bit长度是512的整数倍少64bit,最后再最低的64bit中填入消息x的
比特长度
的二进制表示。以上是密码学教科书中的md5强化的方法。
那么很容易得到填充的模式是这样的。
但是当你这样去填充的过程中,你会发现消息的bit长度的填充会存在一些问题。
譬如有这样的一个message digest ”abc“,其 bit长度为24,16进制表示为0x18;那么我们以人的思维去填充那么必然是这样的
0 0 0 0 0 0 0 18
,但是这样是错误的。因为这根本就不是真正的填充方式。
在关于md5 hash函数的rfc文档中有这么几句:
In this document a "word" is a 32-bit quantity and a "byte" is an eight-bit quantity. A sequence of bits can be interpreted in a natural manner as a sequence of bytes, where each consecutive group of eight bits is interpreted as a byte with the high-order (mostsignificant) bit of each byte listed first. Similarly, a sequence of bytes can be interpreted as a sequence of 32-bit words, where each consecutive group of four bytes is interpreted as a word with the low-order (least significant) byte given first.
A 64-bit representation of b (the length of the message before the padding bits were added) is appended to the result of the previous step. In the unlikely event that b is greater than 2^64, then only the low-order 64 bits of b are used. (These bits are appended as two 32-bit words and appended low-order word first in accordance with the previous conventions.)
那么就很明显了,在md5的计算中的,其word是从低字节到高字节的(其实这里也就是跨字节的小端序,至于为什么这里要采用小端序的方式来填充bit长度,其实我不是很清楚,就我个人的理解应该是为了后面步骤中的将512bit的block划分为多个32bit的block,因为在md5中其他的跨字节常量在计算机中存的都是小端序,此处采用小端序的顺序来存储方便后续的计算)
故真正的填充顺序为(上个例子) ->
18 0 0 0 0 0 0 0
以上表示填充方式了,简单的说一下就是先填充1bit,然后疯狂的添加0bit,直到$len(message\ digest) \mod 64 = 56 $即可开始填充比特长度。
md5 MainLoop
首先将message digest中的一个512bit的block划分为多个32bit的block;具体的方式如下:
首先在md5中存在初始量A, B, C, D;其长度均为32bit,以计算机小端序的眼光来看的话,那么其存储的顺序为
a[0], a[1], a[2], a[3]
(a[0]为低有效位);那么在对应的md5的计算中a[0]
必然对应一位message digest中的值,当然也就是每个32bit block的最前面的1 byte。但是这是在memory中,所以转化为正常的数字表示,那么32bit block也就需要reverse一下,以32bit中的最后一位为最高位,第一位为最低位。故转化式子为\(m[3] << 24 + m[2] << 16 + m[1] << 8 + m[0]\) (m为32bit block)。
接下来就是4 * 16/圈
的循环了。当然在这之前需要一些function&constant;
自此,便可以开始进行64轮操作了
// A, B, C, D 为常量
var (
ra, rb, rc, rd int
// 循环左移的长度
r [64]int = [64]int{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
}
)
ra, rb, rc, rd = A, B, C, D
func MainLoop(w []uint32) {
var f, g uint32
a, b, c, d = ra, rb, rc, rd
for i := 0; i < 64; i++ {
switch {
case i < 16:
f, g = F(b, c, d), i
case i < 32:
f, g = G(b, c, d), (5 * i + 1) % 16
case i < 48:
f, g = H(b, c, d), (3 * i + 5) % 16
default:
f, g = I(b, c, d), (7 * i) % 16
}
temp := d
d = c
c = b
b = leftrotate((a + f + k[i] + w[g]), r[i]) + b
a = temp
}
ra, rb, rc, rd = ra + a, rb + b, rc + c, rd + d
}
func leftrotate(a, n uint32) uint32 {
return (a >> (32 - n) | a << n)
}
如此循环完所有的512 bit block便可以计算出对应的MD5散列值,就是\(ra, rb, rc, rd\)在内存中的按小端序排列的值,写一个格式化函数即可实现。
参考文献
- MD5 - 维基百科,自由的百科全书 (wikipedia.org)
- 密码学 (豆瓣) (douban.com)
- RFC 1321: The MD5 Message-Digest Algorithm (rfc-editor.org)
- 字节序 - 维基百科,自由的百科全书 (wikipedia.org)