假如你经营一家「水果保密加工坊」,主营项目是替邻居老王加工苹果汁,却绝不允许自己偷看到苹果的数量——那是他要参赛的机密。聪明的老王把苹果塞进一只带双重锁的密封箱,箱子外面只有一个固定的窗口:接上一根管道,能把两箱果汁倒进一个大桶。你只需要照做:倒、封、送回。老王用唯一的钥匙开箱,惊喜地发现桶里的总量正好等于两箱之和。全程你摸不到一颗苹果,却帮他把活干完了。这种「隔着密封箱加工数据」的魔法,在密码学里叫同态加密(Homomorphic Encryption);而眼前这只只开了一个窗口、只能做一种加工的箱子,就是本文主角——部分同态加密(Partially Homomorphic Encryption, PHE)。本文会拆开 RSA、Paillier 两只经典箱子的内部结构,看看「加法窗口」和「乘法窗口」是如何设计出来的,再聊聊它在投票、医疗统计等场景里的真实用法。

岗位关联

学会部分同态加密,你就掌握了「数据可用不可见」的第一块拼图:既能保护用户隐私,又能继续做统计分析——这是隐私计算、数据工程与 AI 落地中的硬技能。

目录
  1. 可视化演示
  2. 1. 同态加密概述
  3. 2. 核心原理
  4. 3. RSA乘法同态
  5. 4. Paillier加法同态
  6. 5. 其他PHE算法
  7. 6. 工程实现要点
  8. 7. 典型应用场景
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示同态加密核心原理:密文上直接运算(无需解密)、Paillier加法同态vs RSA乘法同态对比、以及PHE→SHE→FHE演进路线(单运算无限→加乘有限→任意电路无限)。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+captions.concept+'

1. 同态加密概述

果汁加工坊开张第一天,老王提着两只密封箱来了。「帮我算算两箱果汁倒在一起总共多少毫升,但不许打开箱子。」你照做了,倒完封好递回去。这单生意让你恍然大悟:有些计算,根本不需要看见数据本身。

这种「在密文上直接计算」的玩法,密码学里叫同态加密。它把「加密」和「计算」两件原本水火不容的事焊在了一起。按能力从弱到强,可分成四档:完全没有同态能力的传统加密、只支持一种运算的部分同态(PHE)、支持有限运算深度的分级同态(SHE)、以及支持任意运算的完全同态(FHE)。本文主角就是第二档——PHE,它就像密封箱上唯一一扇加工窗口。

同态加密可追溯到 1978 年 Rivest、Shamir 和 Adleman 提出 RSA 时顺手发现的「密文乘法窗口」;1999 年 Paillier 补上了实用的「密文加法窗口」;直到 2009 年 Gentry 的博士论文才拼出 FHE 这块天花板。PHE 虽然「偏科」,但正因为它只需保护一种运算,性能远好于 FHE,在实际工程中反而更常用。

老王的果汁坊只装了一条管道,却因此又快又稳。PHE 正是如此:把能力锁死在一扇窗口上,换来可落地的效率——这是「够用就好」的工程智慧,也是它与昂贵的 FHE 拉开差距的关键。

E(m1)⊕E(m2)=E(m1∘m2),D(E(m))=m\displaystyle E(m_1) \oplus E(m_2) = E(m_1 \circ m_2), \qquad D(E(m)) = m
这个公式是「隔箱操作」的通用写法:对两个密文做加工 ⊕,等价于对明文先做运算 ○ 再封箱;而开箱函数 D 能把结果还原成明文 m。
  1. E 与 D(封箱与开箱)E 是「封箱函数」(加密),D 是「开箱函数」(解密)。在果汁坊里,E 就是把水果压进密封箱,D 就是用钥匙取回果汁。
    E(m)=c,D(c)=mE(m) = c, \quad D(c) = m
  2. m₁ 与 m₂(两篮水果)两个参与计算的水果原值,它们可以是果汁体积、选票数量、医院病例数等任何业务数字。
    m1,m2∈Mm_1, m_2 \in \mathcal{M}
  3. ○ 与 ⊕(内外两套运算)○ 是「箱外世界」的运算(如加法),⊕ 是「隔着箱子」的对应动作(如密文相乘)。同态的本质,就是这一内一外两个符号可以互换位置。
    ⊕⟷∘\oplus \longleftrightarrow \circ
  4. D(E(m)) = m(开箱验货)先封箱再开箱等于没动过——这是密码学正确性要求,也是「加工不破坏结果」的保证。
    D(E(m))=mD(E(m)) = m

