☰
哈希算法原理图解:从MD5到SHA-3,一张图看懂数据摘要的生成流程
2026/10/20 16:05:09 网站建设 项目流程

我第一次认真看哈希算法原理图时,说实话有点懵:满屏的方块、箭头、异或符号、循环移位,完全不知道主干在哪。后来我发现问题不在图,而在看图的顺序。哈希算法这个词,做技术的人都天天见,文件完整性校验、接口签名、密码存储、区块链区块哈希,背后都是它在运行。但真要说清楚它的原理,很多人只会背一句"不可逆的散列函数"。

这篇文章就把常用哈希算法——MD5、SHA-1、SHA-2、SHA-3、SM3、BLAKE2——的原理图拆开讲一遍,告诉你图里哪些是主干,哪些可以暂时忽略,整张图到底应该从左往右看还是从下往上看。适合想搞懂哈希到底怎么运作、又不想啃数学公式和标准文档的人。

1. 为什么一张合适的原理图,比读十篇文字更能讲清哈希算法

哈希算法的标准文档动辄几十页,里面全是轮的次数、常数的取值、位移的位数。大多数工程人员并不需要背这些,只需要理解数据是怎么被"搅"成一段固定长度摘要的。原理图的价值在于把过程压缩成一条主链,让人一眼看到数据从哪进、往哪走、从哪出。

1.1 一张哈希原理图里到底画了什么

多数原理图可以拆成三个层次来看。

第一层是主流程,也就是整张图的大框架:输入数据、填充、分块、迭代、输出。这层决定了哈希函数的整体形态。第二层是压缩函数的内部细节,通常画成一个放大的方框,里面有异或、加法、移位、查表之类的操作。对绝大多数使用者来说,这一层可以暂时不看,它解释的是"具体怎么搅",而不是"整个流程怎么走"。第三层是状态寄存器,通常画成一排小格子,代表计算过程中一直在变化的那几个中间值。

我建议看图的顺序是固定的:先找到输入箭头从哪里进入,再顺着箭头找到填充区,然后看分块之后的循环结构,最后看输出箭头从哪里离开。只要把这条路径走通,整张图的主干就清楚了。

1.2 看图的顺序:先主流程,再轮函数

很多人犯的错误是一上来就盯住轮函数不放,想搞懂每一个异或和移位为什么那样设计。结果看了半小时,连数据从哪个口进、从哪个口出都没找到。我自己最初看SHA-256的图也是这样,盯着一个加号看了很久,最后才意识到那条贯穿全图的长箭头才是真正的主角。

实际操作中,只有当你需要排查安全漏洞、做性能优化或者自己实现算法时,才有必要深入轮函数。日常做接口签名、文件校验、数据完整性检查,理解主流程就足够。原理图里最粗的那条线,通常就是数据从填充到分块再到迭代的主链,沿着它走一遍,胜过读十段文字描述。

2. 哈希的三个"反直觉"特性,是所有原理图的前提

在看图之前,有件事必须想明白:哈希函数到底在做什么。它做的事情可以概括成一句话——把任意长度的输入,变成一段固定长度的、看起来和输入没有任何规律的输出。这句话里有三个点都很反直觉,而原理图上所有方块和箭头,都是为了实现这三个点。

2.1 特性一:输出长度固定,输入大小和输出长度没关系

不管输入是一个字节,还是一个1GB的文件,SHA-256的输出永远是256位,也就是32个字节,写成十六进制是64个字符。MD5输出永远是128位,16个字节。SHA-1输出永远是160位,20个字节。原理图上处理大文件时,数据被切成很多块,一块一块地处理,但每一块都在更新同一组状态值,最终输出的只是这组状态值的最终快照。

用生活里的例子类比:就像一个寄存处,你存一张纸条也好,存一箱书也好,最后给你的取件码长度都一样。取件码不能代表包裹的大小,只代表"有这么一件东西存在过"。

2.2 特性二:雪崩效应,改一个比特输出就完全变样

