2、MD5 算法原理:数据填充、初始化 MD 缓冲区、处理消息块
讲 MD5 算法,很多人一开始就被那些移位、模加弄得头大。我刚开始接触的时候也一样,觉得这玩意儿就是个黑盒子。
其实 MD5 的原理并不复杂,说白了就三个阶段:
- 数据填充 – 把消息补成 512 位的整数倍
- 初始化 MD 缓冲区 – 准备好四个 32 位寄存器
- 处理消息块 – 用四个轮函数来回折腾
下面我一个一个说透。
2.1 数据填充
MD5 以 512 位(64 字节)为一个消息块。如果原始消息长度不是 512 的倍数,就必须填充。填充规则很死板:
- 先补一个 ‘1’ 位(二进制),后面补 ‘0’。
- 填充后的总长度(单位:位)必须满足
长度 ≡ 448 mod 512。也就是说,最后留出 64 位放原始消息长度。 - 最后附加原始消息的长度(以 bit 为单位,用小端序存放),长度用 64 位无符号整数表示。
举个例子,原始消息是 “abc”(3 字节 = 24 位)。填充后变成:
原始二进制: 01100001 01100010 01100011
补 '1': … 01100011 1
补 '0' 到 448 位: … 后面补 423 个 0
附加原始长度 24 位: 最后 64 位为 0x00000000 00000018 (小端)
最终消息块 512 位: [填充后的 448 位] + [64 位长度]
padding_len = (448 - (len * 8) % 512) % 512。注意取模要小心负数情况,我建议用 (448 - (msg_bits & 511)) & 511 来算。
2.2 初始化 MD 缓冲区
MD5 需要四个 32 位寄存器,记为 A、B、C、D。初始值是固定的:
| 寄存器 | 初始值(十六进制) | 说明 |
|---|---|---|
| A | 0x67452301 |
小端序读出来就是 01 23 45 67 |
| B | 0xEFCDAB89 |
小端序 89 AB CD EF |
| C | 0x98BADCFE |
小端序 FE DC BA 98 |
| D | 0x10325476 |
小端序 76 54 32 10 |
这几个值看起来随机,其实是 MD5 作者 Ronald Rivest 特意选的,说白了就是为了让初始状态均匀分布。
我在写自己的 MD5 实现时,曾犯过一个低级的错误:把大小端顺序搞反了。如果你直接拿网络上的十六进制串赋值,而你的平台是大端,一定要做字节序转换。大部分 x86 机器是小端,但 ARM 可以配置,**检查一下你的平台**。
2.3 处理消息块
这是 MD5 的核心,也是最容易晕的地方。我尽量用直白的语言解释。
每个 512 位的消息块会被细分成 16 个 32 位的字(记为 M[0]...M[15])。然后进行四轮运算,每轮 16 步,一共 64 步。
每一步都会更新 A、B、C、D:
// 伪代码,一步操作
FF(a, b, c, d, M[k], s, t) // 第1轮
GG(a, b, c, d, M[k], s, t) // 第2轮
HH(a, b, c, d, M[k], s, t) // 第3轮
II(a, b, c, d, M[k], s, t) // 第4轮
具体每个轮的逻辑函数:
| 轮数 | 函数 | 逻辑表达式 | 非线性特点 |
|---|---|---|---|
| 1 | F | (b & c) | (~b & d) |
位选择,类似IF-THEN-ELSE |
| 2 | G | (b & d) | (c & ~d) |
与 F 对称,但条件位不同 |
| 3 | H | b ^ c ^ d |
异或,强雪崩效应 |
| 4 | I | c ^ (b | ~d) |
与 H 类似但更复杂 |
每步还有一个固定的移位量 s 和一个正弦常数 t。常数来自 floor(2^32 * |sin(i)|),i 从 1 到 64。这些常数事先算好,直接查表。
// 当前状态:a, b, c, d
// M[k] 是第 k 个消息字(0-15),s 是移位位数,t 是常数
temp = (a + F(b, c, d) + M[k] + t) & 0xFFFFFFFF
a = d
d = c
c = b
b = (b + (temp << s | temp >> (32 - s))) & 0xFFFFFFFF
每 16 步之后,A、B、C、D 会累加到上一轮的输出上。最终,整个消息块处理完后得到的 A || B || C || D 就是 128 位的 MD5 摘要。
你问为什么需要这么多轮?其实就是为了让输入的任何微小变化都能扩散到整个输出。我调试过 MD5 的 collisions,发现两对不同的消息能在某一步产生相同的中间状态,但经过 64 步之后雪崩效应会强行把差异放大。不过,嗯,后来王小云教授找到的碰撞攻击证明这个扩散还不够强……那是后话了。
2.4 整体流程一览
用一张图总结 MD5 的完整处理过程:
sin,可能会因为浮点精度出偏差。另外,每一步的地址索引(k 的顺序)在四轮中是不同的,我当年就是抄错了 GG 轮的 k 顺序,导致摘要全错,检查了一整天。
好,MD5 的基本原理就是这样:填充、初始化、处理块。虽然现在 MD5 已经被证明不安全,但理解它的结构对学习其他哈希算法(比如 SHA-2 / SHA-3)非常有帮助。至少你在看密码学论文时,不会对“压缩函数”和“轮迭代”感到陌生。