2. 核心原理

你忍不住好奇:这箱子到底怎么做到「只看得见加工、看不见内容」?再回果汁坊观察,你发现两个细节:第一,箱子的窗口类型是出厂时焊死的,加法箱永远倒不出乘法结果;第二,老王每次封箱,箱内隔层都长得不一样,可开箱后的果汁量却分毫不差。这就是「运算耦合」与「语义安全」。

密码学里,这两个细节对应三个核心组件:公钥 pk(谁都能用的标准锁扣)、私钥 sk(唯一的开箱钥匙)、随机盐 ρ(每次封箱的内部隔层)。加密函数把 m、pk、ρ 三者揉在一起生成密文 c;解密时只能用 sk 把 c 还原成 m。公钥公开、私钥私有、随机盐每次变换,三者缺一不可。

重点理解「运算耦合」:同态性质不是外挂插件,而是代数结构自带的属性。RSA 的密文是 m^e mod n,这个「幂」结构天然让密文相乘映射明文相乘;Paillier 的密文是 g^m·r^n,这个「指数」结构天然让密文相乘映射明文相加。想换运算类型?请换一只箱子——这就是「部分」二字的真正含义。

所以,部分同态加密的本质,是一类「出厂即焊死窗口」的精密箱子。选箱子之前,请先想清楚业务只需要加法还是乘法,再去货架上挑对应型号。

c=Encpk(m,ρ),Decsk(c)=m\displaystyle c = \mathrm{Enc}_{pk}(m, \rho), \qquad \mathrm{Dec}_{sk}(c) = m
加密不只有一个输入 m,还有一个随机因子 ρ——这就是箱内的「隔层」;即使每次封箱的水果相同,箱子外观也不同,但开箱后依然还原 m。
  1. pk(公钥锁扣)公开的标准锁扣,任何人都能用它来封箱,对应果汁坊门口挂着的那把「通用锁扣」。
    Encpk\mathrm{Enc}_{pk}
  2. ρ(随机隔层)每次封箱随机摆放的内部隔层,让相同明文产生不同密文,防止外人「看箱子认水果」(即频率分析攻击)。
    ρ∈{0,1}λ\rho \in \{0,1\}^{\lambda}
  3. sk(私钥钥匙)只保存在数据主人手里的钥匙,能打开所有用 pk 封的箱子,解密时配合私钥完成。
    Decsk(c)=m\mathrm{Dec}_{sk}(c) = m
  4. 正确性与安全性并存密文每次不同(c ≠ c'),但解密结果始终一致——这正是语义安全的核心要求。
    c≠c′,Decsk(c)=Decsk(c′)=mc \ne c', \quad \mathrm{Dec}_{sk}(c) = \mathrm{Dec}_{sk}(c') = m

3. RSA乘法同态

第一只经典箱子是 RSA。它像一只「乘法窗口」的密封箱:两箱水果的数量可以隔着箱子直接相乘。1978 年 RSA 论文发表时,作者们就注意到这个奇妙的副作用——这大概是密码学史上第一次有人意识到「加密和计算可以共存」的现实。

RSA 的加密极简:选两个大素数 p、q,令 n = pq,取公开指数 e 与私钥 d,满足 e·d ≡ 1 (mod φ(n))。加密就是计算 c = m^e mod n。打个比方:n 是箱体尺寸,e 是窗口的咬合齿,m^e mod n 就是把水果数字「抬」进箱子的上锁动作。