哈希函数还有个奇怪的性质:输入只改动一个比特,输出就会面目全非,大约一半的输出位会发生变化。这就是常说的雪崩效应。原理图里的压缩函数做的大量移位、异或、加法操作,本质上就是在做两件事:扩散和混淆。扩散是把输入的影响铺开到整个状态,混淆是让输出和输入之间的关系变得极其复杂。

你可以想象一本书,只改了其中一个标点符号,但整本书的指纹却完全变了。这也是哈希能做完整性校验的根本原因:只要数据被篡改,哪怕只有一丁点,算出来的摘要就和原来的对不上。

2.3 特性三:单向不可逆,哈希绝不是加密

这是新手最容易混淆的地方。加密是可逆的,你有密钥就可以把密文还原成明文。哈希是不可逆的,从摘要反推原始输入基本不可能。原理图上为什么画得那么复杂?因为每一步压缩都在丢失信息,丢掉的信息无法找回,相当于把一本书磨成粉末,再告诉你粉末的重量,但没法从重量还原书的内容。

所以记住一个判断标准:任何声称"可解密的哈希"都是伪概念。哈希不是加密,而是摘要、指纹、消息的浓缩。它适合用来验证"东西有没有变",不适合用来传递机密信息。

3. 从输入到摘要,一张图里的五段主干流程

现在可以正式进入那张图了。大多数传统哈希算法的原理图,主干都可以分成五段:消息填充、分块、状态初始化、逐块迭代压缩、输出截断。下面按数据流动的顺序逐段说。

3.1 第一段:消息填充与长度登记

压缩函数有个硬性要求:每次处理的输入长度必须是固定的,不能多也不能少。比如SHA-256的压缩函数一次处理512位,也就是64字节。原始数据长度几乎不可能正好是64的倍数,所以第一步必须做填充。

填充规则在不同算法里细节不同,但思路一致:先在消息末尾补一个"1",再补一串"0",直到长度差不多够用,最后还要留出固定位置来登记原始消息的比特长度。SHA-256会在末尾留出64位来记录原始长度。这一步看着不起眼,作用却很大。它保证了两个不同长度的消息不会被填成同样的内容,同时这个长度信息也参与了最后的计算,让输出对原始长度敏感。

为什么图里一定会有这一步?因为如果没有填充,最后一块数据不满64字节,压缩函数就无法处理。填充区通常画在整张图的左上角,就是从输入箭头出来的第一个方框。

3.2 第二段:分块与状态初始化

填充完成后,消息被切成一个个大小相同的块。SHA-256每块64字节,SHA-512每块128字节,MD5和SHA-1每块64字节。这些块会按顺序一块接一块地进入后面的循环。注意:处理顺序绝对不能乱,因为后一块的计算依赖前一块的结果,这也是为什么哈希计算很难并行加速的核心原因。

在开始处理第一个块之前,算法还要初始化一组状态寄存器。SHA-256有8个32位的寄存器,初始值是固定的,所有计算都从这组值出发。你可以把这组初始值理解成"搅拌机的初始状态":机器是新的,里面是空的,但运行规则已经设定好。原理图里这组值通常画在循环区左边,标着IV或者Initial Value。

3.3 第三段:逐块压缩,图的"心脏"

这是整张图最重要的部分。每一块消息进入后,都和当前的状态值做一次压缩计算,产生新的状态值,然后下一块接着和新的状态值计算。整个过程像一条链子,环环相扣,所以这种结构被称作"迭代式压缩结构",通常缩写为M-D结构。MD5、SHA-1、SHA-2、SM3用的都是这个骨架。

我可以把一个块的处理过程粗略理解成:"把新到的消息块,和当前状态这盆水放在一起搅拌一轮,得到一盆新的水。"下一块消息再倒进去继续搅。图里通常画成一个大的循环框,框里有几十轮小操作。SHA-256内部会做64轮,SHA-1做80轮,MD5做64轮。轮数越多,搅拌次数越多,雪崩效应通常也越充分。

