可搜索加密原理:SSE 与 PEKS 的核心差异

聊完了倒排索引和加密算法的基础,我们终于要进入正题了——可搜索加密(Searchable Encryption)。

很多人一听到这个名字,第一反应都是:“不就是把加密后的数据存进去,然后按关键词查出来吗?”

嗯,大体逻辑没错,但实现起来道道很深。

我个人习惯把可搜索加密分为两大类来理解:对称可搜索加密(SSE)和非对称可搜索加密(PEKS)。这两者在密钥管理、适用场景、安全模型上差异很大。

我在第一个密文检索项目中,就是因为没搞清楚两者的适用边界,选错了方案,后面改得很痛苦。今天咱们就把这块彻底掰扯清楚。

可搜索加密两大分类与核心流程 可搜索加密核心分类与流程对比 可搜索加密 (SE) 对称可搜索加密 (SSE) 非对称可搜索加密 (PEKS) ① 数据持有方生成对称密钥 K ② 加密文档 + 构建加密索引 ③ 客户端发送陷门 () ④ 服务器遍历索引返回结果 ① 接收方生成 (PK, SK) 密钥对 ② 发送方用 PK 加密关键字上链 ③ 接收方用 SK 生成搜索凭证 ④ 服务器运行 Test 算法匹配 关键区别:SSE 是“先加密再查”,需共享密钥;PEKS 是“让别人替你加密后也能查”,无需共享私钥。 SSE 安全假设更弱(KPA),PEKS 能抵御“关键词猜测攻击”但性能更差。

对称可搜索加密 (SSE)

SSE 是实际落地中最常用的方案。为什么?很简单,性能好、实现成熟。

说白了,SSE 的核心思想就是:你提前把文档的关键字提取出来,构建一个加密的倒排索引,然后把加密后的文档和这个索引一起扔给服务器。

查询的时候,你用对称密钥生成一个“陷门”(Trapdoor),服务器拿着这个陷门在你的加密索引里查找匹配项。

SSE 的典型工作流程

  1. 数据持有方生成对称密钥 K
  2. 用 K 加密所有文档(比如 AES-GCM)
  3. 用 K 加密每个文档的关键字,建立加密倒排索引
  4. 将密文文档 + 加密索引上传到云服务器
  5. 客户端用 K 生成目标关键词的陷门
  6. 服务器在不解密的情况下遍历索引,返回匹配的文档 ID
  7. 客户端用 K 解密返回的密文文档

这里有个核心问题:陷门是怎么保证“不可区分”的?

我记得有一个很经典的例子。假设你的索引里存了“医院”和“学校”两个词。如果你每次查“医院”都生成同一个陷门,服务器用不了多久就能统计出来——这不是泄露了吗?

所以,SSE 的方案里通常会在陷门生成时引入一个随机数(Nonce),保证同一关键词多次查询的陷门不同。但请注意,这并不能完全隐藏查询模式(Query Pattern),只能阻止“直接明文映射”。

我曾经踩过的一个坑

我记得有一次做金融文档检索项目,我天真地以为加密索引里的陷门是不可追踪的。后来被安全审计的老师问:“你的陷门访问模式有没有做 Oblivious 处理?”我一查,才发现普通的 SSE 方案,服务器虽然不知道你查的是什么词,但它能判断出“这次查的和上次查的是同一个词”。这就是“访问模式泄露”(Access Pattern Leakage)。在金融、医疗等强隐私场景下,这是不可接受的。

解决方案是引入 ORAM(Oblivious RAM)或 Path ORAM 来隐藏访问模式,但性能代价极高。这一点在选型时要心里有数。

非对称可搜索加密 (PEKS)

PEKS 的全称是 Public-key Encryption with Keyword Search。和 SSE 最大的不同在于——密钥是对称的。

你想想看,SSE 要求客户端和服务器事先共享同一个对称密钥。但在很多真实场景里,这是做不到的。比如:

  • Alice 想把加密邮件发送到服务器上,Bob 想从服务器上搜索特定关键词的邮件
  • 多个不同的发送方需要加密数据给同一个接收方

这种情况下,你总不能把对称密钥分发给所有发送方吧?密钥管理会变成灾难。

PEKS 解决的就是这个问题。它用公钥加密搜索关键字,用私钥生成搜索凭证。

PEKS 典型场景举例

我记得之前有个医疗数据共享平台的设计,多个医院要把加密的病历上传到一个联邦服务器上。所有医院都使用同一个公钥加密关键字,而卫计委的人拿着私钥查询“肺癌患者”的相关病历。这就是 PEKS 的典型应用。如果换成 SSE,我得给每个医院发一把密钥,密钥分发就成了最头疼的事。

