Skip to content
crypto2026-06-184 分钟阅读

"哈希和加密不是一回事吗?" 这是新手最常问的问题,也是导致密码泄露事件的常见根因。把加密用在哈希的场景(或反过来),后果可能很严重:用可逆加密存密码,攻击者拿到密钥就能解出所有密码;用不可逆哈希存需要恢复的数据,原始信息就永久丢失了。

这篇指南讲清楚两件事:哈希和加密在数学上的根本区别,以及在实际项目里如何正确选择。想亲眼看一下哈希的单向性?用我们的 SHA-256 哈希生成器 算一个摘要试试。

一句话区分

哈希不可逆,加密可逆。

  • 哈希:把任意输入变成固定长度的"指纹"。你无法从指纹还原出原始输入。
  • 加密:用密钥把明文变成密文。有密钥就能还原明文。

这个差别决定了所有其他差异。

哈希:单向函数

哈希函数接收任意大小的输入,输出固定长度的摘要。关键特性:

  • 确定性:相同输入永远产生相同输出
  • 单向:无法从输出反推输入(计算上不可行)
  • 抗碰撞:找不到两个不同输入产生相同输出(理论可能,实际不可行)

常见哈希算法:

# SHA-256(32 字节,64 个十六进制字符)
echo -n "hello" | shasum -a 256
# 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

# MD5(16 字节,32 个十六进制字符,已不安全)
echo -n "hello" | md5
# 5d41402abc4b2a76b9719d911017c592

哈希的典型用途:

  • 文件完整性校验:下载大文件后对比哈希,确认没有损坏或被篡改
  • 内容寻址存储:Git 用对象哈希作为唯一标识
  • 缓存键和去重:相同内容产生相同键,快速查找
  • 密码存储:这里有个大坑,下面单独说

加密:双向函数

加密需要密钥。加密用密钥把明文变成密文,解密用(相同或不同)密钥还原。

两种主要类型:

对称加密:加密和解密用同一个密钥。速度快,适合大量数据。

# AES-GCM(对称加密的工业标准)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

key = AESGCM.generate_key(bit_length=256)
nonce = os.urandom(12)
aesgcm = AESGCM(key)

ciphertext = aesgcm.encrypt(nonce, b"secret message", None)
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
# plaintext == b"secret message"

非对称加密:一对密钥,公钥加密,私钥解密。慢,但解决了密钥分发问题。

# RSA:用公钥加密,私钥解密
openssl rsautl -encrypt -pubin -inkey public.pem -in message.txt -out encrypted.bin
openssl rsautl -decrypt -inkey private.pem -in encrypted.bin -out message.txt

加密的典型用途:

  • 传输层安全:TLS 用混合加密保护 HTTPS 流量
  • 静态数据保护:磁盘加密、数据库字段加密
  • 数字信封:用对称密钥加密数据,再用非对称密钥加密对称密钥
  • 任何需要恢复原始数据的场景

密码存储:最容易踩的坑

密码存储是哈希和加密混淆最频繁的场景。规则很明确:

永远不要加密密码。永远不要用裸哈希存密码。

加密意味着有密钥能还原。一旦密钥泄露(往往就存在同一台服务器上),所有密码立刻明文暴露。

裸 SHA-256 或 MD5 同样不可接受。它们太快,现代 GPU 每秒能尝试几十亿次哈希,简单的字典攻击几小时就能破解大量弱密码。

正确做法是使用专门的密码哈希函数

import bcrypt

# 哈希密码(bcrypt 自动加盐)
hashed = bcrypt.hashpw(b"mypassword", bcrypt.gensalt(rounds=12))
# b'$2b$12$...'

# 验证密码
bcrypt.checkpw(b"mypassword", hashed)  # True
bcrypt.checkpw(b"wrongpassword", hashed)  # False

密码哈希函数(bcrypt、scrypt、argon2id)的关键特性是故意很慢。它们通过"工作因子"参数让单次哈希耗时可控(通常几百毫秒),让暴力破解在计算上不可行。Argon2id 是 2015 年密码哈希竞赛的获胜者,是目前推荐的黄金标准。

何时用哈希 vs 加密

简单的判断:你需要能恢复原始数据吗?

  • 需要 → 加密
  • 不需要 → 哈希

展开成决策表:

| 场景 | 用哈希 | 用加密 | |------|--------|--------| | 用户密码 | ✓(用 bcrypt/argon2) | ✗ | | API 请求签名 | ✓(HMAC) | ✗ | | 文件完整性校验 | ✓ | ✗ | | 缓存键 / 去重 | ✓ | ✗ | | 数据库字段(需查询原值) | ✗ | ✓ | | 通信内容(TLS、消息) | ✗ | ✓ | | 备份归档 | ✗ | ✓ | | 数字签名 | ✓(哈希后签名) | ✓(非对称签名) |

注意最后一行:数字签名同时用到两者。先对原文做哈希压缩,再用非对称密钥对哈希签名,避免对整个文档做昂贵的非对称运算。

性能对比

哈希通常比加密快(虽然现代 CPU 都有硬件加速,差距已经不大):

| 操作 | 现代硬件吞吐量 | |------|----------------| | SHA-256(带 SHA-NI) | ~1.2 GB/s | | AES-256-GCM(带 AES-NI) | ~5 GB/s | | bcrypt(rounds=12) | ~100 次/秒 |

注意 bcrypt 的吞吐量是"次/秒",而不是"GB/秒"。这种慢是有意为之的,对密码哈希反而是优点。

在本地完成敏感操作

无论你要哈希 API 密钥、加密配置文件,还是生成密码哈希,都应该尽量避免把敏感数据发送到陌生服务器。

SHA-256 哈希生成器MD5 哈希生成器bcrypt 哈希AES 加解密RSA 加解密 都完全在浏览器中运行,使用 Web Crypto API 处理输入。你的明文、密钥、密码或哈希结果从不离开你的机器。这在处理 API 凭证、客户数据、专有代码等敏感内容时尤其重要,没有日志、没有留存、没有第三方窥探。

总结:需要恢复原文用加密,不需要用哈希,存密码用 bcrypt 或 argon2,永远不要混淆这三件事。


广告