关键在于模幂运算的天然亲和力:m₁^e × m₂^e = (m₁×m₂)^e (mod n),于是 E(m₁)×E(m₂) = E(m₁×m₂)——密文相乘,开箱即得明文之积。但也正因如此,教科书式 RSA 的密文可以被「加注」:攻击者把密文 c 换成 c·r^e,骗你解出 m·r,这叫密文可塑性。真实系统需要带填充的 RSA,但填充又会破坏同态——这是 RSA 作为 PHE 的先天矛盾。

这只乘法窗的箱子给了密码学界第一束光,但它「只能乘不能加」的偏科,也早早预告了后面 Paillier 的登场——果汁坊的下一个爆款,将是加法窗口。

c=me mod n,E(m1)×E(m2)=E(m1×m2)\displaystyle c = m^e \bmod n, \qquad E(m_1) \times E(m_2) = E(m_1 \times m_2)
RSA 的乘法窗口:两个密文相乘得到的密文,开箱后正好是明文乘积,即「密文相乘 = 明文相乘再上锁」。
  1. n = p·q(箱体尺寸)选取两个大素数 p、q 相乘得到 n,n 的因子分解之难是 RSA 安全的根基,n 越大箱子越难撬。
    n=p×qn = p \times q
  2. e(窗口的咬合齿)公开指数,封箱时把水果数字 m 抬到 e 次方并取模,这是「上锁」动作。
    c=me mod nc = m^e \bmod n
  3. E(m₁)×E(m₂)(两箱相乘)密文相乘时指数部分自然相加,等价于明文先乘再上锁,乘法窗口就此成立。
    m1e×m2e=(m1×m2)e(modn)m_1^e \times m_2^e = (m_1 \times m_2)^e \pmod n
  4. d(私钥钥匙)d 满足 e·d ≡ 1 (mod φ(n)),开箱时把密文抬到 d 次方,正好抵消上锁动作还原 m。
    m=cd mod nm = c^d \bmod n

4. Paillier加法同态

如果说 RSA 是乘法窗口,Paillier 就是目前应用最广的「加法窗口」。为什么加法更吃香?因为投票、统计、求和这些日常场景,本质上都是加法。1999 年 Pascal Paillier 提出的方案,一举把「密文相乘 = 明文相加」变成现实,从此电子投票有了密码学地基。

Paillier 加密分三步:选合数 n 和公开基数 g(通常取 g = n+1),每次加密随机挑一个盐 r,计算 c = g^m · r^n mod n²。这里 r^n 像箱内每次重新排列的隔层,使得同一种水果每次封箱外形都不同;而 g^m 则是把明文 m 刻进箱子里的「量杯刻度」。

当两个密文相乘时,(g^{m₁}r₁ⁿ)(g^{m₂}r₂ⁿ) = g^{m₁+m₂}(r₁r₂)ⁿ mod n²,解密时隔层自然抵消,得到 m₁+m₂。解密需要私钥 λ = lcm(p−1, q−1) 和 L 函数:m = L(c^λ mod n²) / L(g^λ mod n²) mod n。安全性基于合数剩余类假设(DCR),2048 位参数下被公认为语义安全。

加法窗口有多实用?老王后来接的投票单子,全靠这只箱子——所有选票隔着箱子累加,开箱只有总和,没人能定位任何一张票的归属。

c=gm⋅rn mod n2,D(c1×c2 mod n2)=m1+m2\displaystyle c = g^m \cdot r^n \bmod n^2, \qquad D(c_1 \times c_2 \bmod n^2) = m_1 + m_2
Paillier 的加法窗口:两个密文相乘后开箱,得到的是明文之和——注意「密文相乘」对应「明文相加」。
  1. n²(大箱体)Paillier 的模数是 n²,明文在 n 以内而密文生活在更大的 n² 空间,这是加法窗口成立的空间基础。
    c∈Zn2∗c \in \mathbb{Z}_{n^2}^{*}
  2. g^m(量杯刻度)公开基数 g 把明文 m 从「数字」变成「箱子里的痕迹」,m 作为指数参与。
    gmg^m
  3. rⁿ(随机隔层)随机盐 r 的 n 次方让相同明文呈现不同密文,是语义安全的关键部件。
    rn mod n2r^n \bmod n^2
  4. 密文相乘 → 明文相加两箱相乘时 g 的指数相加、隔层相乘后在解密时被消掉,最终得到 m₁+m₂。
    gm1+m2(r1r2)n≡c1c2(modn2)g^{m_1+m_2}(r_1 r_2)^n \equiv c_1 c_2 \pmod{n^2}

