密码学基础回顾:对称加密、非对称加密、哈希

聊索引加密之前,得先把密码学基本功打牢。我这些年做安全项目,见过太多人把AES当万能药,或者拿RSA直接加密大文件,结果性能崩得一塌糊涂。说白了,每种算法都有自己的脾气和适用场景。这一节咱们就捋一捋三种最常见的密码学原语——对称加密、非对称加密、哈希函数,我尽量结合踩过的坑来说。

对称加密:AES 是现在的工业标准

对称加密,意思就是加密和解密用同一把钥匙。你想象一下,你和朋友共用一把锁。AES(Advanced Encryption Standard)是目前最主流的对称加密算法,没有之一。为什么?因为它快,而且经过这么多年破解,依然坚挺。

我个人习惯用 AES-256,虽然多费一点点计算量,但安全裕度大。你想想看,密钥长度每增加1位,暴力破解的难度翻一倍。AES-128 现在也够安全,但量子计算要真来了,256 位能多撑一阵子。

AES 有三种密钥长度:128、192、256。对应的轮数不一样:

算法密钥长度轮数安全级别
AES-128128 位10 轮够用,但别用在前瞻性系统
AES-192192 位12 轮中等安全,较少直接用
AES-256256 位14 轮推荐,能抗较长时期

提到 AES,就绕不过模式。ECB 模式千万别用——它会把同样的明文块加密成相同的密文块,你想想看,一张图片的轮廓都能透出来。我就在项目里见过有人图省事直接用 ECB,结果被安全审计打了个大红叉。CBC 模式稍微好一点,但需要 IV 随机化,而且没有认证,容易遭 padding oracle 攻击。我个人现在基本只用 GCM 模式——它在 CBC 基础上加了认证标签,既能防篡改,又能检测伪造。

说个大白话: AES-256-GCM 就是当前对称加密的黄金组合。加密、认证一次搞定,不用再额外搞 HMAC。

来看个简单的 Python 示例,用 PyCryptodome 实现 AES-GCM 加密:

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

key = get_random_bytes(32)  # AES-256
cipher = AES.new(key, AES.MODE_GCM)
plaintext = b"Hello, Index Encryption!"
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
# 存储 cipher.nonce, ciphertext, tag 即可解密

嗯,这里要注意——nonce 和解密时的 IV 必须传输给接收方,但不需要保密。解密时:

cipher_dec = AES.new(key, AES.MODE_GCM, nonce=nonce)
data = cipher_dec.decrypt_and_verify(ciphertext, tag)

防坑建议:千万不要重复使用同一个 nonce+key 做两次加密,否则认证直接失效。我曾经在测试时就干过这事,排查了好久。

非对称加密:RSA 的取舍

非对称加密就有点意思了——它有两个钥匙,一个公开(公钥),一个自己私藏(私钥)。RSA 是其中最经典的一种,靠的是大整数分解的数学难题。你用它加密,公钥加密的数据只能用私钥解开,反过来也可以用私钥签名、公钥验证。

但是它有个致命的弱点:慢。我印象特别深,有一次用 RSA-4096 加密一个 1KB 的文本,花了差不多 5 毫秒。相比 AES 加密同样数据只要微秒级,差了三个数量级。所以实际项目中,从来不会用 RSA 直接加密大文件。正确的做法是:用 RSA 加密一个临时生成的 AES 密钥,再用 AES 加密真正的数据——也就是混合加密体系。

RSA 密钥长度的选择也得留神。目前 1024 位已经被视为不安全,我见过的厂商最小也要求 2048 位。下面这张表总结了常见长度:

密钥长度安全状态建议
1024 位已破解(普通计算资源可攻破)禁用
2048 位目前安全,但长远看可能不够最低标准
4096 位足够长期安全推荐用于敏感场景
重要警告: RSA 不得用于加密任意长度的数据。PKCS#1 v1.5 模式有 padding oracle 攻击风险,推荐改用 OAEP 填充。我在改造遗留系统时就踩过这个坑——v1.5 的明文能被逐字节猜出来。

一个典型的 RSA 加密(OAEP)代码片段:

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

key = RSA.generate(2048)
cipher_rsa = PKCS1_OAEP.new(key.publickey())
ciphertext = cipher_rsa.encrypt(b"AES key data")

哈希函数:不可逆的指纹

哈希函数跟加密不一样——它没有解密这一说。你把任意长度的数据塞进去,出来固定长度的摘要,而且理论上不可能反向还原。它的核心用途是完整性校验和密码存储。我习惯管它叫数据的指纹。

市面上的哈希算法一大堆,但很多已经不顶用了。MD5 和 SHA-1 已经能人为制造碰撞,说白了就是两个不同的文件可以算出相同的哈希值。我几年前做文件去重,用的 MD5,结果发现两个不同的文档居然哈希一样,差点酿成大错。从那以后,我就只认 SHA-256。

常用的哈希算法对比:

算法摘要长度安全性推荐使用
MD5128 位已破解,碰撞易构造仅用于非安全场景(如非关键的校验)
SHA-1160 位已破解(SHAttered 攻击)禁用
SHA-256256 位目前安全推荐
SHA-3可变新一代安全可选,兼容性稍差

还有一点容易被忽略:哈希函数不是用来“加密”数据的。做密码存储时,别直接用 SHA-256 哈希密码,因为彩虹表分分钟反向查出来。要用加盐的慢哈希算法,比如 bcrypt、scrypt、Argon2。我有个项目就是直接 SHA-256,被安全评审要求全部重写密码存储逻辑,别提多麻烦了。

小提示: 在之前的章节里,我们会用 SHA-256 来提取文档的确定性标识,也会用 HMAC-SHA256 做密钥派生。这些在模糊索引构建中很重要。

三种原语怎么搭配?

好,现在我们把这三种技术放一块儿看。它们不是互相取代的关系,而是各司其职:

  • 对称加密(AES):跑得快,适合加密大量数据。但密钥分发麻烦。
  • 非对称加密(RSA):解决密钥分发问题,还能做数字签名。但慢,只能加密小块数据。
  • 哈希函数(SHA-256):生成不可逆的摘要,用于完整性验证、指纹提取,不是用来藏数据的。

在我做的索引加密系统里,典型流程是这样:用户输入文档 ——> 用 SHA-256 计算文档指纹(用于去重和校验) ——> 用 AES-256-GCM 加密文档内容 ——> 用 RSA 加密随机的 AES 密钥 ——> 最后把加密后的文档和加密后的密钥存到一起。是不是清晰多了?

这张结构图把整个密码学基础的关系理了一遍,你看一眼就明白了。

密码学基础三大原语 密码学基础 对称加密(AES) 哈希函数(SHA-256) 非对称加密(RSA) 密钥管理 CBC/GCM 模式 混合加密 完整性校验 加盐存储 指纹提取 密钥交换 数字签名 OAEP 填充 核心原则 对称加密做数据保护,非对称加密做密钥分发,哈希做完整性验证

好了,这一节的基础就这么多。你记住这三者的分工和配合,后面做索引加密时就能清楚每一步该用什么算法,为什么用。我不会一开始就让你写密码实现,先把这些底层概念嚼透,后面写代码才不会手忙脚乱。