Caloogy 文章
English

信封加密:非对称只封钥匙,对称负责数据

日期
2025 年 3 月 2 日
系列
密码学
阅读
4 分钟
主题
Envelope encryptionHybrid encryptionHpkeEcdh

一句话版本

实际的加密系统几乎从不直接用 RSA 或椭圆曲线去加密一整个文件,因为它们又慢、又有长度限制。标准做法是混合加密,也叫信封加密:随机生成一把对称密钥加密数据(快),再用接收者的公钥把这把对称密钥"封"起来(安全地交给对方)。 它由三个零件组成:非对称(交付钥匙)、对称(加密数据并防篡改)、哈希 / 密钥派生(把共享秘密变成合格的钥匙)。HTTPS、PGP 邮件、云服务的 KMS、libsodium 的 sealed box,结构都是这样。

全文分三层:

  1. 通俗理解:把钥匙放进信封
  2. 实际用处:云 KMS、PGP、HPKE、浏览器端加密
  3. 原理与工程要点:ECDH + HKDF + AEAD 的完整流程、大文件分块、常见错误

第一层:通俗理解

1.1 把钥匙放进信封

你要寄一箱贵重物品给朋友:

  1. 买一把新挂锁,把箱子锁上(对称加密数据,又快又能锁大东西);
  2. 把这把挂锁的钥匙放进一个只有朋友能拆的特制信封(用朋友的公钥加密这把小钥匙);
  3. 箱子和信封一起寄出。

朋友拆开信封拿到钥匙,再打开箱子。途中任何人都拿不到钥匙,也就打不开箱子。

1.2 为什么不直接用公钥加密整个文件

非对称(RSA、椭圆曲线) 对称(AES、ChaCha20)
速度 慢,每次运算都是大数或曲线运算 极快,有硬件加速
能加密多长 RSA 一次只能加密比密钥短的数据 任意长
擅长 让陌生人之间安全地交付一个小秘密 加密大量数据

所以各取所长:非对称只负责那几十个字节的钥匙,对称负责剩下的一切。


第二层:实际用处

场景 怎么用信封加密
云服务 KMS(AWS KMS、Google Cloud KMS 等) 数据用"数据密钥"(DEK)加密,DEK 再用 KMS 里的"主密钥"(KEK)加密后和数据存在一起。主密钥从不离开 KMS
PGP / GPG 邮件 随机会话密钥加密邮件正文,再用每个收件人的公钥分别加密会话密钥
HTTPS 握手时用(EC)DH 协商出共享秘密,派生出对称密钥加密之后的流量
HPKE(RFC 9180,2022) 把"KEM + 密钥派生 + AEAD"标准化成一个通用构件,被 TLS 的加密客户端问候(ECH)、MLS 群聊协议等采用
浏览器端加密上传 浏览器用服务器所有者的公钥加密文件后上传,服务器只存密文,只有持有私钥的人能解开

轮换主密钥变得很便宜:换主密钥时,只要把每个 DEK 重新封一次(每个只有几十字节),不需要把 TB 级的数据全部重新加密。这是云服务普遍采用信封加密的重要原因。


第三层:原理与工程要点

3.1 基于 ECDH 的完整流程

接收者事先生成一对长期密钥:私钥 xx,公钥 X=xGX = xG。

发送方:

  1. 生成一对临时密钥 yy、Y=yGY = yG;
  2. 计算共享秘密 S=yX=xyGS = yX = xyG(ECDH,见《Diffie–Hellman与离散对数》、椭圆曲线);
  3. 用 HKDF 从 SS 派生出对称密钥 KK;
  4. 用 AEAD(如 AES-GCM、ChaCha20-Poly1305)和随机 nonce 加密数据,得到密文和认证标签;
  5. 发送 (Y, nonce, 密文)(Y,\ \text{nonce},\ \text{密文})。

接收方:用自己的私钥算 xY=xyG=SxY = xyG = S,派生出同一个 KK,解密并验证标签。

只看到 XX 和 YY 的旁观者,要算出 xyGxyG 就得解椭圆曲线离散对数问题。

3.2 为什么需要 HKDF

ECDH 得到的共享秘密 SS 是曲线上一个点的坐标,它不是均匀随机的比特串(比如它一定满足曲线方程),不能直接当 AES 密钥。HKDF 分两步:

info 参数还能做域分离:同一个共享秘密,给不同用途派生出互不相关的密钥。

3.3 为什么要 AEAD

只加密不认证是不够的:攻击者虽然读不懂密文,但可能改动它,让解密结果按他的意图变化(见《一次一密》 里的可延展性)。AEAD(带关联数据的认证加密)在加密的同时生成一个认证标签,密文被改动一个比特,解密就会直接失败。

3.4 大文件:分块加密

把一个几 GB 的录音一次性读进内存加密不现实。标准做法:

  1. 随机生成一把内容密钥,用信封方式封起来;
  2. 用这把内容密钥对文件分块做 AEAD,每块带序号,最后一块带"结束"标记;
  3. 序号和结束标记防止攻击者重排、删除或截断数据块。

libsodium 的 secretstream 接口就是这样设计的,可以边录边加密。

3.5 常见错误

错误 后果
同一把密钥下重复使用 nonce(AES-GCM) 灾难性:两条明文的异或泄露,认证也能被伪造
用 RSA 但不加 OAEP 填充 教科书式 RSA 可被多种方式攻破(见《RSA与欧拉定理》)
共享秘密直接当密钥,不做 KDF 密钥分布不均匀,也没有域分离
只加密不认证 密文可被篡改
不检查对方公钥是否合法 小子群攻击(见《Diffie–Hellman与离散对数》)

所以实践中最稳妥的选择是使用高层封装(libsodium 的 sealed box、HPKE 库、云 KMS 的 SDK),而不是自己拼装底层原语。

信封加密不自动带来前向保密

如果接收方始终用同一把长期私钥 xx,那么 xx 一旦泄露,所有历史上收到的信封都能被打开。这是"必须能事后解密"的存储场景的固有代价,详见《前向保密》。


总结

层次 要点
直觉 对称锁箱子,非对称封钥匙
三个零件 非对称(ECDH / RSA-OAEP)、KDF(HKDF)、AEAD(AES-GCM / ChaCha20-Poly1305)
好处 快、能加密任意长度、换主密钥只需重封 DEK
实践 用高层封装;nonce 绝不重复;大文件分块并防截断