5. 其他PHE算法

果汁坊的货架上,除了 RSA 和 Paillier 两只爆款,还摆着几款「小众箱型」,各有各的脾气。它们同样只开一扇窗口,但在代数结构、密钥大小、适用场景上各有侧重。

ElGamal 是 1985 年诞生的乘法窗口方案,密文是二元组 (g^r, m·h^r),两个分量都带着随机扰动;两个密文逐项相乘,开箱得到明文乘积,适合盲签名等乘法型协议。Benaloh 是 Paillier 的前身,标准的加法窗口,明文空间被设计得小而精巧,特别适合「0/1 投票」这类小整数累加。NTRU 则基于格理论,加密本质是多项式环上的乘法,天然携带同态潜力,后来的很多全同态方案都从它身上吸取灵感。

选哪只箱子,取决于业务运算类型:要做聚合统计、累计总和,就选加法窗口(Paillier / Benaloh);要做乘积协议、盲签名,就选乘法窗口(ElGamal / RSA)。PHE 的「偏科」不是缺陷,而是一种定位——先确定唯一需要的运算,再选对应的箱子。

货架上的箱子各有分工:加法窗口服务统计,乘法窗口服务乘积。记住一句话:先看业务要什么运算,再挑箱子——选型永远跟着场景走。

E(m)=(gr,  m⋅hr),E(m1)×E(m2)=E(m1×m2)\displaystyle E(m) = (g^r,\; m \cdot h^r), \qquad E(m_1) \times E(m_2) = E(m_1 \times m_2)
ElGamal 把消息藏在第二个分量里:两个密文逐项相乘,开箱后得到明文乘积,是 RSA 之外的另一种乘法窗口。
  1. gʳ(第一分量)用随机 r 扰动生成元,相当于箱子的「随机浮标」,让每次封箱外观不同。
    grg^r
  2. hʳ(遮蔽项)h = gˣ 是公钥,hʳ 把消息 m 罩住,外人看不到箱内真实数字。
    m⋅hrm \cdot h^r
  3. 元组相乘(逐项乘)两个密文 (A, B)×(C, D) 按分量相乘,g 和 h 的指数分别相加。
    (gr1+r2, m1m2hr1+r2)(g^{r_1+r_2},\, m_1 m_2 h^{r_1+r_2})
  4. 幂次抵消(开箱)解密时用私钥 x 把 hʳ 成分消掉,留下明文乘积 m₁m₂。
    m=B⋅A−xm = B \cdot A^{-x}

6. 工程实现要点

磨刀不误砍柴工。真要把果汁坊开成连锁店,你还得算算成本账:密封箱比水果大多少?加工一次耗多少电?钥匙怎么管?把这些账算清,PHE 才能从论文走进生产系统。

首先是参数与空间。Paillier 通常取 2048 位模数 n,明文空间是 n,密文空间是 n²,于是密文长度是明文的两倍——这个「膨胀系数 2」意味着加密成本、带宽、存储都会翻倍。RSA 的模幂运算是 O(log e) 次模乘,Paillier 类似但模数更大,实际吞吐量通常比对称加密低两到三个数量级,这是「可以接受但必须规划」的代价。

工程落地要三管齐下:一是性能优化,对大整数模幂做预计算与批处理,用蒙哥马利约减、CRT 分解等经典技巧提速;二是密钥管理,公钥可公开分发,私钥必须硬件加密存储,实践上常做「工作密钥 + 主密钥」两层分离;三是工具库,Python 生态推荐 python-paillier、phe,C++ 可用微软 SEAL(含 PHE 部分),接入前务必做正确性向量测试与安全审计。

成本账算清后,你会发现 PHE 的「两倍膨胀」其实很划算:一点点带宽换来全程不可见,对于隐私敏感业务是稳赚不赔的买卖。

