密码学基础回顾:对称加密、非对称加密、哈希函数、伪随机函数
做可搜索加密,你绕不开这几个老朋友。对称加密、非对称加密、哈希函数、伪随机函数——它们就像工具箱里的螺丝刀、扳手、钳子和卷尺。少一个,活就没法干。
我个人习惯先把它们拉出来理一遍。不是为了复习课本,而是为了打通之后盲索引设计的任督二脉。你想想看,没有伪随机函数,盲化怎么做?没有哈希,索引怎么定址?底子不牢,后面代码写到一半就得懵。
对称加密:又快又稳的看门狗
对称加密,说白了加密和解密用同一把钥匙。就像你家的门锁,锁门和开门的钥匙是一模一样的。我最常用的是 AES-256-GCM,既有认证加密,又能扛住很多侧信道攻击。
举个例子,用 Python 的 cryptography 库加密一段数据:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
key = os.urandom(32) # AES-256 密钥
nonce = os.urandom(12) # GCM 模式 nonce 推荐 12 字节
cipher = Cipher(algorithms.AES(key), modes.GCM(nonce), backend=default_backend())
encryptor = cipher.encryptor()
ciphertext = encryptor.update(b"Hello, searchable world!") + encryptor.finalize()
嗯,这里要注意:nonce 必须唯一,否则 GCM 会直接跪。我曾经在一个项目里没处理好 nonce 轮换,结果同一密钥下出现了两次相同的 nonce,导致认证标签被攻破。幸好是内部测试,不然就搞出大新闻了。
非对称加密:公钥私钥的二人转
非对称加密就不同了。加密用公钥,解密用私钥。公钥可以到处贴,私钥锁在保险柜里。常见的是 RSA 和 ECC(椭圆曲线)。在可搜索加密中,非对称加密常用于密钥交换或构建更复杂的密文策略。
| 特性 | 对称加密 | 非对称加密 |
|---|---|---|
| 密钥数量 | 1 个(共享密钥) | 2 个(公钥+私钥) |
| 计算速度 | 极快(硬件加速) | 慢(百倍量级) |
| 典型算法 | AES, ChaCha20 | RSA, ECDH, Ed25519 |
| 密钥长度 | 128~256 bit | 2048~4096 bit(RSA) |
| 适用场景 | 批量数据加密 | 密钥协商、数字签名 |
为什么非对称慢?因为数学运算复杂。RSA 靠大整数分解,ECC 靠椭圆曲线上的点乘。你在可搜索加密里要是用非对称去加密文档,性能会很难看。所以我建议你只拿它来做密钥协商或生成短小的密文令牌。
哈希函数:捏泥巴一样的数据指纹
哈希函数接受任意长度的输入,输出固定长度的摘要(比如 SHA-256 输出 256 位)。它的特点是:不可逆、抗碰撞、雪崩效应。改一个比特,输出就面目全非。
在可搜索加密里,哈希函数经常用来构造索引的键。举个例子:把文档中的关键词哈希一下,然后把哈希值当作索引的地址。但直接哈希会面临“确定性泄露”——同一个关键词永远对应同一个哈希。所以我们需要加盐(salted hash)或者用伪随机函数来盲化。
来看一个简单的 SHA-256 使用示例:
import hashlib
msg = b"keyword_to_index"
digest = hashlib.sha256(msg).hexdigest()
print(digest) # 64 位十六进制字符串
但你想想看,如果服务器知道这个哈希值,就能对字典里的每个候选词做一遍哈希,然后匹配。这就是确定性哈希的致命伤。所以真正的盲索引要用伪随机函数来代替。
伪随机函数:可搜索加密的魔法引擎
伪随机函数(PRF)是一个带密钥的确定性函数。输入一个值和密钥,输出一个看起来完全随机的值。同样输入再跑一次,结果一样。但是因为没有密钥的人无法猜测输出,所以它完美替代了哈希函数在索引中的角色。
说它“伪随机”,是因为理论上它并不是真随机——只要知道密钥就能复现。但这就是我们想要的:用户掌握密钥,服务器没有密钥,那么服务器看到的输出就是随机的。
标准的 PRF 构造可以用 HMAC-SHA256 或者 AES-CMAC。我一般用 HMAC-SHA256,因为 Python 标准库直接支持,不用额外依赖。
import hmac
import hashlib
key = b"32_byte_secret_key_here..."
msg = b"search_term"
prf_output = hmac.new(key, msg, hashlib.sha256).digest()
print(prf_output.hex()) # 64 字节的伪随机输出
搞清楚了这四个基础部件,接下来我们就能搭积木一样把盲索引系统建起来。下面这张图把它们的定位和关系串了起来。
你看,对称加密负责数据的机密性,非对称加密处理密钥交换,哈希函数提供不可逆的指纹,而伪随机函数——嗯,它就是那个让盲索引名副其实的魔法。后面几节我们就要用 PRF 来构建真正的盲索引方案了。先把这些概念在脑子里过一遍,实操起来会顺畅得多。