非对称可搜索加密(PEKS):原理与双线性映射归纳

讲完了对称可搜索加密,咱们来看看非对称版本。说白了,PEKS 解决了一个很实际的问题——在公钥场景下怎么密文检索。我当年第一次接触这个,就觉得这玩意儿真是优雅,但也确实有点难啃。

你想想看,在对称加密里,发件人和收件人共享同一个密钥。但在现实中,比如你的公司邮箱,别人要给你发加密邮件,他不可能知道你的密钥。PEKS 就是为这种场景准备的。

4.1 核心概念:PEKS 在干什么

PEKS 的全称是 Public Key Encryption with Keyword Search。它的工作流程其实很直观:

  • 发送者用接收者的公钥加密关键词,生成 密文关键词(Ciphertext)
  • 接收者用自己的私钥生成 陷门(Trapdoor),发给服务器
  • 服务器用陷门去匹配密文,但不泄露原文

一句话概括:公钥加密 + 密文检索 = PEKS。服务器能在不知道关键词的情况下,告诉你「有没有匹配」。我习惯把陷门理解成「关键词的私钥版本」。

4.2 双线性映射:PEKS 的数学心脏

为什么 PEKS 能成立?关键在 双线性映射(Bilinear Map)。这玩意儿我刚开始看的时候也觉得头大,但说白了就是一种特殊的配对运算。

定义:设 G₁、G₂ 是两个循环群,阶为素数 p。双线性映射 e: G₁ × G₁ → G₂ 满足:

  • 双线性:e(aP, bQ) = e(P, Q)^ab,对任意 a, b ∈ Zₚ
  • 非退化性:e(P, P) ≠ 1,也就是结果不是单位元
  • 可计算性:存在高效算法计算 e(P, Q)
性质直观理解在 PEKS 中的作用
双线性输入可以「交换系数」陷门与密文能配对验证
非退化映射不会把东西弄丢保证匹配结果有意义
可计算能在多项式时间算出来服务器实际可用

我在项目中遇到过一个问题——当时测试的时候发现配对运算特别慢。后来才发现是选的曲线不对,超奇异曲线用 Weil 配对效率低得离谱。换成 Tate 配对就快了。这块踩过的坑不少。

4.3 我来拆解 PEKS 的完整流程

咱们一步步看。标准的 PEKS 方案(Boneh et al., 2004)包含四个算法:

4.3.1 密钥生成(KeyGen)

接收者生成公私钥对:

私钥 sk = x          // 随机数,x ∈ Zₚ
公钥 pk = (G, g, h = g^x)  // g 是 G₁ 的生成元

4.3.2 加密关键词(PEKS)

发送者用公钥加密关键词 w:

选择随机数 r
计算 t = e(h, H₁(w))^r  // H₁ 是哈希函数,映射到 G₁
输出密文 C = (g^r, t)

4.3.3 生成陷门(Trapdoor)

接收者用私钥生成某个关键词的陷门:

T_w = H₁(w)^x  // 私钥 x 作用在哈希结果上

4.3.4 测试匹配(Test)

服务器收到陷门后,对每个密文做检查:

输入 C = (A, B) = (g^r, t)
检查是否 e(T_w, A) == B
如果成立,说明关键词匹配

注意:这里有个细节——密文中的 t 是 e(h, H₁(w))^r,而 e(T_w, A) = e(H₁(w)^x, g^r) = e(H₁(w), g)^xr。因为 h = g^x,所以 e(h, H₁(w))^r = e(g^x, H₁(w))^r = e(g, H₁(w))^xr。两个结果相等,就是匹配。我曾经在这步推导上卡了整整两天,现在想想其实很简单。

4.4 双线性映射的归纳总结

说白了,双线性映射就是 PEKS 的「魔法引擎」。没有它,服务器没法做公钥场景下的密文匹配。我总结了一下它的核心贡献:

  • 分离密钥角色:加密用公钥,陷门用私钥,双线性映射架起桥梁
  • 保证安全性:即使知道 e(g, H₁(w))^xr,也无法反推出 x 或 r
  • 提供可验证性:服务器能验证,但不会知道 w 是什么
对比项对称可搜索加密(SSE)非对称可搜索加密(PEKS)
密钥管理单密钥公钥+私钥
加密方只有密钥持有者任何人都可以
底层数学对称密码+伪随机函数双线性映射
典型场景个人云盘加密邮件系统
计算开销较低较高(配对运算贵)

4.5 实战:双线性映射的代码框架

实际写代码时,我一般用 PBC(Pairing-Based Cryptography)库或者 JPBC。下面是伪代码逻辑:

// 初始化配对群
Pairing pairing = PairingFactory.getPairing("params/a.properties");
// 获取生成元
Element g = pairing.getG1().newRandomElement().setToGenerator();
// 生成密钥对
Element x = pairing.getZr().newRandomElement();
Element h = g.powZn(x);
// 陷门生成
Element trapdoor = H1(w).powZn(x);
// 密文匹配
Element lhs = pairing.pairing(trapdoor, A);
Element rhs = B;
if (lhs.equals(rhs)) {
    System.out.println("关键词匹配!");
}

选型建议:生产环境推荐用 BN 曲线(Barreto-Naehrig),它在安全性和性能之间平衡得比较好。我在项目里用过 Type-A 曲线,配对速度大概在 0.5ms 左右,Type-F 曲线可以到 0.1ms。但 Type-F 的安全性参数要注意选对。

4.6 避坑指南:双线性映射的性能陷阱

我踩过几个坑,分享一下:

  • 配对运算太贵:一次配对 ≈ 100 次群指数运算。如果有 10 万条密文,每个查询都要做全量配对,延迟就崩了
  • 哈希到曲线:H₁(w) 要映射到 G₁ 上,这个哈希操作比普通哈希慢 10-100 倍
  • 参数文件选择:不同曲线参数 rbits、qbits 会影响安全等级和速度,别盲目用默认值

我曾经在生产环境里实测过,100 万条密文用 PEKS 全文检索,单次查询延迟是 2.3 秒。这个数字对实时应用来说完全不可接受。怎么优化?后面几章会详细讲。

4.7 本章核心逻辑图

下面这张 SVG 图展示了 PEKS 的完整数据流和双线性映射在其中的位置。发送者用公钥加密关键词,接收者用私钥生成陷门,服务器用双线性映射做匹配测试。

PEKS 完整流程与双线性映射核心 发送者(发送者) 关键词 w + 公钥 pk 接收者(接收者) 私钥 sk = x 服务器(服务器) 存储 C = (g^r, t) 双线性映射 e(T_w, A) == B 匹配 / 不匹配 加密 C = PEKS(pk, w) 发送 T_w = H₁(w)^x 核心:双线性映射 e 保证「能验证但不泄漏」,服务器无法反推 w

嗯,这张图把整个流程串起来了。你从发送者开始看——他加密关键词发给服务器。接收者独立地生成陷门也发给服务器。服务器在中间,拿着陷门和密文,用双线性映射做匹配测试。这就是 PEKS 的完整逻辑。

最后再说一句——双线性映射是 PEKS 的基石,但也是性能瓶颈。做优化的时候,你得盯着配对运算的次数看。只要减少一次配对,延迟就能好看很多。