∣c∣∣m∣=2∣n∣∣n∣=2\displaystyle \frac{|c|}{|m|} = \frac{2|n|}{|n|} = 2
Paillier 密文膨胀两倍:明文是一个 n 长度的数字,密文在 n² 空间里是两倍长,包装比内容大正是安全包装的代价。
  1. |c| 与 |m|(包装与水果)|c| 是密封箱体积(密文位数),|m| 是水果净重(明文位数),二者都以 n 的位数计。
    ∣m∣=∣n∣|m| = |n|
  2. 密文在 n² 空间密文取值范围是 0 到 n²,位数是 2|n|,这就是「大箱子装小水果」。
    ∣c∣=2∣n∣|c| = 2|n|
  3. 膨胀比(成本核算)二者相除得到 2,即密文是明文的两倍长,传输与存储成本翻倍。
    ∣c∣∣m∣=2\frac{|c|}{|m|} = 2

7. 典型应用场景

果汁坊的名声传开了,三张大单接踵而至:全校投票、疾控统计、行业工资普查。它们的共同点是——只要总量,不要明细。而这正是加法窗口最擅长的战场。

这三类需求的解法完全一样:用加法窗口做「密文累加」。每个参与者加密自己的数值得到 E(vᵢ),中心节点把 N 个密文连乘得到一个总密文,交给持私钥的权威方解密,得到总和 Σvᵢ。任何单个明文在传输和计算过程中都以「密封箱」形态存在,单份数据对中心节点也不可见。

展开来说:电子投票中,每张选票加密成 E(vᵢ),vᵢ ∈ {0,1},连乘解密即得赞成票总数;疾控统计中,各医院加密阳性人数,中心汇总出总病例数;工资普查中,企业加密人均工资,行业协会解密得行业均值,单家企业数据不可见。需要提醒的是,PHE 只负责「聚合保密」,不负责「参与方作恶检测」,生产系统通常要搭配数字签名、零知识证明一起使用。

从投票箱到统计报表,「密封箱累加」的套路万变不离其宗:数据可用,但不可见——这正是隐私计算最迷人的地方。

逐字解析结构约束(违反会导致 JSON 转换崩溃):

  • 每个步骤必须含三部分:步骤名(加粗)+ 文字说明(冒号后)+ LaTeX 片段(独立一行 $$...$$)
  • 步骤名不得为空,文字说明 ≥ 10 字
  • LaTeX 片段必须是合法公式,不得为空字符串
  • 禁止跳过步骤名直接写说明,禁止省略 LaTeX 片段

正文约束:

  • 总字数 2000-10000 字
  • 每段 ≥ 80 字
  • 每节遵循四层结构:比喻引入 → 概念过渡 → 技术展开 → 比喻回扣
  • 每个公式必须有逐字解析(步骤数由公式复杂度决定,每步含文字说明 + LaTeX 片段)
  • 公式用 \displaystyle 提升可读性,用 \times 而非 x
  • 专业术语首次出现必须有比喻或通俗解释缓冲
  • 严格遵循思维导图的知识结构,不得超出知识边界
∑i=1Nvi=D(∏i=1NE(vi))\displaystyle \sum_{i=1}^{N} v_i = D\left(\prod_{i=1}^{N} E(v_i)\right)
N 个加密值连乘后再解密,等于 N 个明文之和——这就是「密文累加器」,电子投票和隐私统计的总量计算都靠它。
  1. vᵢ(第 i 份数据)每个参与者加密自己的数值,比如一张选票的 0/1、一家医院的阳性人数。
    vi,i=1,2,…,Nv_i, \quad i = 1, 2, \dots, N
  2. E(vᵢ)(逐份封箱)各方用公钥对各自数据封箱,其他人看到的只是密封箱。
    ci=E(vi)c_i = E(v_i)
  3. Π(密文连乘)中心节点把 N 个密文相乘,完成「隔箱累加」,全程看不到任何单份明文。
    c=∏i=1Ncic = \prod_{i=1}^{N} c_i
  4. D 与 Σ(开箱取总量)只有持私钥的权威方能开箱,得到所有数据的和并对外发布。
    D(c)=∑i=1NviD(c) = \sum_{i=1}^{N} v_i

