技术笔记
哈希、HMAC 和加密回答的是三个问题
三者都常被叫做「加密一下」,但输出能不能还原、要不要密钥、密钥证明的是什么,完全不同。这篇把三条路径并排放好,并对应到浏览器里的 API。
作者 Brook/更新于 2026-10-09/约 11 分钟
先问要解决的是哪件事
哈希问的是:这段内容和以前那份是不是同一份。HMAC 问的是:这段内容是不是持有密钥的人认可的。加密问的是:没有密钥的人能不能读到原文。三个问题可以同时出现在一个系统里,但不能用同一种运算代替另外两种。
叫法混在一起时,设计会跟着错。把密码「加密」进数据库,如果实际做的是可逆加密,密钥一漏,全部密码回到明文。如果做的是不带盐的快速哈希,又挡不住预先算好的对照表。先把问题说清楚,再选算法。
三条路径并排放
哈希
没有密钥。同样的输入永远得到同样的摘要。
- 摘要长度固定
- 推不回原文
- 改一个比特,摘要整个变
- SHA-256 适合完整性
HMAC
哈希加上密钥。没有密钥就做不出能通过校验的摘要。
- 证明持有密钥
- 同样推不回原文
- 密钥和消息一起进入
- JWT 的 HS256 就是它
加密
有密钥才能还原。没有密钥时,看到的是密文。
- AES-GCM 同时给完整性
- 需要 IV 或 nonce
- 密钥不能跟密文放一起
- 可以还原出原文
哈希不需要密钥,因此也不能当身份证明。任何人都能对一段公开内容算出同样的 SHA-256。它适合做「文件有没有被改过」:你保存摘要,以后重算一遍对比。它不适合做「是不是某个人同意了这份内容」,因为谁都能算出那个摘要。
HMAC 是带密钥的摘要
HMAC 的构造是把密钥混进哈希的输入,具体是先用密钥导出两个填充块,再和消息一起做两轮哈希。使用时不必自己拼这套步骤,密码库和 Web Crypto 都提供了 HMAC。需要记住的是:校验时要用同一把密钥、同一种哈希,并且比较摘要时不要用会提前返回的普通字符串比较。
密钥短、可预测,HMAC 就弱。用用户名当密钥、用 "secret" 当密钥,和没有密钥差别不大。密钥应该是足够长的随机字节,而不是一句口令。口令要先经过专门的拉伸函数,才能拿去当密钥用,那又是另一条路径。
加密要能还原,所以多了 IV
AES 这类分组加密需要一个初始向量或 nonce,让同一把密钥对同一段明文不会总是得到同一段密文。IV 不用保密,但同一把密钥下不能重复使用同一个 IV,尤其是 CTR 和 GCM。GCM 还会给出认证标签,解密时标签对不上就应该直接失败,不要把解出来的半截明文交给业务。
密文、IV、标签通常要一起保存。少了任何一件都解不回去。把它们用某种文本编码(常常是 Base64)串起来是存储格式的事,不是加密算法的一部分。解码 Base64 只得到字节,离明文还差一次真正的解密。
几种看起来像加密、其实不是的做法
- Base64 不是加密。谁都能解,没有密钥。
- MD5 不适合再当安全摘要。碰撞已经实用化,完整性请用 SHA-256 或更强的。
- 不带盐的哈希不适合存密码。同样的密码得到同样的摘要,可以预先算表。
- 自己发明「把字节异或一个固定数」不是加密方案。固定数一旦出现在前端代码里,就等于没有。
密码存储要的是慢哈希加唯一盐,例如 Argon2、bcrypt 或 scrypt。SHA-256 再快,也不替代这件事。本站的哈希工具用来核对摘要、比对文件,不用来生成可直接入库的密码摘要。
在浏览器里这三件事走 Web Crypto
- 01页面你粘贴的文本或文件先变成字节。文本用 UTF-8,文件用原始字节,不要先做一次多余的 Base64。
- 02Web Cryptodigest、sign 或 encryptSHA-256 走 digest,HMAC 走 sign,AES-GCM 走 encrypt。算法名写在调用里。
- 03结果摘要或密文留在页面上需要复制时再编码成十六进制或 Base64。编码发生在运算之后。
Web Crypto 的接口是异步的,输入是 ArrayBuffer。文本要先用 TextEncoder 变成 UTF-8 字节,否则中文的摘要会和按 UTF-16 去算的实现对不上。这些调用都在本机完成。本站的哈希和 AES 工具走的就是这条路径,输入不会作为请求体发出去。