在这一段,图里常常出现叉号、加号、方框等符号,代表位运算和查表。你不需要知道每个符号的具体含义,只需要抓住一点:每一轮都在把消息块的信息往状态值里"揉",揉得越碎,输出的随机性越好,安全性越高。

3.4 第四段:海绵结构,SHA-3的另一种画法

SHA-3没有沿用M-D结构,而是采用了海绵结构。原理图画起来也完全不同。海绵结构分成两个阶段:吸收阶段和挤出阶段。吸收阶段把消息切成一段一段的,逐段"吸"进一个大状态里,每吸一段就做一次置换运算,把所有消息块都吸收完后,状态里已经包含了全部输入的信息。然后进入挤出阶段,从状态里不断取出数据,形成摘要。

对比一下两种图:M-D结构是从左往右的一条链,像流水线一样一块一块处理;海绵结构更像一个水槽,消息从上方被吸收进去,摘要从另一侧被挤出来,可以按需设定输出长度。这也是为什么SHA-3的输出长度是可变的,而SHA-256固定256位。

3.5 第五段:输出截断与最终摘要

所有消息块处理完之后,状态寄存器里装着最终的结果。这一步做的事很简单:把状态值按顺序拼接起来,变成最终的摘要。SHA-256有8个32位寄存器,拼起来就是256位,输出64个十六进制字符。SHA-512有8个64位寄存器,拼起来就是512位。

有些图在输出端画得很随意,好像摘要可以随便截取。在标准算法里,输出格式是严格定义的,哪位寄存器在前、哪个字节在后都有规定。如果你自己在实现哈希时需要截取或截断,必须参考标准文档,否则做出来的摘要和别人对不上。

这五段走完,一张主流哈希原理图的基本逻辑就清楚了。接下来看看不同算法之间到底差在哪。

4. 主流哈希算法的血统图:同源与分家

把MD5、SHA-1、SHA-2、SHA-3、SM3、BLAKE2摆在一起,你会发现它们的关系像一张家族谱:有的是亲兄弟,有的只是远房亲戚,还有的是半路出家另立门户。

4.1 MD系列与SHA-1、SHA-2:同一个骨架的三次改装

MD4和MD5是这个家族里比较早的成员,上世纪90年代初由一位密码学者设计,输出128位。设计者最初的目标是提供一种快速的摘要算法。后来MD4被发现存在严重缺陷,MD5也由于碰撞攻击被逐步淘汰,但它的结构框架被保留下来。

SHA-0和SHA-1是在MD4框架基础上调整出来的,输出160位,处理逻辑相似。SHA-1在很长一段时间里是安全领域的主力,但后来也产生了碰撞问题。SHA-2家族则是对SHA-1的深度改造,输出扩展到256位和512位,轮运算更复杂,常量表更大。虽然还是M-D结构,但安全余量明显提高。

这三代算法的关系就像同一套房子的三次装修:承重墙没动,还是填充、分块、迭代、输出,但内部管线、门窗细节都换了。所以从原理图上看,它们的主流程几乎一模一样,区别主要在压缩函数内部。

4.2 SHA-3:另起炉灶的海绵路线

到SHA-2时代,加密哈希的结构已经高度统一,都是M-D结构。官方密码标准组织在征集新一代哈希时,偏好的不是继续在M-D上修修补补,而是完全不同的路线。最终胜出的算法采用了海绵结构,也就是前面讲过的吸收和挤出两阶段设计。

SHA-3不是对SHA-2的增强,而是提供了一个全新的备份方案。如果哪天SHA-2被发现重大问题,整个行业可以迁移到SHA-3。它的内部状态达到1600位,远大于SHA-256的256位,结构上天然抵抗某些针对M-D链式结构的攻击。从图上区分两种算法很简单:看到链式循环框的是M-D家族,看到海绵状吸收挤出结构的是SHA-3家族。

4.3 SM3与BLAKE2:两种值得关注的特殊选手

