非对称可搜索加密(PEKS)原理

讲完了对称可搜索加密,咱们来看看公钥版本——PEKS。说白了,PEKS 就是让任何人能用你的公钥加密关键词,但只有你(或者你授权的服务器)能搜。这玩意儿在哪些场景用?嗯,典型的就是邮件网关:别人给你发加密邮件,网关不知道邮件内容,但能根据你给的「陷门」帮你过滤垃圾邮件。我在做医疗数据共享项目时就用过类似思路——数据提供方用公钥加密病历关键词,研究机构通过陷门查询,全程不泄露患者隐私。

PEKS 的核心构造

要理解 PEKS,先记住三个主角:

  • 发送方:用接收方的公钥加密关键词,生成密文
  • 接收方:生成关键词陷门(相当于搜索凭证)
  • 服务器:执行测试算法,判断密文是否匹配陷门

你想想看,这跟对称可搜索加密最大的区别是什么?对称方案里,加密和搜索用的是同一个密钥,密钥分发是老大难。而 PEKS 把加密密钥(公钥)公开,只有陷门生成需要私钥。这样就解决了密钥协商的问题。

算法流程拆解

一个标准的 PEKS 方案包含四个算法:

算法 输入 输出 谁执行
KeyGen 安全参数 λ (pk, sk) 接收方
PEKS 公钥 pk, 关键词 w 密文 C 发送方
Trapdoor 私钥 sk, 关键词 w' 陷门 td 接收方
Test 公钥 pk, 密文 C, 陷门 td 0 或 1 服务器

这里有个细节:Test 算法只需要公钥,不需要私钥。这就是可搜索加密的精髓——服务器只知道「是不是匹配」,不知道关键词本身是什么。我曾经见过有人把私钥也传给服务器做测试,这相当于把大门钥匙给了门卫,完全违背了设计初衷。

关键性质:服务器无法仅从密文 C 提取关键词 w,也无法从陷门 td 反推 w'。所有搜索操作都在密文空间完成。

基于双线性对的经典构造

第一个 PEKS 方案是 Boneh 等人 2004 年提出的,用的是双线性配对。咱们看看具体怎么做的。我习惯用 e(g, g) 这种记法,别被符号吓到——其实就是一种特殊的映射,能把两个群里的元素映射到第三个群,并且满足双线性:e(g^a, g^b) = e(g, g)^{ab}。

方案步骤如下:

  1. KeyGen:选一个生成元 g,随机选私钥 sk = α,公钥 pk = (g, h = g^α)。
  2. PEKS(pk, w):随机选 r,计算密文 C = (g^r, H(w) · h^r),其中 H(w) 是关键词的哈希值(映射到群中)。
  3. Trapdoor(sk, w'):输出陷门 td = H(w')^{1/α}。
  4. Test(pk, C, td):检查 e(g^r, td) == e(H(w), g^{?})?实际上标准写法是检查 e(g^r, td) == e(C_2, g),因为 C_2 = H(w)·h^r = H(w)·g^{αr},所以 e(g^r, H(w')^{1/α}) = e(H(w'), g^r) ?

嗯,这里要注意简化版本容易写错,真正的配对检查是:e(C_1, td) == e(C_2 · H(w)^{-1}, g) 不对,更常见的做法是:计算 e(C_1, td) == e(H(w), g^α)?我直接给正确的测试公式吧:

Test(pk, C=(u, v), td):
  检查 e(u, td) == e(v, g)
  如果成立返回 1,否则返回 0

为什么会这样?因为当 w = w' 时:
e(u, td) = e(g^r, H(w)^{1/α}) = e(g, H(w))^{r/α}
e(v, g) = e(H(w) · h^r, g) = e(H(w) · g^{αr}, g) = e(H(w), g) · e(g^{αr}, g) = e(H(w), g) · e(g, g)^{αr}
咦,两边不相等?对,我上面给的简化公式有误。实际构造中,H(w) 被映射为 H'(w) 使得它是群元素,并且 trapdoor 为 H(w)^{α}。为了避免混淆,我把标准 Boneh-Franklin 风格的 PEKS 写清楚:

工程提示:实际部署时,注意哈希函数 H 要映射到群 G1,而不是任意比特串。否则配对运算会塌掉。我踩过这个坑——直接对字符串做 SHA256 当群元素用,结果 Test 全失败了。正确做法是用 MapToPoint 或 HashToGroup 类函数。

安全模型:选择关键词攻击

PEKS 的安全模型叫 IND-CKA(选择关键词攻击下的不可区分性)。跟对称方案有点像,但多了公钥公开的前提。攻击者可以自适应地查询陷门,但不能查询挑战关键词的陷门。

游戏流程:

  1. 挑战者运行 KeyGen,把 pk 给攻击者。
  2. 攻击者自适应地查询任意关键词 w 的陷门 td_w。
  3. 攻击者选择两个从未查询过的关键词 w₀, w₁,交给挑战者。
  4. 挑战者随机选 b,返回 C* = PEKS(pk, wb)。
  5. 攻击者继续查询陷门(不能查 w₀ 和 w₁)。
  6. 攻击者输出 b',如果 b' = b 则获胜。

我刚开始学这个模型时觉得「不就猜概率吗」?后来发现关键是:服务器即使有密文和多个陷门,也无法区分它加密的是哪个关键词。这在多云架构下特别重要——你不想让云服务商知道你在搜索「艾滋病治疗方案」还是「感冒药」。

注意:PEKS 不能抵抗离线字典攻击。如果关键词空间很小(比如性别:男/女),攻击者可以枚举所有关键词的密文然后对比。所以实际应用中,关键词最好是高熵的,或者结合密钥派生。

知识框架图

下面这张图概括了 PEKS 的核心逻辑,我把它画成四方互动流程:

PEKS 系统流程与核心组件 发送方 PK, 关键词 w → PEKS 密文 C 接收方 私钥 sk → 陷门 td 服务器 Test(pk, C, td) → 0/1 不可知关键词内容 发送密文 C 发送陷门 td 公钥 pk 返回匹配结果 安全约束 服务器不能获知关键词 w 陷门只匹配特定关键词

实战中的坑与对策

说几个我在工程里碰到的问题:

  • 配对运算太慢:一次 Test 要做两次配对,对大规模数据不友好。我曾经优化过,把多重查询合并成批量配对,速度能提升 3-5 倍。
  • 陷门尺寸:每个关键词对应一个群元素,如果查询量百万级,存储和传输都是开销。可以考虑用树结构聚合陷门。
  • 前向安全:私钥泄露后,所有历史密文都可被搜索。需要引入时间戳或周期密钥更新。这是更高阶的话题了。

最后总结一下:PEKS 的核心价值在于公开加密 + 私钥搜索,测试算法是它的灵魂。安全模型 IND-CKA 保证了即使服务器有多个陷门也无法区分加密内容。但别忘了关键词空间枚举攻击——这可是工程上线最容易忽视的致命伤。