AI 应用场景

电子投票的密文计票

阶段:数据收集与聚合(隐私保护下的数据计算)

解决的问题:传统电子投票由服务器逐张解密再统计,服务器可信度要求极高,选民隐私完全暴露,甚至存在「看票卖票」的黑色产业。用 Paillier 加法同态后,计票器只对密文做连乘,单票内容零可见,从密码学层面杜绝了投票环节的隐私泄露。

具体案例:某高校学生会选举,1000 名选民每人用公钥投出加密选票 E(vᵢ),计票节点把 1000 个密文连乘后交给监票委员会解密,得到赞成与反对的总数,全程无法定位任何一张票属于谁——就像把 1000 箱果汁倒进大桶,只报总量,不拆任何一箱。

疾控中心的多院区隐私统计

阶段:联邦式特征工程与聚合统计

解决的问题:多家医院不愿共享原始病例数据(涉及患者隐私与合规红线),传统方式只能靠行政命令强制上报,数据质量差且易泄露。加法同态让每家医院只交「密封计数」,疾控中心直接得到总阳性人数,单院数据不可见。

具体案例:5 家医院分别用 Paillier 加密各自院区昨日流感阳性人数,疾控中心把所有密文相乘并解密,得到 5 个院区的总数,任何一家医院的数据都不被他人看到。该结果可直接用于 AI 传染病预警模型的训练标签聚合。

行业协会的匿名工资普查

阶段:多方数据融合与合规发布

解决的问题:行业协会想发布「行业平均薪资」指导线,但企业不愿意暴露自家薪酬水平。加法同态让每家企业只提交加密的薪资总和与人数,协会解密后得到总量并计算均值,单家企业数据不可反推。

具体案例:某市软件行业协会组织 80 家企业参与普查,每家企业加密「薪资总额」与「员工数」,协会对两组密文分别做累加并解密,发布行业平均薪资。这个过程如同一只只密封果汁箱汇入大桶,协会只拿到混合后的总量。

Python 演示

python56 行
import random  def is_prime(x):    if x < 2:        return False    for i in range(2, int(x ** 0.5) + 1):        if x % i == 0:            return False    return True  def gen_prime(bits=8):    while True:        p = random.randrange(2 ** (bits - 1), 2 ** bits)        if is_prime(p):            return p  def keygen():    # crate size n = p * q, public key (n, e), private key (n, d)    p, q = gen_prime(), gen_prime()    while q == p:        q = gen_prime()    n = p * q    e = 65537    d = pow(e, -1, (p - 1) * (q - 1))    return (n, e), (n, d)  def encrypt(pub, m):    # seal: c = m^e mod n    n, e = pub    return pow(m, e, n)  def decrypt(priv, c):    # open: m = c^d mod n    n, d = priv    return pow(c, d, n)  def main():    pub, priv = keygen()    m1, m2 = 7, 11    c1, c2 = encrypt(pub, m1), encrypt(pub, m2)    c_product = (c1 * c2) % pub[0]   # multiply sealed crates    result = decrypt(priv, c_product)    print("plaintext:", m1, m2, " product:", m1 * m2)    print("ciphertext:", c1, c2)    print("decrypt of product:", result)    assert result == m1 * m2  if __name__ == "__main__":    main()
目的:用 Python 完整实现 RSA 部分同态加密,验证「密文相乘再解密 = 明文乘积」这条乘法窗口性质。
  1. d = pow(e, -1, (p - 1) * (q - 1))用扩展欧几里得求私钥指数 d,相当于打磨「钥匙」的齿形
  2. return pow(m, e, n)Python 内置快速模幂,一步完成「封箱」动作
  3. return pow(c, d, n)反向模幂完成「开箱」,与加密动作互为逆运算
  4. c_product = (c1 * c2) % pub[0]同态加工的关键一行:只碰密文、不碰明文
  5. assert result == m1 * m2验证乘法窗口成立,证明 E(m1)×E(m2)=E(m1×m2)

常见误区

