科普

SHA-1 哈希算法详解:工作原理、安全漏洞与现代替代方案

深入解析 SHA-1 安全哈希算法:从设计历史、核心特征到详细的工作原理(如消息填充、消息扩展与 80 步主循环运算)。同时剖析其著名的 SHAttered 碰撞攻击与安全性崩溃,阐明为什么它不再适用于密码加密和签名,并介绍现代安全的 SHA-256 和 SHA-3 替代方案。

什么是 SHA-1?

SHA-1(Secure Hash Algorithm 1,安全哈希算法 1) 是一种由美国国家安全局(NSA)设计、并由美国国家标准与技术研究院(NIST)于 1995 年作为联邦信息处理标准(FIPS PUB 180-1)发布的密码学哈希(Hash)算法。它是早期 SHA-0 算法的修正版本,主要修复了一个未公开的安全性弱点。

与所有哈希散列函数类似,SHA-1 的核心任务是:将任意长度的输入数据(无论是文本、图片还是大文件),经过一系列复杂的位运算、逻辑函数与加法混合,转化为一个固定长度的 160 位(20 字节)输出值(即“数据指纹”),通常以 40 个十六进制字符的形式表示。

例如,将明文 "hello" 输入 SHA-1 算法,得到的哈希值(40个十六进制字符)如下:

aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

无论输入内容是短短一个字母,还是一整套百科全书,其 SHA-1 的输出长度永远固定为 40 个字符。

在线工具推荐:如果您需要即时生成文本或文件的 SHA-1 哈希值,或者进行签名校验,可以使用我们提供的 SHA-1 在线生成工具,支持文本、文件一键处理,安全快捷。


SHA-1 与 MD5 的对比

SHA-1 在设计上深受 MD5 算法的影响,它们有着相似的整体结构(Merkle-Damgård 结构),但 SHA-1 的设计目标是提供比 MD5 更高的安全级别。

以下是它们的核心区别对比:

特征维度MD5 (Message-Digest 5)SHA-1 (Secure Hash Algorithm 1)
设计者Ronald Rivest (RSA 发明者之一)美国国家安全局 (NSA)
发布年份1991 年1995 年
输出哈希长度128 位 (16 字节,32 个十六进制字符)160 位 (20 字节,40 个十六进制字符)
分组大小512 位 (64 字节)512 位 (64 字节)
运算步数64 步 (4 轮 ×\times 16 步)80 步 (4 轮 ×\times 20 步)
内部状态缓冲区4 个 32 位寄存器 (A, B, C, D)5 个 32 位寄存器 (A, B, C, D, E)
碰撞安全性彻底崩溃(可在毫秒级制造碰撞)彻底崩溃(2017年证实首个真实碰撞,2020年实现实用碰撞)
计算速度极快较快(略慢于 MD5)

虽然 SHA-1 拥有比 MD5 更长的输出长度(提供更大的抗碰撞空间:理论上暴力破解需要 2802^{80} 次运算,而 MD5 为 2642^{64} 次),但在当今时代,两者在密码学安全意义上均已被宣告“破产”。


SHA-1 的四大核心特征

作为一个经典的密码学散列算法,SHA-1 具有以下四个决定其特性的核心特征:

1. 确定性(Deterministic)

对于给定的输入数据,无论在何种计算机架构下、执行多少次 SHA-1 运算,得到的哈希值都是完全相同的。这是哈希校验可行的基础。

2. 雪崩效应(Avalanche Effect)

输入数据的极微小变化,都会导致输出的哈希值产生翻天覆地的彻底改变。例如:

  • 输入 SHA-1 \rightarrow c571b86549e49bf223cf648388c46288c2241b5a
  • 输入 sha-1 \rightarrow b710a299818728a4e4cb4851fca1607ae797a07b

仅仅是大小写的微调,输出值中没有半点相似的痕迹。

3. 单向性(One-way / 不可逆)

SHA-1 是一种单向函数。从明文计算出 SHA-1 哈希极其迅速,但几乎不可能从一个已知的 40 个十六进制字符表示的哈希值逆向推导回原始输入。

4. 冲突防范(抗碰撞性 - Collision Resistance)

在设计之初,SHA-1 具备极高的抗碰撞性。也就是说,要找到两个内容不同但 SHA-1 哈希值完全相同的输入,在理论上是极其困难的。然而这一特性已被现代密码学研究所打破。


