MD5哈希算法的消息扩展过程详解
MD5(Message-Digest Algorithm 5)是一种广泛使用的密码哈希函数,能将任意长度的消息映射为一个128位(16字节)的哈希值。它的计算过程包含填充、消息扩展、初始化和多轮压缩等步骤。其中,消息扩展是将原始填充后的消息块(每个块512位)扩展为供后续压缩函数使用的64个32位字的过程。这个步骤是MD5计算的核心,它确保了消息的每个位都能充分影响最终的哈希值。
下面,我将为你详细讲解MD5消息扩展过程的每个步骤。
第一步:理解消息分块与总体流程
在MD5开始处理前,输入消息会先被填充,使其长度(以位为单位)满足:
长度 mod 512 = 448
并在末尾附加一个64位的原始消息长度,最终总长度是512位的整数倍。我们将这个填充后的消息按512位(即64字节)分割成若干个“数据块”。
对每一个512位的数据块,MD5会执行以下操作:
- 消息扩展:将这个512位的数据块扩展成64个32位字(记为
W[0],W[1], ...,W[63])。这就是我们本次讲解的重点。 - 压缩函数:使用这64个字,结合四个32位寄存器(A, B, C, D)的当前值,执行64步复杂的位运算,更新寄存器的值。
处理完所有数据块后,四个寄存器的最终连接就是MD5哈希值。
第二步:初始数据块的划分
一个512位的数据块由64个字节组成,记作M[0], M[1], ..., M[63]。
在消息扩展的第一步,我们先将这64个字节,按小端字节序,划分成16个32位字(即初始的16个字)。这里的“小端字节序”意味着一个32位字中,编号最小的字节是权重最低的字节。
具体操作如下:
- 对于块中的前4个字节
M[0],M[1],M[2],M[3]:- 将它们组合成字
W[0],其中M[0]是最低有效字节(LSB),M[3]是最高有效字节(MSB)。 - 公式化表示为:
W[0] = M[0] | (M[1] << 8) | (M[2] << 16) | (M[3] << 24)
- 将它们组合成字
- 依次类推,
M[4]到M[7]组成W[1],直到M[60]到M[63]组成W[15]。
至此,我们得到了W[0]到W[15],共16个32位字。
第三步:生成剩余的48个字(W[16] 到 W[63])
MD5的消息扩展规则非常简单,它是一个线性递归定义。对于j从16到63:
W[j] = W[j-16] + ROTL_left( (W[j-3] ⊕ W[j-8] ⊕ W[j-14] ⊕ W[j-16]), s )
其中:
⊕表示按位异或(XOR)运算。+表示模2^32加法(即结果超过2^32时,丢弃进位)。ROTL_left(X, s)表示将32位数X循环左移s位。在MD5的消息扩展中,位移量s恒为1。- 因此,公式可以简化为:
W[j] = W[j-16] + ROTL_left_1( W[j-3] ⊕ W[j-8] ⊕ W[j-14] ⊕ W[j-16] )
让我们拆解这个公式的含义:
- 异或部分:
W[j-3] ⊕ W[j-8] ⊕ W[j-14] ⊕ W[j-16]- 这是从当前已生成的序列中,选择4个特定位置的字进行XOR。这四个索引的选取(相差3, 5, 6, 16)是经过设计的,目的是让新生成的字与“较远”和“较近”的历史字都发生关联,增加了数据的扩散和混淆。
- 循环左移1位:将第一步XOR的结果循环左移1位。
- 循环移位是引入非线性的简单而有效的方法,左移1位相当于将最高位的比特信息移动到最低位,打破了XOR运算的线性特性,使得每个比特位的影响能被传播到不同位置。
- 模加部分:将
W[j-16]与移位后的结果做模2^32加法。- 这一步是关键的“雪崩”效应增强步骤。它将新计算出的值与“最旧”的一个值(
W[j-16])结合。由于加法运算会产生进位,比特位的改变能向上传播,这比单纯的XOR或移位有更强的扩散性。
- 这一步是关键的“雪崩”效应增强步骤。它将新计算出的值与“最旧”的一个值(
第四步:实例演示与验证
为了让你更清楚,我们来看一个简化例子。假设我们已经通过第二步,得到了:
W[0] = 0x00000001, W[1] = 0x00000002, ..., W[15] = 0x00000010
(这是一个为了演示的简单序列,实际中它们是随机的)
现在来计算W[16]:
j = 16
计算异或:W[13] ⊕ W[8] ⊕ W[2] ⊕ W[0] = 0x0000000e ⊕ 0x00000009 ⊕ 0x00000003 ⊕ 0x00000001
我们一步步算:
0x0e ⊕ 0x09 = 0x07
0x07 ⊕ 0x03 = 0x04
0x04 ⊕ 0x01 = 0x05
所以异或结果为 0x00000005。
循环左移1位:ROTL_left_1(0x00000005) = 0x0000000a
(因为 0x05 的二进制是 0000 0101,左移1位是 0000 1010,即 0x0a)
模2^32加法:W[0] + 0x0000000a = 0x00000001 + 0x0000000a = 0x0000000b
所以,W[16] = 0x0000000b。
你可以看到,尽管我们用了很简单的初始值,但W[16]的值已经与所有参与计算的原始值不同了。这个扩展过程会一直持续到计算出W[63]。
第五步:过程总结与安全意义
MD5的消息扩展过程可以总结为:
- 初始划分:将512位输入块按小端序划分成16个32位字
W[0..15]。 - 递归扩展:通过一个结合了异或、循环左移1位和模加法的线性递归关系,生成剩余的48个字
W[16..63]。其递推公式为:
W[j] = W[j-16] + ROTL_left_1( W[j-3] ⊕ W[j-8] ⊕ W[j-14] ⊕ W[j-16] )
安全意义:
- 消息扩散:这个设计确保原始512位块中的每一个比特,都会影响到扩展后64个字中的绝大部分。任何输入比特的微小改变,都会通过递归和位运算扩散到多个
W[j]中,从而在后续的压缩函数中引发巨大的“雪崩效应”。 - 计算简单高效:整个扩展过程只涉及非常快速的位运算(XOR,移位)和整数加法,这使得MD5在历史上的软件实现中非常高效。
- 现今的弱点:然而,也正是由于这个扩展过程是线性的(递归公式中没有引入非线性S盒或乘性运算),它成为了MD5抗碰撞攻击的致命弱点。攻击者可以利用其线性性质,通过精心构造消息,在扩展后产生特定的、有利于制造碰撞的差分模式。著名的“选择前缀碰撞攻击”就充分利用了这一点。
希望这个循序渐进的讲解,能帮助你透彻理解MD5哈希算法中消息扩展这个基础但关键的步骤。