下载文件之后怎么校验 SHA-256:对不上时先看长度

· 6 分钟阅读 · SHA-256 校验和 MD5 · English

校验和这件事只有两个问题:字节有没有意外改掉,和有没有人主动伪造过这份字节。第一个用任何摘要都能答,第二个答不了。下面按"拿到一个下载文件"的实际顺序走。

先确认这个校验和值不值信

如果文件的下载地址和公布校验和的地址是同一台服务器,那这串哈希只能证明"我这次下载没断、没被中间人改",证明不了"发布方给的就是原版"——能换文件的人也能换那一行哈希。要防篡改,信的是签名:.sig / .asc 由私钥签出,你用另一条渠道拿到公钥(指纹印在官网、在 keybase、在包装好的系统镜像里)来验。GPG 验签、macOS 的 Gatekeeper、Windows 的代码签名都是这个结构;sha256sum 里没有这个结构。

一句话:校验和防意外,签名防恶意。

命令行还是网页

只有几个 GB 的安装镜像、或者你手上就是原文件,命令行更省事:

平台 命令 备注
Windows PowerShell Get-FileHash .\file.iso 默认就是 SHA-256;换算法加 -Algorithm MD5
Windows(自带) certutil -hashfile file.iso SHA256 老办法,输出里带换行,复制时要留神
Linux sha256sum file.iso 还有 sha1sum、md5sum、b2sum
macOS shasum -a 256 file.iso openssl dgst -sha256 file.iso 也行

网页版适合三种情况:手边只有别人给的机器、要一次看全部算法(同一份字节同时出 MD5 和 SHA-256,用来确认"到底是算法不同还是内容不同"),或者要顺手比对贴进来的官方那串。哈希与校验和计算器 的文件上限是 256 MB——不是算法算不动,是把整个文件读进一个标签页的内存里既慢又容易崩;再大的文件就用上面那行命令,摘要完全一样。

对不上时按这个顺序查

  1. 字节数对不对。 摘要吃的是字节不是文字。文本文件多一个结尾换行、Windows 把 \n 写成 \r\n、同一个中文一句 UTF-8 存和 GBK 存,出来的摘要都是另一个。所以先比"参与计算的字节数"和文件大小是否一致。
  2. 算的是不是同一个文件。 下载器留下过 file (1).iso、浏览器把 .exe 改名成 .exe.exe、解压包里有两个同名不同层的文件——这类比编码问题更常见。
  3. 是不是同一个算法。 看长度就能认(下一节表)。32 个字符的那行永远不可能对上一份 SHA-256。
  4. 下载是否完整。 服务器给了 Content-Length 而本地小一点,就是断了;重下一遍再算,别修摘要。

第 3 步和第 4 步在页面上是分开的两句话:贴进官方那串之后,长度对不上就是算法不同,长度对上了却没匹配才是字节不同。这个区分能省掉大量无效怀疑。

各算法的"身份证"就是长度

算法 摘要 十六进制字符 Base64 字符 现在用来做什么
CRC32 / Adler32 32 位 8 — 发现意外损坏:zip、gzip、PNG、以太网帧
MD5 128 位 32 24 老系统的文件指纹、缓存键、去重
SHA-1 160 位 40 28 旧版 git 对象号、遗留签名
SHA-256 256 位 64 44 软件下载页公布的默认值
SHA-384 / SHA-512 384 / 512 位 96 / 128 64 / 88 更保守的裕度,常见于合规要求
HMAC-SHA256 256 位 64 44 带密钥的码:Webhook、支付回调、JWT 签名

拿到 32 个十六进制字符,它要么是 MD5、要么是别的算法的前 32 位;40 个基本就是 SHA-1,64 个是 SHA-256 或 HMAC-SHA256(两者长度相同,区别在有没有密钥)。同一份摘要写成大写也还是同一份,比对前不分大小写。

MD5 和 SHA-1 坏在哪里,还能干什么