SHA-1 的工作原理步骤

SHA-1 算法的输入以 512 位(64 字节) 的数据块为基本单位进行处理。如果输入数据的长度不足,需要进行填充。其具体计算过程可以划分为以下几个步骤:

graph TD
    A[原始输入数据] --> B[步骤 1: 填充数据至 512 位的倍数减 64 位]
    B --> C[步骤 2: 追加 64 位的原始数据长度]
    C --> D[步骤 3: 初始化 5 个 32 位状态缓冲区 A, B, C, D, E]
    D --> E[步骤 4: 将 16 字分组扩展为 80 字消息日程表 W]
    E --> F[步骤 5: 循环执行 4 轮共 80 步的主压缩函数运算]
    F --> G[步骤 6: 累加缓冲区状态并输出最终 160 位哈希值]

步骤 1:填充消息(Padding)

为了使输入数据长度满足特定要求,必须对其进行填充。填充的规则是:使数据的总位长模 512 的余数等于 448。也就是说,长度满足 L448(mod512)L \equiv 448 \pmod{512}

  • 即使数据的原始长度刚好满足上述条件,也必须进行填充。
  • 填充的具体操作是在消息末尾先附加一个二进制的 "1",接着全部填充 "0",直到长度满足要求。
  • 填充长度的范围通常在 1 到 512 位之间。

步骤 2:追加原始消息长度

在填充好的消息后,追加一个 64 位(8 字节) 的二进制数值,表示原始消息的位长度(在填充前)。

  • 追加这 64 位之后,整个消息的位长度正好是 512 位的整数倍。
  • 分块处理时,消息会被切分成若干个 512 位的分组(Y0,Y1,,YN1Y_0, Y_1, \dots, Y_{N-1})。

步骤 3:初始化 H 缓冲区

算法内部维护了 5 个 32 位的状态寄存器(称为 A, B, C, D, E),其初始值(大端序表示)是一组固定的常数:

  • H0=0x67452301H_0 = \text{0x67452301}
  • H1=0xEFCDAB89H_1 = \text{0xEFCDAB89}
  • H2=0x98BADCFEH_2 = \text{0x98BADCFE}
  • H3=0x10325476H_3 = \text{0x10325476}
  • H4=0xC3D2E1F0H_4 = \text{0xC3D2E1F0}

这 5 个寄存器的初始状态拼接起来,就是没有输入任何数据时的哈希中间值。

步骤 4:消息扩展(Message Expansion)

这是 SHA-1 区别于 MD5 的核心步骤,也是其更加抗冲突的关键所在。 对于每个 512 位的消息分组,它首先被拆分为 16 个 32 位的字,记作 W0,W1,,W15W_0, W_1, \dots, W_{15}。 接下来,算法通过以下递推公式,将这 16 个字扩展为 80 个字W16W_{16}W79W_{79}): Wt=(Wt3Wt8Wt14Wt16)ROTL1(对于 16t79)W_t = (W_{t-3} \oplus W_{t-8} \oplus W_{t-14} \oplus W_{t-16}) \operatorname{ROTL} 1 \quad (\text{对于 } 16 \le t \le 79) 其中,ROTL1\operatorname{ROTL} 1 表示将 32 位无符号整数循环左移 1 位。

历史注记:早期被废弃的 SHA-0 算法在该步骤中没有进行“循环左移 1 位”的运算。正是因为少了这 1 位的位移,导致 SHA-0 存在严重的代数弱点。NSA 在发现后紧急发布了增加了这一移位操作的 SHA-1。

步骤 5:主循环运算(80 步)

SHA-1 的主压缩函数共包含 80 步运算,分为 4 轮,每轮 20 步。 每一轮使用当前缓冲区的寄存器数值(A, B, C, D, E)、消息字 WtW_t、一个常数 KtK_t,并通过一个非线性逻辑函数 FtF_t 进行迭代混淆。

每一轮所使用的逻辑函数 FtF_t 和常数 KtK_t 如下表所示:

轮次 (Step tt)逻辑函数 Ft(B,C,D)F_t(B, C, D)常数 KtK_t (十六进制)
第一轮 (0t190 \le t \le 19)(BC)(¬BD)(B \wedge C) \vee (\neg B \wedge D)0x5A827999
第二轮 (20t3920 \le t \le 39)BCDB \oplus C \oplus D0x6ED9EBA1
第三轮 (40t5940 \le t \le 59)(BC)(BD)(CD)(B \wedge C) \vee (B \wedge D) \vee (C \wedge D)0x8F1BBCDC
第四轮 (60t7960 \le t \le 79)BCDB \oplus C \oplus D0xCA62C1D6