误区觉得只要用了同态加密,就能在密文上做加减乘除所有运算,只是速度慢一点而已。于是拿着 Paillier 去算密文乘积,或拿着 RSA 去累加密文,结果得到一堆乱码,还以为是代码写错了。

正解部分同态只支持一种运算——RSA 只开乘法窗口,Paillier 只开加法窗口。想同时做加法和乘法,需要分级同态(SHE)或完全同态(FHE)。就像果汁坊的密封箱,出厂时焊死了唯一一扇窗口,拿加法箱去做乘法,管道根本接不上。选型第一步,永远是明确业务只需要哪种运算。

为何常见:「同态加密」这个名字范围很大,加上 FHE 的新闻传播广,初学者很容易把「部分」二字忽略,默认它能做所有运算。

误区既然叫「加法同态」,那自然就是把两个密文直接加在一起,c1 + c2,开箱得到明文之和。很多初学者的第一版代码都这么写,然后发现解密结果完全不对。

正解Paillier 的加法同态是密文相乘,即 c₁·c₂ mod n²,开箱后得到 m₁+m₂。因为 c₁·c₂ = g^{m₁+m₂}(r₁r₂)ⁿ,随机隔层在解密时抵消。「密文上的某个操作对应明文加法」才是同态的定义,而这个操作不一定是表面上的加号。

为何常见:名字里带「加法」二字,加上直觉上「密文加法对应明文加法」很像一回事,望文生义就踩了坑。

误区Paillier 加密里的随机盐 r 看着没用,开箱后反正会被消掉,不如直接省略,还能少算一次模幂,让加密快一倍。

正解r 是语义安全的核心。没有 r 的 Paillier 变成确定性加密:相同明文永远产生相同密文,攻击者用频率分析就能猜出明文分布。就像果汁箱没有随机隔层,两箱同样的水果外观完全一致,外人一眼就能认出「这箱和那箱是同一种」。r 不改变解密结果,但决定了密文能不能守住秘密。

为何常见:初学者追求性能,往往先砍掉「看不懂用途」的部件,而语义安全这类理论性质不像正确性那样能立刻暴露问题。

误区RSA 部分同态实现简单,Python 几行就能跑通,而且不需要额外签名、填充这些「多余」的步骤,直接把密文乘法写进业务代码上线。

正解裸 RSA 存在密文可塑性:攻击者可以把密文 c 替换成 c·r^e,受害者解出的结果变成 m·r,整个统计被悄悄篡改。标准做法是加 OAEP 填充,但填充又会破坏同态性质——所以生产环境做密文累加,通常选 Paillier 这类加法同态方案,而不是裸 RSA。

为何常见:教科书示例和工程安全之间存在巨大落差,而可塑性攻击不像私钥泄露那样直观,很容易被忽略。

面试题

Q1. 部分同态加密与传统加密最大的区别是什么?

单选题 · easy

  • A.密文上可以直接运算← 正确答案
  • B.密文可无限次计算
  • C.密文能转为明文使用
  • D.密文完全不需要密钥

答案:A。部分同态加密允许在密文上直接完成一种计算(如加法或乘法),这是传统加密做不到的。B 是 FHE 的特性,C 违背加密目的,D 更是无稽之谈——就像果汁加工坊可以隔着箱子倒果汁,但不代表箱子不需要锁。

Q2. RSA 两个密文相乘并解密,结果等于什么?

单选题 · easy

  • A.两个明文之积← 正确答案
  • B.两个明文之和
  • C.两个明文之差
  • D.两个明文之商

答案:A。RSA 具有乘法同态性质 E(m₁)×E(m₂)=E(m₁×m₂),因为 m₁^e × m₂^e = (m₁×m₂)^e mod n。这是 RSA 那只「乘法窗口」密封箱的核心性质,其余选项都没有对应的同态关系。

Q3. Paillier 想要得到明文之和,需要对两个密文做什么操作?

单选题 · mid

  • A.两个密文做乘方
  • B.两个密文直接相加
  • C.两个密文做乘积← 正确答案
  • D.两个密文做除法