“坏"专指碰撞:能造出两份不同字节得到同一个摘要。MD5 在普通笔记本上是几分钟的事;SHA-1 的碰撞 2017 年被公开做出(SHAttered 那对 PDF),后来的 chosen-prefix 碰撞已经能拿去伪造证书链。这跟原像攻击(给定摘要反推出任意一份原文)是两件事,两者至今都遥不可及。

所以:拿 MD5 做缓存键、做本地去重、做"这两个文件一样吗"的快筛,都没问题;拿它证明"这份软件没被动过手脚"就是问题,因为攻击者可以给你一份和正版同摘要的可执行文件。SHA-256 至今没有可用的公开攻击,裕度足够——这就是下载页清一色写 sha256sum 的原因。老站点只公布 MD5 时,照用它,但心里把它的证明力降级为"下载完整”。

CRC32 为什么不算哈希

校验和是线性的:改几个字节、再附四个精心挑的补字节,可以让 CRC32 原样不变。zip 里条目被改而校验和不动就是这个原因;Adler32 是两次模 65521 的求和,对结构化数据更弱。所以 CRC32 在传输协议里非常好用(快、够用),在"证明没人动过"这个用途上一票否决。

同一份字节三种摘要并排看,差别一眼就清楚(下面是 hello 这五个字节,UTF-8,无结尾换行):

CRC32     3610a686
MD5       5d41402abc4b2a76b9719d911017c592
SHA-256   2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

HMAC 不是"把密钥拼上去"

HMAC-SHA256(消息, 密钥) 和 sha256(密钥 + 消息) 都输出 64 个十六进制字符,肉眼看不出区别,但后者会被长度扩展攻击利用:SHA-2 家族是 Merkle–Damgård 结构,知道公开摘要就能在不知道密钥的情况下继续往后算,等于别人可以在你的合法消息后面追加内容并给出"合法"的码。HMAC 的构造是把密钥掩进分组里哈希两遍,没有密钥的人既伪造不了也延长不了。

密钥 secret、消息 hello 时(实测,两者长度一样、值完全不同):
HMAC-SHA256 = 88aab3ede8d3adf94d26ab90d3bafd4a…
SHA-256(secret + hello) = b755d600ebe94c1a3f3d6aebbd7e12bc…

对接 Webhook 时这是最常见的实现错误之一:别自己发明签名方案,按对方文档里的 HMAC 口径来,并比较摘要时避免用短路式的字符串比较(时间差也是一个信道)。

在浏览器里算有什么限制

MD5、CRC32、Adler32 是脚本自己算的(浏览器没有 MD5,JavaScript 写 CRC32 就是三十行查表),任何场合都在。四行 SHA 和两行 HMAC 走 crypto.subtle——浏览器同一套 TLS 原语——而它只在安全上下文里存在。实测:直接把页面从磁盘双击打开(file://)时 isSecureContext 为真,SHA 行正常;用 https:// 访问正常;用局域网 IP 走普通 http:// 打开时会被关掉,这时 MD5/CRC32 那几行照样出,SHA 那几行会写明"不给用"。这不是代码偷懒,是浏览器不给不安全来源开这个口。

隐私口径就一句:文本、选中的文件字节都在你打开的这个标签页里处理,脚本里没有 fetch,输入的内容不会离开这台设备。

相关问题

摘要能还原成文件吗? 不能,它是单向压缩:SHA-256 把任意长度压成 32 字节。但能猜:把常见文件、常见口令各算一遍存表反查,这叫彩虹表 / 字典,对短输入(口令、手机号)非常有效——所以哈希口令必须加盐并用慢哈希(Argon2id、bcrypt)。

文件名、路径要不要一起算? 摘要只覆盖文件内容。要一次锁住一批文件,用清单文件(sha256sum -c checksums.txt 那种),别把文件名混进单条摘要里。

Base64 的校验和能用吗? 能,同一份字节:SHA-256 写成 hex 是 64 字符、写成 Base64 是 44 字符(去掉 = 填充 43)。比对前统一成一种形态即可,Base64 工具 能看字节数。

微信
微信
支付宝
支付宝