密文检索基础:同态加密与可搜索加密
为什么需要密文检索?
数据加密后,你就没法直接搜、没法直接算了。但业务场景往往要求你在密文上干活——比如云端存着病人的化验单,你不能解密再统计平均值,那会泄露隐私。密文检索就是解决这个矛盾的。
说白了,它让系统在“眼盲”的状态下,还能帮你找到正确的东西。我接触这个领域是几年前做医疗数据共享项目,甲方要求搜索“阳性病例”但不能看到具体姓名。那会儿我第一个想到的就是可搜索加密,后来发现同态加密也能做,但性能天差地别。
同态加密(HE):加密状态下计算
同态加密允许你对密文直接做加法、乘法,解密后得到的结果和明文上做运算一样。举个例子:你手里有加密后的工资数据,你想算平均工资。正常做法是解密→计算→再加密。HE 让你直接在密文上算,算出加密后的平均值,然后解密拿到结果。
同态加密有几个级别:
- 部分同态(PHE):只支持加法或只支持乘法。比如 Paillier 只能做加法同态,RSA 只能做乘法同态。性能还行,实际可用。
- 浅同态(SHE):支持有限次数的加法和乘法。适合做多项式计算,但不能无限叠加。
- 全同态(FHE):支持任意次数的加法和乘法,理论上可以跑任何程序。但性能极差,一个简单计算可能慢几千倍。
我记得第一次跑全同态乘法,128 位安全级别下,一次乘法花了 0.3 秒。我当时的内心是崩溃的。嗯,不要指望 FHE 能处理实时流数据。
可搜索加密(SSE):加密状态下搜索
可搜索加密解决的是另一种需求:我想在加密文档库里搜关键词,但不暴露文档内容,也不暴露搜索词。HE 也能搜,但代价太大。SSE 专为此而生。
SSE 的基本思路:
- 为文档集构建加密索引,索引里每个关键词对应一组文档标识符。
- 用户用密钥生成一个“陷门”(trapdoor),相当于加密后的搜索词。
- 服务器拿着陷门在加密索引上匹配,返回匹配的加密文档标识符。
- 用户解密标识符,再按需解密文档。
你想想看,整个过程服务器不知道你搜了什么词,也不知道你搜出了哪些文档。隐私保护到位。
SSE 又分为几种类型:
| 类型 | 特点 | 性能 | 典型场景 |
|---|---|---|---|
| 对称 SSE | 搜索方和加密方共享同一个密钥 | 快(O(匹配文档数)) | 个人云盘、私有文档搜索 |
| 非对称 SSE | 公钥加密,私钥搜索;支持多用户写入 | 慢(通常需要配对运算) | 共享数据库、投稿系统 |
| 动态 SSE | 支持添加/删除文档后更新索引 | 中等(更新开销较大) | 日志审计、实时数据流 |
我参与过的一个日志审计系统,用的就是动态 SSE。因为每天会新增几百万条日志,静态索引重建一次要 40 分钟,太离谱了。后来换用支持更新操作的方案,每次增量更新几十毫秒搞定。这就引出一个避坑点——
同态加密 vs 可搜索加密:怎么选?
很多人搞混这两者。我简单对比一下:
| 对比维度 | 同态加密(HE) | 可搜索加密(SSE) |
|---|---|---|
| 核心能力 | 密文上做任意计算 | 密文上做关键词搜索 |
| 搜索性能 | 极差(FHE 慢 10^6 倍) | 好(通常亚秒级) |
| 计算灵活性 | 极高(可执行任意函数) | 低(仅支持关键词匹配) |
| 数据泄露 | 几乎无(语义安全) | 有(访问模式泄露) |
| 适用场景 | 数据聚合、模型推理、隐私机器学习 | 文档搜索、日志审计、关键字过滤 |
| 成熟度 | FHE 仍偏学术,工程化困难 | 已有工业级实现(如 CryptDB、Oblivious 方案) |
技术选型决策逻辑
下面这张 SVG 图展示了我在项目中常备的选型流程。从需求出发,先问你是要“计算”还是“搜索”,然后逐层细化到具体技术。
你看这个图,从根需求出发,两条路区别很明显。左边走 HE,右边走 SSE。我个人的建议是:别一上来就想着“全同态加密好屌”,先掂量一下你的计算复杂度。多数审计日志场景只需要关键词匹配,SSE 足够。
避坑清单(个人实战总结)
- 别忽略访问模式泄露:SSE 的搜索模式会暴露哪些文档被搜到了多少次。攻击者可以通过频率分析反推查询内容。我在一个金融项目里补丁了 ORAM(不经意随机访问)才解决。
- 同态加密的密钥长度别贪安全:有人非要设 256 位安全级别,结果一次加法慢得肉眼可见。128 位通常够用,别自找麻烦。
- 索引更新务必测试并发:动态 SSE 的并发写入经常死锁。我曾经在生产环境遇到过,因为底层数据结构(如稀疏矩阵)没加锁,导致索引损坏。
- 硬件加速别迷信:有些同态加密声称支持 GPU/FPGA 加速,但实际部署时驱动、精度、内存带宽都会出问题。买之前找厂商要真实 benchmark。
- 业务逻辑不要硬套:如果你的查询经常需要排序、模糊匹配、正则,SSE 基本帮不上忙。这时候考虑一下 SQL on encrypted data(比如 CryptDB 的方案),或者干脆配合硬件 TEE。
性能调试小记
我举个例子,之前做 SSE 项目,加密索引用的是布谷鸟哈希(Cuckoo Hashing)。理论写入复杂度 O(1),但实际跑起来因为冲突重哈希,经常把 CPU 打满。后来换成经典哈希表 + 链地址法,虽然空间多了点,但实际吞吐翻了三倍。
嗯,这里有个教训:别只看里的渐进复杂度,要拿真实数据集做压力测试。我个人习惯是先拿 10 万条数据跑一轮,确认没有内存泄漏和性能断崖,再上生产。