SHA-1 哈希算法的填充与迭代过程详解
我将为您详细讲解 SHA-1 哈希算法的填充与迭代过程。这两个步骤是将任意长度输入转换为固定长度摘要的核心预处理和主框架。
题目描述
SHA-1 算法接收一个最大长度小于 2^64 比特的输入消息,并输出一个 160 比特的哈希值。其核心过程包括填充、消息分块、迭代压缩。我们将重点聚焦在填充规则和主迭代循环上,解释其如何确保算法的确定性和抗碰撞性。
解题过程(知识讲解)
第一步:理解 SHA-1 算法的整体流程
SHA-1 是一种 Merkle-Damgård 结构的哈希函数,其工作流程可分为三个阶段:
- 预处理:包括填充和消息分块。
- 初始化:设置 5 个 32 位(160 位总计)的初始哈希值
H0, H1, H2, H3, H4。 - 迭代压缩:对每个消息块,与当前哈希值一起,通过压缩函数进行计算,更新哈希值。
最终,最后一轮迭代输出的哈希值就是最终摘要。
第二步:填充过程详解
填充的目的是使消息长度恰好满足分块要求。SHA-1 以 512 比特为一个块进行处理。
填充规则如下:
- 在消息末尾添加比特“1”。这相当于在字节流末尾添加一个值为
0x80的字节。 - 在“1”后面添加 k 个“0”比特。其中 k 是满足下式的最小非负整数:
(原始消息长度 + 1 + k) ≡ 448 (mod 512)
换句话说,添加“0”是为了让“填充后的总长度”在对 512 取模后等于 448。因为最后还要附加 64 比特的长度,所以 512 - 64 = 448。 - 在填充的最后,附加一个 64 比特的大端序整数,该整数表示原始消息的比特长度。
举例说明:
假设原始消息是 “abc” 的 ASCII 表示(0x61 0x62 0x63),共 24 比特。
- 原始消息长度 L = 24。
- 首先在消息后添加 “1”:
0x61 0x62 0x63 0x80。现在长度为 32 比特。 - 现在需要填充“0”,使得总长度对 512 取模等于 448。计算:当前 32 比特,32 ≡ 32 (mod 512)。我们需要 448,所以需要填充 448 - 32 = 416 个“0”比特。也就是添加 52 个
0x00字节。 - 最后,附加 64 比特的原始长度 24。24 的 16 进制是
0x18。在大端序下,是00 00 00 00 00 00 00 18。
所以,最终填充后的消息是一个 512 比特的块(24 + 1 + 416 + 64 = 512 比特)。如果没有这一步,不同长度的消息可能产生相同分块,导致碰撞。
第三步:迭代压缩过程详解
填充后的消息被分割成 N 个 512 比特的块:M1, M2, ..., MN。迭代过程从一个固定的初始向量开始。
-
初始化:
设置 5 个 32 位寄存器 A, B, C, D, E 的初始值:
A = 0x67452301
B = 0xEFCDAB89
C = 0x98BADCFE
D = 0x10325476
E = 0xC3D2E1F0
这组值称为初始链变量(Initial Hash Value)。 -
主循环:
对每个 512 比特的消息块Mi进行如下处理:
a. 消息扩展:将 512 比特的Mi分成 16 个 32 比特字W[0] ... W[15]。然后通过递推公式扩展成 80 个字W[0] ... W[79]。这是为后续 80 轮运算准备数据。
b. 压缩函数:将当前的 A, B, C, D, E 值复制到临时变量 a, b, c, d, e 中。然后进行 80 轮运算,每轮 t 使用一个逻辑函数f_t和一个常数K_t。
每轮运算的逻辑如下:
TEMP = (a 循环左移 5) + f_t(b, c, d) + e + K_t + W[t] e = d d = c c = b 循环左移 30 b = a a = TEMP
其中,f_t和K_t每 20 轮变化一次:
- 轮 0-19:f_t = (b AND c) OR ((NOT b) AND d),K_t = 0x5A827999
- 轮 20-39:f_t = b XOR c XOR d,K_t = 0x6ED9EBA1
- 轮 40-59:f_t = (b AND c) OR (b AND d) OR (c AND d),K_t = 0x8F1BBCDC
- 轮 60-79:f_t = b XOR c XOR d,K_t = 0xCA62C1D6
c. 结果累加:80 轮之后,将临时结果与初始链变量相加:
A_new = A + a
B_new = B + b
C_new = C + c
D_new = D + d
E_new = E + e
这组新的 A, B, C, D, E 将成为处理下一个消息块Mi+1时的初始链变量。 -
输出:
处理完所有 N 个消息块后,最终的 160 位输出就是连接 A, B, C, D, E 五个 32 位寄存器(以大端序表示)得到的 160 比特哈希值。
核心要点总结
- 填充 确保了输入长度标准化,是哈希函数能够处理任意长度输入并包含长度信息的基石。
- 迭代压缩 构成了 Merkle-Damgård 结构的主体,其抗碰撞安全性依赖于压缩函数的设计。SHA-1 的压缩函数由 80 轮非线性运算构成,结合了与、或、非、异或、模 2^32 加法和循环移位等多种操作,提供了良好的扩散和混淆。
- 链式结构 使得任何输入比特的改变,都会通过迭代传播,影响最终的哈希值,这提供了雪崩效应。
希望这个逐步的讲解能帮助你清晰理解 SHA-1 算法如何从任意输入一步步生成固定输出的摘要。尽管 SHA-1 因安全性原因已不再推荐用于新的密码学应用,但其结构和设计思路仍是学习哈希算法的经典范例。