PEKS 的安全性假设比 SSE 更强。在 PEKS 中,攻击者无法区分给定了公钥的两个关键字加密结果。即使用户知道公钥,也无法推断出哪些关键字被加密了。这被称为“选择关键字攻击下的不可区分性”(IND-CKA)。

但硬币总有另一面。PEKS 的性能比 SSE 差很多。为什么呢?因为它依赖双线性对(Bilinear Pairing)运算。

每次匹配测试都需要做一次双线性对计算,而双线性对的计算开销是 AES 加密的几百倍甚至上千倍。

SSE vs PEKS:关键对比

维度 SSE(对称可搜索加密) PEKS(非对称可搜索加密)
密钥关系 收发双方共享对称密钥 公钥加密,私钥查询
适用场景 单一用户自存储、单用户多设备 多发送方给同一接收方的加密场景
性能效率 极快(主要开销在伪随机函数 PRF) 慢(主要开销在双线性对运算)
安全模型 KPA(已知明文攻击)或 stronger IND-CKA(选择关键字攻击)
关键词猜测攻击 天生不适用(无公钥) 内部攻击者可以用 PK 做离线猜测攻击
查询模式泄露 容易泄露(基本方案) 同样泄露(需引入模糊匹配)
典型算法 SWP 方案、OXT 方案、Diana BDOP 方案、BCOP 方案

看到这个对比表,不知道你是不是也想到了:为什么没有一个方案能同时兼具 SSE 的高性能和 PEKS 的多发送方能力呢?

嗯,现实就是这么残酷。密码学里经常会有这样的“不可能三角”。

一个实战层面的选型建议

在我经历过的真实项目中,选择 SSE 还是 PEKS,通常遵循以下几条原则:

  • 如果只有你一个人存、一个人查 —— 果断选 SSE。性能好,实现简单,安全性足够。
  • 如果有多人加密、一人查询 —— 用 PEKS。别纠结性能,功能需求优先。
  • 如果需要多人加密、多人查询 —— 两个都不行。你需要更复杂的方案(如 ABK-SE、属性基加密)。
  • 如果对查询模式泄露有严格限制 —— 别只选 SSE 或 PEKS,必须叠加 ORAM 或 PIR 等混淆机制。

一个小经验

如果你刚开始学习或做原型,我建议先从 SSE 入手。为什么?因为它的数学基础相对简单(主要是伪随机函数和对称加密),代码实现可以完全自己写。PEKS 涉及双线性对,需要用 PBC 库或 pairing-based cryptography 库,调试起来会复杂得多。

我自己带的实习生,通常第一周能搞定一个简单的 SSE 原型,但要做 PEKS 的 demo,两三天不一定能跑通双线性对的配对运算。

安全和隐私的权衡

讲到这里,必须提一个容易被忽视的点:SSE 的安全假设其实是 KPA(Known-Plaintext Attack)。

什么意思?就是攻击者不能同时看到“明文”和“密文”。但在很多场景下,这是不太现实的。比如你的文档里包含了固定的模板文字,攻击者很容易猜出一部分内容。

PEKS 的安全性更强,因为它用的是公钥加密,明文空间是关键词,理论上更难被破解。但 PEKS 有一个致命弱点——关键词猜测攻击(Keyword Guessing Attack)。

想象一下:攻击者手里有你的公钥 PK,他知道你可能会搜的词语(比如“发票”、“合同”这些常见词),他就可以用 PK 把猜测的关键词加密,然后和你的密文比对。如果匹配,他就知道了你查的是什么词。

我当年第一次看到这个攻击的时候,后背一凉。心里想:“那 PEKS 还有什么用?”

后来发现,解决方法有:

  • 加 Salt,让相同关键词每次加密结果不同
  • 使用更复杂的方案(如 SCF-PEKS、指定测试者的 PEKS)

但这些方案本质上是在安全性和可用性之间做权衡。没有安全的方案,只有足够安全的方案。

选型时的两个硬约束

1. 如果业务场景允许预计算索引,用 SSE(如日志检索、归档数据)

2. 如果业务场景不允许提前知道谁要查询,用 PEKS(如邮件网关、医疗共享)

强行在一个场景下用另一个方案,最终都会在密钥管理或查询延迟上吃大亏。

这一节的内容,表面上是两个方案的区别,但背后其实是“对称密码 vs 公钥密码”在搜索场景下的延伸。理解了这两条路,再去看具体的方案(比如 OXT、BCOP、Diana),你会发现它们都不外乎是在这两个框架上做优化和修补。

花时间把 SSE 和 PEKS 的原理吃透,后面你读到那几十篇加密搜索时,就能很快抓住核心了。