密码学基础:对称加密(AES)、非对称加密(RSA/ECC)、哈希函数
做密文检索,密码学是绕不开的地基。说实话,刚开始接触这个领域时,我也觉得这些概念很枯燥。但后来在实际开发中踩过几次坑才明白——不理解这些基础,根本写不出安全又高效的检索代码。
这一章我们只讲三样东西:对称加密、非对称加密、哈希函数。它们是密文检索的三大支柱。我会结合自己踩过的坑,帮你把每个原理和选型要点说透。
对称加密:一把钥匙开一把锁
对称加密很好理解:加密和解密用同一把密钥。就像你家里的大门钥匙,锁门和开门都是它。
我个人习惯在密文检索场景里优先选 AES(Advanced Encryption Standard)。为什么?因为它快,硬件支持好,而且经过全球密码学家多年审视,安全性公认可靠。
AES 的核心参数
| 参数 | 说明 | 我的建议 |
|---|---|---|
| 密钥长度 | 128 / 192 / 256 bit | 必须 256 bit,别省那点性能 |
| 工作模式 | ECB / CBC / GCM / CTR … | 推荐 GCM(认证加密),其次 CTR |
| 填充方式 | PKCS#7 / NoPadding | 流模式不需要填充,GCM 自带认证标签 |
| 初始化向量 | IV / Nonce | 每次加密必须不同,可以用随机数+计数器 |
为什么 AES 能用在密文检索里?因为我们可以把加密后的数据直接建索引,只要工作模式选对了(比如确定性的保序加密模式或支持相等检索的格式保留加密),就能在不解密的情况下完成匹配。
一个简单的 AES-GCM 加密示例(伪代码)
// 注意:实际生产不要硬编码密钥
key = random_bytes(32); // 256位密钥
iv = random_bytes(12); // 96位Nonce
cipher = AES_GCM_Encrypt(plaintext, key, iv, aad);
// cipher = (密文 || 认证标签)
decrypted = AES_GCM_Decrypt(cipher, key, iv, aad);
// 验证失败返回错误,否则返回明文
做密文检索时,如果你需要做范围查询(比如“大于”“小于”),对称加密的保序加密(OPE)是个方向。我试过 CLOZ 方案,但要注意泄露边界——少泄露不等于零泄露。
非对称加密:公钥私钥分着用
对称加密有个通病:密钥分发很棘手。你想想看,加密检索的双方怎么安全地交换那把密钥?非对称加密就解决了这个问题——公钥随便发,私钥自己藏。
密文检索里,非对称加密通常用来做两件事:密钥交换和数字签名。主流的算法有 RSA 和 ECC(椭圆曲线密码学)。
RSA vs ECC 对比
| 特性 | RSA (2048位) | ECC (256位, 如 secp256r1) |
|---|---|---|
| 安全性等价 | 112 位对称安全 | 128 位对称安全 |
| 密钥长度 | 2048 位 | 256 位 |
| 加密性能 | 较慢(模幂运算) | 更快(标量乘) |
| 签名大小 | 256 字节 | 64 字节 |
| 推荐场景 | 兼容旧系统、无性能瓶颈 | 移动端、IoT、高频签名 |
我个人更偏爱 ECC,尤其在资源受限的环境里。我记得有一次集成密文检索 SDK 到嵌入式设备,RSA 一次签名要 50ms,换成 ECC 后降到 5ms,差距肉眼可见。
重点: 在密文检索里,非对称加密很少直接加密原始数据(太慢)。通常用对称加密加密内容,用非对称加密加密对称密钥。这叫「混合加密」,既安全又高效。
RSA 示例(密钥生成、加密、解密)
# 生成 2048 位 RSA 密钥对
rsa_keypair = RSA_Generate(2048)
# 用公钥加密对称密钥
encrypted_key = RSA_Encrypt(symmetric_key, rsa_keypair.public)
# 用私钥解密对称密钥
decrypted_key = RSA_Decrypt(encrypted_key, rsa_keypair.private)
注意:RSA 能加密的明文长度有限制(< 密钥位数/8 - 填充长度)。2048 位 RSA 大约只能加密 245 字节。别直接把大文件往上怼,否则会报错。
哈希函数:不可逆的指纹
哈希函数会把任意长度的输入,变成固定长度的输出。像指纹一样,很难找到两个不同输入有相同输出(抗碰撞),而且从哈希值反推原始输入几乎不可能(单向性)。
在密文检索里,哈希最常见的用途就是构建 关键词索引。你把每个关键词哈希一下,把哈希值作为索引,之后搜的时候把搜索词的哈希值拿过来匹配就行。这样服务器不知道你搜的是什么词。
常用哈希算法选型
| 算法 | 输出长度 | 安全性 | 性能(相对) |
|---|---|---|---|
| MD5 | 128 位 | 已破解,不推荐 | 极快 |
| SHA-1 | 160 位 | 已破解,不推荐 | 快 |
| SHA-256 | 256 位 | 安全 | 中等 |
| SHA-3-256 | 256 位 | 安全,新一代 | 略慢于 SHA-2 |
| BLAKE2 | 可配置 | 安全,极快 | 比 SHA-2 快 2-3 倍 |
平时做密文检索索引,我几乎只用 SHA-256 或 BLAKE2b。BLAKE2 在软件实现上特别快,而且支持输出长度自定义,非常灵活。
你可能会问:哈希后的索引会不会碰撞?理论上有,但概率极低。如果你要做多关键词的联合检索,还可以把多个哈希值拼接后再次哈希,或者用布隆过滤器压缩存储——我在项目里就是这么做的,内存占用降了 80%。
哈希索引构建伪代码
// 构建倒排索引(关键词 -> 文档列表)
for each document in corpus:
for each keyword in extract_keywords(document):
h = SHA256(keyword)
inverted_index[h].append(document.id)
// 查询
query_hash = SHA256(user_query)
results = inverted_index.get(query_hash, [])
别直接用原始哈希做相等检索!因为相同关键词的哈希值永远一样,这能泄露频率信息——攻击者可以统计每个哈希出现次数,推断出热门关键词。解决办法:加盐(随机数),或者用可搜索加密专用的原语。
三种技术的关系:一张图理清
我把对称加密、非对称加密、哈希在密文检索中的应用画成了下面这张图。你看一遍就能明白它们各自扮演什么角色。
实战选型建议
说了这么多理论,到了真要做密文检索 SDK 时,该怎么选?我给你一个我自己的参考清单:
- 数据加密: 用 AES-256-GCM。既加密又认证,一个 API 搞定全部。
- 密钥交换: 用 ECDH(基于 ECC 的 Diffie-Hellman),安全级别高,握手快。
- 关键词索引: 用 SHA-256 或 BLAKE2b,配合随机盐值防频率分析。
- 数字签名 / 身份验证: 用 ECDSA(基于 ECC 的签名算法),签名短,验证快。
- 混合封装: 用 ECC 公钥加密 AES 密钥,AES 密钥加密数据,哈希索引独立构建。
另外,别忘了考虑量子计算抗性。虽然现在还不急,但如果你的产品生命周期超过 5 年,建议关注 NIST 后量子密码标准(如 Kyber、Dilithium)。我已经在下一代 SDK 里预留了替换接口。
嗯,这一章的内容量不少,但都是实打实要用的。你不需要一次性记住所有参数,只要理解每个原语在密文检索里的定位——对称加密负责速度,非对称加密负责安全分发,哈希负责可搜索性。后面讲具体 SDK 接口实现时,我们再深入每个环节的代码细节。