每一步的运算公式如下: TEMP=(AROTL5)+Ft(B,C,D)+E+Wt+KtTEMP = (A \operatorname{ROTL} 5) + F_t(B, C, D) + E + W_t + K_t E=DE = D D=CD = C C=BROTL30C = B \operatorname{ROTL} 30 B=AB = A A=TEMPA = TEMP (注:上述所有加法均在模 2322^{32} 下进行)

步骤 6:累加输出

处理完一个 512 位分组的所有 80 步后,将当前的 A, B, C, D, E 值分别加到初始的 H0,H1,H2,H3,H4H_0, H_1, H_2, H_3, H_4 缓冲区上。 当所有的分组都处理完毕后,将 H0,H1,H2,H3,H4H_0, H_1, H_2, H_3, H_4 这五个 32 位寄存器的最终数值按顺序拼接起来,就得到了 160 位的二进制哈希值,即 40 位十六进制字符串。


SHA-1 安全性的全面崩溃

随着计算机硬件技术(尤其是 GPU、ASIC)的发展和密码分析学的突破,SHA-1 的安全性经历了一步步崩溃的历程。

timeline
    title SHA-1 算法安全性瓦解史
    1995 : SHA-1 正式发布 : 修复了 SHA-0 的未知漏洞
    2005 : 理论性碰撞突破 : 王小云教授团队提出复杂度为 2^69 的碰撞攻击理论,远低于理论暴力破译的 2^80
    2015 : 实用性更近一步 : 密码学家利用 GPU 成功实现 SHA-1 的自由起始碰撞攻击
    2017 : 首个真实碰撞 (SHAttered) : CWI 与 Google 成功制造出两个 SHA-1 值完全相同的不同 PDF 文件(耗费 110 个 GPU 运行一年)
    2020 : 选择前缀碰撞 (Chosen-prefix) : 研究人员实现了成本仅为 4.5 万美元的选择前缀碰撞,SHA-1 彻底被拉下神坛

1. 理论碰撞的破晓(2005年)

中国密码学家王小云教授领导的团队在 2005 年证明,可以在 2692^{69} 次计算内找到 SHA-1 的碰撞,这远低于理论上的 2802^{80} 次暴力破解限制。这一发现震惊了密码学界,正式拉开了 SHA-1 退役的序幕。

2. SHAttered 攻击——首个真实物理碰撞(2017年)

2017 年 2 月,荷兰数学与计算机科学研究学会(CWI)和谷歌(Google)联合宣布了名为 SHAttered 的研究成果。 他们成功生成了两个内容完全不同、但 SHA-1 值完全相同的 PDF 文件

  • 该项目的计算量约为 922 京(9.22×10189.22 \times 10^{18})次 SHA-1 运算。
  • 相当于单个 CPU 连续计算 6500 年,或者单个 GPU 连续计算 110 年。
  • 这一成果标志着 SHA-1 的抗碰撞性在物理层面被彻底攻破。

3. 选择前缀碰撞攻击的实现(2020年)

2020 年,研究人员 Gaëtan Leurent 和 Thomas Peyrin 进一步将 SHA-1 的攻击推进到了**“选择前缀碰撞”(Chosen-prefix Collision)阶段。 在此之前,制造碰撞的两个文件内容必须是由计算程序生成的无意义字符;而“选择前缀碰撞”允许攻击者自定义两个文件的开头部分**(如:一个是“授权付款 100 元”,另一个是“授权付款 100 万元”),然后通过算法在末尾添加一些碰撞填充字节,使其 SHA-1 相同。

  • 此攻击的计算成本降至仅约 4.5 万美元(通过云计算租用 GPU 算力)。
  • 这意味着任何资金充足的黑客或组织都可以轻松伪造带有 SHA-1 数字签名的数字证书或软件安装包。

为什么 SHA-1 不能再用于现代安全?

由于上述安全漏洞,在现代应用中继续使用 SHA-1 会带来严重的风险:

1. 证书与 SSL/TLS 被彻底废弃