SM3是国密标准里的哈希算法,输出256位,整体结构非常接近SHA-256,同样采用M-D骨架,消息块同样是512位,内部也是64轮。区别主要体现在消息扩展方式和压缩函数的具体运算上。由于结构相近,你完全可以用同一张主流程去理解它,只需注意轮函数实现不同。

BLAKE2走的是另一条路线,它基于ChaCha的置换思想设计,目标是速度和安全性兼得。它的输出长度可以灵活配置,在性能敏感的场景里表现更好。原理图上依然是填充、分块、迭代、输出的框架,只是内部置换运算换成了一套更轻量的规则。

4.4 常用算法参数速查表

算法输出长度(位)内部结构消息块大小(位)当前状态常见用途
MD5128M-D链式512碰撞已被构造,不推荐安全用途旧系统兼容、非对抗场景
SHA-1160M-D链式512碰撞已被构造,正在淘汰版本管理指纹等
SHA-256256M-D链式512当前认为安全文件校验、签名、区块链
SHA-512512M-D链式1024当前认为安全数据完整性校验
SHA-3可变海绵结构可变当前认为安全新系统可选用的备份方案
SM3256M-D链式512当前认为安全国密合规场景
BLAKE2可变类ChaCha置换可变当前认为安全性能敏感场景

这张表里最需要记住的是输出长度背后的安全含义。对抗碰撞攻击时,安全强度大约只有输出长度的一半。也就是说,128位的MD5,抗碰撞强度只有约2的64次方,这在当前算力下已经不够看了。这也是为什么MD5和SHA-1在安全场景里必须退场。

5. 原理图画不出来的三种攻击,以及它们的现实版本

原理图画的是"算法怎么算",但不会告诉你"算法会被怎么打"。这部分的现实版本同样重要,尤其是当你打算在实际系统里选用哈希算法时。

5.1 生日攻击:为什么128位摘要也不够用

如果不做任何攻击,哈希看起来无懈可击:输入一变,输出就变,想猜中某个摘要对应的输入,平均要试2的256次方次,对于SHA-256来说是个天文数字。但攻击者往往不针对"特定摘要"下手,而是找"任意碰撞"——任何两个不同输入,只要摘要相同就行。

这里有个著名的生日悖论:在一个房间里只要有23个人,就有超过一半的概率出现两个生日相同的人。应用到哈希上,对于n位的摘要,找任意碰撞大约只需要试2的n/2次方次,而不是2的n次方次。所以128位的MD5,抗碰撞强度只剩2的64次方。在这个量级下,大规模算力是可以尝试的,现实中确实已经有针对MD5和SHA-1的碰撞攻击出现。

这条结论直接对应原理图上的"输出长度":输出越短,抗碰撞越弱。选型时不能只看摘要长度顺眼,而要问一句"我需要抵抗碰撞攻击吗"。

5.2 长度扩展攻击:M-D结构的结构性问题

M-D结构有一个容易被忽视的弱点。攻击者如果知道某条消息m的摘要H(m),并且知道m的长度,那么即使完全不知道m的内容,也能构造出一条更长的消息m',使得H(m')可以被算出来,而不需要知道m。这就是长度扩展攻击。

原因是M-D结构把消息一块接一块地处理,最终的状态值就是前面所有块处理完的结果。攻击者不需要知道前面的内容,只需要把这个最终状态当作"起点",继续往后面添加新的消息块就行。这就像一个流水线,攻击者中途接手,继续往传送带上放零件,照样能推算出最终成品。

这个攻击对实际应用有什么影响?影响最大的是"把密钥拼在消息前面再哈希"的认证方案。如果你用H(key + message)来当校验码,攻击者可能不需要知道key,就能构造出合法的校验值。正确的做法是使用HMAC,或者改用SHA-3这类不受长度扩展影响的结构。原理图上这一点看不出来,但真出问题的时候,它比碰撞攻击更隐蔽。

5.3 用三个安全属性快速判断算法能不能用