答案:C。Paillier 是加法同态,但对应操作是密文相乘:c₁×c₂ mod n² 开箱后得到 m₁+m₂。很多初学者误以为「加法同态=密文相加」,实际上密文空间的「加」由模乘实现,就像密封箱之间的加工动作是拼接管道而不是把箱子砸在一起。

Q4. Paillier 加密中引入随机数 r 的核心目的是什么?

单选题 · mid

  • A.提升加密算法的速度
  • B.保证相同明文密文不同← 正确答案
  • C.用于替代公钥加密
  • D.用于压缩密文体积

答案:B。随机数 r 构成密文中的「随机隔层」,让相同明文每次加密产生不同密文,这是语义安全的前提。没有 r,攻击者就能用频率分析猜出明文。r 不提升速度、不替代公钥、更不压缩密文,它的唯一使命是让箱子「同果不同相」。

Q5. Paillier 的密文长度约为明文长度的多少倍?

单选题 · mid

  • A.大约两倍← 正确答案
  • B.大约四倍
  • C.大约八倍
  • D.完全相同

答案:A。Paillier 明文在 Zₙ 空间,密文在 Z_{n²} 空间,密文位数为 2|n|,而明文位数为 |n|,所以膨胀系数为 2。这是「大箱子装小水果」的安全包装代价,工程上要为此规划带宽和存储。

Q6. 裸 RSA 同态加密在生产环境中要警惕哪些问题?

多选题 · hard

  • A.密文可塑性易被利用← 正确答案
  • B.无法进行乘法同态计算
  • C.难防选择密文攻击← 正确答案
  • D.无法生成任何密文

答案:AC。裸 RSA 的乘法窗口是双刃剑:攻击者可以把密文 c 改成 c·r^e,骗受害者解出 m·r,这就是密文可塑性(A);同时没有填充的 RSA 对选择密文攻击几乎没有防御力(C)。B 恰恰相反,RSA 本来就能做乘法同态;D 更是完全错误。

Q7. ElGamal 加密输出的密文结构是什么?

单选题 · mid

  • A.一个生成元和一个元素
  • B.两个元素组成的元组← 正确答案
  • C.三个大素数的乘积
  • D.一个公开基数的幂

答案:B。ElGamal 密文是二元组 (g^r, m·h^r):第一个分量是随机扰动后的生成元,第二个分量是消息与遮蔽项的乘积。两个密文逐项相乘再解密,得到明文乘积——这是 RSA 之外另一种乘法窗口的箱子。

Q8. 按同态计算能力划分,同态加密可分为哪些类型?

多选题 · easy

  • A.部分同态加密← 正确答案
  • B.分级同态加密← 正确答案
  • C.完全同态加密← 正确答案
  • D.对称同态加密

答案:ABC。按能力从弱到强分为部分同态(PHE,只支持一种运算)、分级同态(SHE,支持有限深度运算)、完全同态(FHE,支持任意运算)。「对称同态加密」是把「对称/非对称」这个加密维度混进来了,不是按同态能力划分的类别。

Q9. 电子投票中利用加法同态累加选票,正确做法是什么?

单选题 · hard

  • A.将加密选票直接相加
  • B.将选票做乘方运算
  • C.将加密选票相乘后解密← 正确答案
  • D.将选票明文公开统计

答案:C。Paillier 加法同态的正确用法是密文连乘:计票器计算 ∏E(vᵢ),再把总密文交给持私钥的监票方解密,得到 Σvᵢ。A 选项混淆了「密文相加与明文相加」;B 无意义;D 直接公开明文,完全失去隐私保护的意义。

Q10. 关于 PHE 与 FHE 的比较,下列说法正确的有哪些?

多选题 · extreme

  • A.FHE 支持任意计算← 正确答案
  • B.PHE 只支持一种运算← 正确答案
  • C.FHE 噪声管理更难← 正确答案
  • D.PHE 性能必定更慢

答案:ABC。FHE 支持任意计算但需要处理噪声累积,难度远高于 PHE;PHE 只开一扇窗口,噪声问题基本可控,所以性能通常远好于 FHE,D 说反了。选型时要记住:FHE 是「万能窗口但很贵」,PHE 是「单窗口但便宜好用」,按业务需求取舍。