在 2017 年之前,许多网站的 SSL/TLS 证书使用 SHA-1 算法进行签名。黑客如果通过选择前缀碰撞伪造出具有相同 SHA-1 签名的证书,就可以进行完美的中间人攻击(MITM),窃取用户的敏感通信数据。 因此,自 2017 年起,各大主流浏览器(Chrome、Firefox、Safari、Edge)均已彻底停止对 SHA-1 证书的支持,将其标记为“不安全”。

2. 严禁用于密码哈希存储

与 MD5 一样,SHA-1 的计算速度非常快。这意味着如果数据库泄露,黑客可以使用现代显卡进行极其高速的离线爆破,或者通过现成的**彩虹表(Rainbow Tables)**瞬间反查出用户的明文密码。

安全提醒:如果你的系统目前还在使用类似于 SHA-1(password + salt) 的方式存储密码,请立刻进行系统重构!

3. 软件数字签名失效

在过去,软件开发商(如微软)使用 SHA-1 对系统驱动程序和更新包进行数字签名,以证明软件未被篡改。但在 2020 年微软也正式弃用了 SHA-1 签名,转而使用 SHA-2,因为黑客可以通过碰撞向看似正规的驱动程序中注入木马恶意代码,而维持签名合法。


现代密码学替代方案

在需要确保机密性、完整性和真实性的安全场景下,必须用以下现代散列算法替换 SHA-1:

1. SHA-2 家族(推荐行业标准)

SHA-2 是目前最主流的哈希算法家族,同样由美国国家安全局(NSA)设计,至今未发现任何有效的密码学攻击手段。

  • SHA-256:最常用的标准,输出 256 位哈希,适用于绝大多数网络通信、HTTPS 证书与区块链(如比特币)。
  • SHA-512:输出 512 位哈希,在 64 位操作系统上运行效率甚至高于 SHA-256,适合处理极大型数据。

2. SHA-3 家族(下一代防御)

为了防止 SHA-2 突遭破解,NIST 于 2015 年正式发布了 SHA-3 算法(基于 Keccak 算法)。它在内部结构上与传统的 MD5、SHA-1、SHA-2 截然不同(采用海绵结构),提供极高的防御冗余,常作为超高安全规格项目的替代方案。

3. bcrypt 与 Argon2(专用于密码存储)

散列密码不能追求“快速”,而必须追求“慢速”。

  • Argon2 是目前最先进的密码散列算法,支持自定义内存和 CPU 消耗,彻底防范 GPU 和 ASIC 硬件矿机式的暴力破解。
  • bcrypt 是支持自适应算力调节的经典方案,同样被广泛推荐。

SHA-1 在当今时代的非安全合理应用

尽管在密码学安全上已经退役,SHA-1 由于其运行速度快、占用空间较小和强大的兼容性,在完全不涉及对抗恶意攻击的非安全领域仍有广泛应用。

1. Git 版本控制系统

著名的分布式版本控制工具 Git 在历史上完全依赖 SHA-1 来计算提交(Commit)、树(Tree)和文件(Blob)的唯一标识符。

  • Git 为什么不怕碰撞?:Git 用 SHA-1 的初衷是为了标识和查重,而不是防止恶意篡改。
  • 现代升级:尽管如此,为了防范恶意的仓库破坏(如在提交中恶意注入碰撞对象),Git 目前已经实现了向 SHA-256 的平滑过渡,并在过渡期间引入了碰撞检测机制(sha1collisiondetection),一旦检测到类似 SHAttered 攻击特征的文件,会立即拒绝并警告。

2. 基础数据校验和(Checksum)

在网络下载或内部数据同步中,如果只需要防止由于物理网络故障、磁盘损坏等“非蓄意”因素导致的数据传输错误,SHA-1 仍然是一项高效的选择。

3. 遗留系统兼容

对于大量已经部署完毕、且短时间内无法重构的遗留工业控制系统、旧式硬件设备,SHA-1 作为数据通信指纹在受信任的隔离局域网内依然发挥着余热。


总结

SHA-1 是密码学发展史上的一座重要里程碑。它承接了 MD5 的辉煌,也经历了被时代算力击碎的痛苦。

  • 对于涉及安全性验证、用户登录、数字签名与敏感数据保障的场景:请立刻全面升级到 SHA-256 或 SHA-3。
  • 对于日常的文本快速散列、老旧系统调试或非安全场景下的文件指纹提取:我们的 SHA-1 在线生成工具 仍然是您最顺手、便捷的多用途工具。