判断一个哈希算法在某个场景里能不能用,不用背论文,盯住三个属性就行。

安全属性含义被攻破的影响
抗碰撞找不到两条不同消息产生相同摘要恶意文件可以伪装成正常文件
抗原像知道摘要也无法还原原始消息隐私信息可能被反推
抗第二原像已知一条消息,找不到另一条消息摘要相同正常消息可以被替换

对于文件校验和数字签名,抗碰撞最重要。对于密码存储,抗原像和抗穷举更重要。对于消息认证,还要防长度扩展攻击,所以通常直接选用HMAC。把这四个问题过一遍,选型基本不会跑偏。

6. 看懂图谱之后的选型建议与常见误用

了解了原理,最后落到实际选择上。不同场景对哈希的要求不太一样,没有一种算法通吃所有地方。

6.1 按场景选哈希的决策参考

使用场景推荐方案理由和注意事项
文件下载完整性校验SHA-256广泛支持,安全强度足够,不容易出错
接口签名、防篡改HMAC-SHA256HMAC能防长度扩展攻击,密钥管理要单独处理
密码存储加盐的慢哈希算法(如bcrypt、argon2等)普通哈希太快,暴力猜密码更容易成功
防篡改日志、区块链场景SHA-256或SHA-3链式累积摘要,抗碰撞要求高
内部数据去重、非对抗场景视性能需求选择如果无安全要求,非加密哈希更快,但不要用于签名

特别注意密码存储这条。很多人以为把密码做一个SHA-256存入数据库就够了,这是最经典的误用。哈希速度太快,攻击者拿到数据库后可以每秒试上亿次密码,配合彩虹表更是雪上加霜。正确的做法是加随机盐,再选用特意设计得"慢"的密码哈希算法。这一点原理图中看不出来,但实际部署时影响巨大。

6.2 三个最常见的误用,以及踩过的坑

第一,直接用单次哈希做签名。我早年间维护过一个内部系统,最初用MD5对参数拼接做签名,后来意识到字段顺序如果固定不变,碰撞风险就是实打实的隐患,于是把所有签名都改成HMAC-SHA256。这个改动不复杂,但心里踏实很多。

第二,自己把标准算法的输出截断来用。有些人觉得SHA-512输出太长,截一半当SHA-256用。理论上这不是不行,但标准里截断方式有严格定义,自己随意截容易造成不同系统之间摘要对不上,也失去标准实现的安全性保证。能用标准库的就用标准库。

第三,自己设计"增强版"哈希。在标准哈希输出上再套一层哈希,或者自己改轮函数,都属于给自己挖坑。哈希算法的安全性经过了无数密码分析者的检验,个人发明基本无法达到同等保障。记住一句话:不要自己发明密码学。

6.3 一个随时可做的验证实验

如果读完上面这些还是有点晕,我建议你亲自动手做一次验证。随便打开一个终端,对同一段文字分别用MD5、SHA-1、SHA-256计算摘要,观察输出长度。再改一个字母重新算,观察输出的变化幅度。用Python只需要几行代码就能验证:

import hashlib text = b"hello world" print(hashlib.md5(text).hexdigest()) # 32位十六进制 print(hashlib.sha1(text).hexdigest()) # 40位十六进制 print(hashlib.sha256(text).hexdigest()) # 64位十六进制

命令行也可以用:sha256sum 文件名。看看输出是不是64个十六进制字符,然后改文件名内容的一点点,再看输出是不是变得面目全非。这个实验做完,前面讲的所有特性就都变成亲眼所见的东西了。

我个人的习惯是:看哈希原理图时只看主流程,确认数据从哪里进、从哪里出,只有在排查安全细节时才去翻轮函数。当你能够把"填充、分块、迭代、输出"这个链条随口说出来,再遇到MD5、SHA-256还是SM3,都只是同一张图的不同变体罢了。哈希没有想象中那么玄,它就是把一盆水反复搅拌,直到你认不出原来的样子。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询