Day 148 — 对称与非对称加密:AES、RSA 与混合加密
阶段:Phase 7 — 进阶与性能优化 · 主题:实战密码学
前置知识:Day 147 学了哈希与 HMAC(解决完整性 / 不可否认)。今天进入另一个方向——机密性(Confidentiality):数据要在不可信信道上传输,怎么保证别人看不到。理解了 AES / RSA / 混合加密,你才看得懂 TLS、VPN、加密聊天软件、JWT 签名背后到底发生了什么。
一、概念解释
1.1 对称加密(Symmetric Encryption)
定义:加密和解密使用同一把密钥的加密方式。
明文 + 密钥 K ──[加密]──► 密文 ──[解密]──► 明文(同一个密钥 K)
代表算法:
| 算法 | 密钥长度 | 分组长度 | 状态 | 说明 |
|---|---|---|---|---|
| DES | 56 bit | 64 bit | ❌ 已淘汰 | 密钥太短,暴力可破 |
| 3DES | 112/168 bit | 64 bit | ⚠️ 逐步淘汰 | 性能差、Sweet32 攻击 |
| AES | 128/192/256 bit | 128 bit | ✅ 主流 | 硬件加速,业界标准 |
| ChaCha20 | 256 bit | 流式 | ✅ 主流 | 无 AES 硬件时的首选(移动端) |
| SM4 | 128 bit | 128 bit | ✅ 国密 | 国内合规场景 |
为什么它快? 加密算法本身只做查表、移位、异或这类按位运算,且可以
硬件指令加速(x86 的 AES-NI)。实测 AES-GCM 在现代 CPU 上可达 1~5 GB/s。
它解决不了的问题:密钥分发。A 和 B 隔着不可信网络,怎么让两人拥有
同一把密钥?如果先能安全传密钥,那干脆用它传数据就好了——这就是死循环。
非对称加密负责打破这个死循环。
1.2 非对称加密(Asymmetric / Public-Key Encryption)
定义:使用一对数学关联的密钥——公开的公钥(public key)和保密的
私钥(private key)。公钥加密的数据,只有对应私钥能解;私钥签名的数据,
任何持有公钥的人都能验证。
| 算法 | 安全基础 | 典型密钥长度 | 特点 |
|---|---|---|---|
| RSA | 大整数分解难题 | 2048 / 3072 / 4096 bit | 最通用,兼容性好,慢 |
| ECC (ECDSA/ECDH) | 椭圆曲线离散对数 | 256 bit | 同等安全下密钥更短、更快 |
| Ed25519 | 爱德华兹曲线 | 256 bit | 现代签名首选,快且抗侧信道 |
| DH / ECDHE | 离散对数 | — | 用于密钥协商,不用于加密数据 |
为什么它慢? 运算对象是上千位的大整数,核心操作是模幂(m^e mod n),
大量大数乘法和取模,比 AES 的按位操作慢几个数量级(通常几十到上千倍)。
两大用途必须分清:
① 加密(保证机密性): 公钥加密 ──► 私钥解密
目标:任何人都能发消息给我,但只有我能看
② 签名(保证身份+完整性): 私钥签名 ──► 公钥验签
目标:证明"这条消息确实来自私钥持有者",且未被篡改
常见误区:"用私钥加密、公钥解密" 这种说法容易误导。正确表述是
签名 = 用私钥对消息摘要做运算,公钥可以验证。因为公钥是公开的,
用"私钥加密"得到的密文谁都能解开,根本谈不上保密。
1.3 混合加密(Hybrid Encryption)
工业界从不单用 RSA 加密业务数据,而是混合:
① 随机生成一次性对称密钥 K(会话密钥,如 32 字节)
② 用接收方公钥加密 K → 解决"密钥分发"(非对称的强项)
③ 用 K 加密真正的数据(AES-GCM)→ 解决"高速大批量"(对称的强项)
④ 接收方用私钥解出 K,再解密数据
TLS/HTTPS、PGP、Signal、VPN 全是这个套路。记住一句话:
非对称加密只用来搬运密钥和做签名,真正的数据永远交给对称加密。
1.4 三个概念别混淆:编码 / 哈希 / 加密
| 名称 | 可逆? | 有密钥? | 目的 |
|---|---|---|---|
| Base64 编码 | ✅ 可逆 | ❌ 无 | 把二进制塞进文本协议,不是安全措施 |
| 哈希(SHA-256) | ❌ 不可逆 | ❌ 无 | 完整性校验、指纹 |
| HMAC | ❌ 不可逆 | ✅ 有 | 带密钥的完整性/认证(Day 147) |
| 对称加密 | ✅ 可逆 | ✅ 有 | 机密性 |
| 非对称加密 | ✅ 可逆 | ✅ 有(一对) | 机密性 + 密钥分发 |
| 数字签名 | — | ✅ 有 | 身份认证 + 完整性 + 不可否认 |
⚠️ 把 Base64 当加密用是新手最常见的笑话:
base64.b64encode(b"password")的结果cGFzc3dvcmQ=任何人都能解。
1.5 分组密码的五个关键术语
- 分组(Block):AES 一次处理的固定长度,恒为 128 bit(16 字节)。
- 密钥长度:AES-128/192/256,只影响轮数(10/12/14 轮),不改分组大小。
- 模式(Mode):如何把多个分组串起来加密,决定安全性(见 2.2)。
- IV / nonce:初始向量 / 一次性数字,保证"同样的明文每次加密结果不同"。
- 填充(Padding):明文长度不是分组整数倍时补齐到整数倍(见 2.3)。
为什么 AES 分组是 128 bit 不变? 因为 SubBytes/ShiftRows/MixColumns 这一套
"状态矩阵"运算天然是 16 字节的结构(4×4 字节矩阵);加大分组会让安全性
提升有限而性能骤降,所以设计者选择用更多轮数(更长的密钥)来提升强度。
二、原理深入
2.1 AES 内部结构(为什么它安全且可逆)
AES 把 16 字节明文排成 4×4 的"状态矩阵",每轮做四个变换:
flowchart TB
P[明文 16 字节] --> AK0[AddRoundKey: 与第0轮子密钥异或]
AK0 --> R{重复 9/11/13 轮}
R --> SB[SubBytes: 逐字节 S盒非线性替换]
SB --> SR[ShiftRows: 每行循环左移 0/1/2/3]
SR --> MC[MixColumns: 列混淆 4x4 矩阵乘法]
MC --> AK[AddRoundKey: 异或本轮子密钥]
AK --> R
R --> F[最后一轮: SubBytes/ShiftRows/AddRoundKey 无 MixColumns]
F --> C[密文 16 字节]
KS[密钥扩展 Key Schedule] -.子密钥.-> AK0
KS -.子密钥.-> AK
四个变换各自的作用(为什么是这四个):
- SubBytes:唯一的非线性步骤(S 盒)。没有它,整个密码就是线性
方程组,能被直接解出密钥。它提供"混淆(confusion)"。 - ShiftRows:把字节在列之间打散,保证一个字节的影响扩散到多列。
- MixColumns:列内线性混合,提供"扩散(diffusion)"——一位变化迅速
影响整块。 - AddRoundKey:把密钥真正"掺"进去,否则算法与密钥无关。
密钥扩展:从 128/192/256 bit 主密钥推导出每轮一把子密钥。这也是为什么
密钥越长为轮数越多(10/12/14 轮),而不是改分组长度。
AES 的逆运算正是把上述步骤逆序 + 用逆 S 盒 / 逆 ShiftRows / 逆 MixColumns,
所以能解密(这是"对称"的底层保证)。
2.2 分组模式:ECB / CBC / CTR / GCM
分组密码只能处理固定 16 字节,现实数据远超 16 字节,于是有了"模式"。
ECB(Electronic Codebook)— ❌ 千万不要用
独立加密每个分组: C_i = AES(P_i)
问题:相同明文分组 → 相同密文分组。加密一张位图后仍能看清轮廓("ECB 企鹅")。
它泄露的是数据的结构,而这往往就是最敏感的信息。
CBC(Cipher Block Chaining)— ⚠️ 可用但需谨慎
C_1 = AES(P_1 XOR IV)
C_i = AES(P_i XOR C_{i-1})
每个明文分组先与前一个密文分组异或,破坏了"相同明文→相同密文"。
要求:IV 必须随机且不可预测,且每次加密换新 IV。
缺点:只保证机密性,不保证完整性(可被 bit-flipping 篡改),且不能并行加密。
CTR(Counter)— ✅ 流式,可并行
C_i = P_i XOR AES(nonce || counter_i)
把分组密码当流密码用:加密一串计数器,再与明文异或。优点:可并行、
可随机访问、无填充。缺点:nonce+counter 组合绝不能重复,且同样不带完整性。
GCM(Galois/Counter Mode)— ✅ 现代默认
GCM = CTR 模式加密 + GHASH 认证标签
它是 AEAD(Authenticated Encryption with Associated Data):
一次运算同时给出机密性 + 完整性,还能附带 AAD(不加密但受保护的元数据)。
只要 tag 校验不过,解密直接失败——不会把被篡改的数据交给你。
这就是 TLS 1.3 里只剩 AES-GCM / ChaCha20-Poly1305 的原因。
| 模式 | 机密性 | 完整性 | 可并行 | 推荐度 |
|---|---|---|---|---|
| ECB | ❌ 泄露结构 | ❌ | ✅ | 🚫 禁止 |
| CBC | ✅ | ❌ | ❌ | ⚠️ 仅配合 HMAC(Encrypt-then-MAC) |
| CTR | ✅ | ❌ | ✅ | ⚠️ 需自行加 MAC |
| GCM | ✅ | ✅ | ✅ | ✅ 推荐 |
| ChaCha20-Poly1305 | ✅ | ✅ | ✅ | ✅ 无 AES 硬件时推荐 |
2.3 填充(Padding)与 PKCS#7
CBC 要求明文长度是 16 的整数倍。PKCS#7 规则:缺 n 字节就补 n 个值为 n
的字节;若刚好整除,则补满一整块 16 个 0x10。
"HELLO"(5字节) → 补 11 个 0x0B → 16 字节
16 字节整块 → 补 16 个 0x10 → 32 字节(必须补!否则无法区分"已去掉填充")
Padding Oracle 攻击:如果服务端对"填充错误"和"内容错误"返回不同提示,
攻击者可以逐字节解出明文。这就是为什么 CBC 时代要格外小心,也是 GCM 这种
AEAD 更安全的原因之一。
2.4 RSA 的数学原理
密钥生成:
选两个大素数 p, q(2048 bit 密钥 ⇒ p、q 各约 1024 bit)
n = p * q (公开的"模数")
φ(n) = (p-1)(q-1)
选 e 与 φ(n) 互素(惯例 65537)
求 d 使 e * d ≡ 1 (mod φ(n))
公钥 = (n, e) 私钥 = (n, d)
加密: c = m^e mod n
解密: m = c^d mod n
为什么安全:攻击者知道 n 和 e,想求 d 必须先分解 n 得到 p、q。而
2048-bit 的大整数分解在现有算力下不可行。为什么 e 常取 65537:
它是 2^16+1,二进制只有两个 1,模幂运算快,且足够大以避免小指数攻击。
为什么"教科书 RSA"不安全:
- 确定性:相同明文 + 相同公钥 → 相同密文(可被枚举比对)。
- 小消息可开方:若
m^e < n,则c = m^e,直接对 c 开 e 次方即得 m。 - 可乘性:
E(m1)*E(m2) = E(m1*m2),攻击者可构造定向密文。
填充救场:OAEP(加密用)在明文前掺入随机数并做 Feistel 变换,让同一明文
每次密文都不同;PSS(签名用)用随机盐保证签名不可伪造、不可复用。
2.5 密钥协商与前向保密(Forward Secrecy)
RSA 封装密钥有个隐患:服务器私钥一旦泄露,攻击者录下的历史流量全被解开。
Diffie-Hellman(DH)解决这个:
sequenceDiagram
participant A as 客户端
participant B as 服务器
A->>B: A = g^a mod p (公开)
B->>A: B = g^b mod p (公开)
Note over A: K = B^a mod p
Note over B: K = A^b mod p
Note over A,B: 双方算出同一个 K,窃听者只知道 A、B,算不出 K(离散对数难题)
现代 TLS 用 ECDHE(椭圆曲线临时 DH):每次连接生成一次性密钥对,
用完即弃。这样即使长期私钥泄露,也解不开历史会话——即"前向保密"。
Day 149 学的 JWT 与 Day 148 的这套密码学知识,是同一批底层工具的两种应用。
2.6 为什么非对称加密慢:数量级对比
| 操作 | 相对速度 | 原因 |
|---|---|---|
| AES-256-GCM 加解密 | 1× (基准) | 按位运算 + 硬件加速 |
| RSA-2048 加密(公钥运算) | ~1000× 慢 | 大数模幂,e=65537 已优化 |
| RSA-2048 解密(私钥运算) | ~3000× 慢 | d 是 2048 bit,全长度模幂 |
| ECC-256 运算 | ~100× 慢 | 椭圆曲线点乘 |
正是这个数量级差,逼出了混合加密这一必然方案。
三、常见陷阱与避坑指南
3.1 用 ECB 模式加密(结构性泄露)
# ❌ 错误:ECB 会让相同明文块产生相同密文块
cipher = Cipher(algorithms.AES(key), modes.ECB())
后果:加密图片能看出轮廓,加密数据库字段能看出哪些行值相同。
正确做法:用 AESGCM(推荐)或 CBC + 随机 IV。
3.2 IV / nonce 重用(最致命的一类错误)
- CBC 的 IV 可预测:固定 IV 或"时间戳当 IV",会让相同明文前缀产生相同
密文前缀,泄露信息(BEAST 攻击)。 - GCM 的 nonce 重用是毁灭性的:同一个 (key, nonce) 加密两条消息,
两条密文异或 = 两条明文异或(直接泄露明文关系),更严重的是可以恢复出
认证密钥 H,从而伪造任意消息的 tag。
# ❌ 错误:nonce 写死 / 每次用同一个
aesgcm.encrypt(b"\x00" * 12, data, None)
# ✅ 正确:每次加密都生成新 nonce
nonce = os.urandom(12)
纪律:自己管 nonce 时用随机 12 字节(碰撞概率极低)或严格单调计数器;
用 AESGCM 时把 nonce 与密文一起存储/传输。
3.3 用"教科书 RSA"(无填充)
# ❌ 错误:确定性加密 + 可乘性 + 小消息可开方
c = pow(int.from_bytes(msg, "big"), e, n)
# ✅ 正确:必须带 OAEP 填充
public_key.encrypt(msg, padding.OAEP(mgf=..., algorithm=SHA256(), label=None))
3.4 拿 RSA 加密大文件 / 超长消息
2048-bit 密钥 + SHA-256 的 OAEP 上限约 190 字节。超了会直接报
ValueError: Encryption failed。正确姿势是用混合加密(见 code/03)。
3.5 用 == 比较密钥、tag、HMAC
字符串/字节比较会在第一个不同字节处提前返回,攻击者可据耗时逐字节猜测
(时序攻击)。必须用常数时间比较:
import hmac
hmac.compare_digest(a, b) # ✅
a == b # ❌ 用于安全比较
3.6 硬编码密钥 / 把密钥提交进 Git
KEY = b"1234567890123456" # ❌ 会被 git 永远记住
正确做法:密钥放环境变量、KMS、Vault 或密钥管理服务;用 .gitignore
排除密钥文件;一旦泄露必须轮换密钥(历史提交无法真正删除)。
3.7 自己发明加密方案 / 自造填充
"我用 AES 加密,再把结果倒序一下,再加个盐,肯定更安全" —— 这类
自制密码学(Roll Your Own Crypto) 是安全事故高发区。用经过审计的
库与标准组合:AESGCM、ChaCha20Poly1305、Fernet。
3.8 把编码当加密
base64、hex、urlencode 全部可逆且无密钥,不能保护任何机密。
它们只用于让二进制适配文本协议。
3.9 加密了却忘了认证(Encrypt-then-MAC 顺序)
CBC 加密不防篡改。若必须用 CBC,应"先加密、后对密文做 HMAC"(
Encrypt-then-MAC),并先验 MAC 再解密。顺序颠倒会引入 Padding Oracle。
GCM 已经内置了这个顺序,所以优先选 GCM。
3.10 忽略随机源质量
用 random 模块生成密钥/IV 是灾难(伪随机,种子可预测)。
密钥、IV、盐一律用 os.urandom() 或 secrets 模块。
import secrets
key = secrets.token_bytes(32) # ✅ 密码学安全
四、定义与使用方法(API 速查表)
本节所有 API 来自
cryptography库(pip install cryptography),它是
Python 生态事实标准的密码学库,底层用 OpenSSL/Rust 实现,不要自己
用纯 Python 手写 AES/RSA。
4.1 对称加密 AESGCM
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# 生成密钥
key = AESGCM.generate_key(bit_length=256) # 返回 32 字节 bytes
# 或 key = os.urandom(32)
aes = AESGCM(key)
nonce = os.urandom(12) # 12 字节,每次加密都要新
ct = aes.encrypt(nonce, plaintext_bytes, aad) # aad 可为 None 或 bytes
pt = aes.decrypt(nonce, ct, aad) # 篡改/密钥错 → InvalidTag
| 方法 | 参数 | 返回 | 说明 |
|---|---|---|---|
AESGCM.generate_key(bit_length) |
128/192/256 | bytes |
生成随机密钥 |
AESGCM(key) |
32/24/16 字节 | 实例 | key 长度不对会报错 |
.encrypt(nonce, data, aad) |
nonce 8~128B(推荐 12) | bytes |
输出 = 密文 + 16B tag |
.decrypt(nonce, data, aad) |
同上 | bytes |
失败抛 InvalidTag |
4.2 ChaCha20-Poly1305(无 AES 硬件时的替代)
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
chacha = ChaCha20Poly1305(ChaCha20Poly1305.generate_key())
ct = chacha.encrypt(os.urandom(12), b"data", b"aad")
4.3 RSA 密钥生成与序列化
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
# 导出 PEM
pem_pub = public_key.public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo)
pem_priv = private_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.BestAvailableEncryption(b"passphrase")) # 口令保护
# 从 PEM 加载
pub = serialization.load_pem_public_key(pem_pub)
priv = serialization.load_pem_private_key(pem_priv, password=b"passphrase")
4.4 RSA 加密(OAEP)与签名(PSS)
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
OAEP = padding.OAEP(mgf=padding.MGF1(hashes.SHA256()),
algorithm=hashes.SHA256(), label=None)
PSS = padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH)
ct = public_key.encrypt(b"short msg", OAEP) # ≤ 190 字节 (2048+SHA256)
pt = private_key.decrypt(ct, OAEP)
sig = private_key.sign(b"doc", PSS, hashes.SHA256()) # 私钥签名
public_key.verify(sig, b"doc", PSS, hashes.SHA256()) # 公钥验签,失败抛 InvalidSignature
4.5 常用填充别名
from cryptography.hazmat.primitives import padding as sym_padding
padder = sym_padding.PKCS7(128).padder() # 128 bit = 16 字节分组
4.6 选型速查
| 需求 | 推荐 API |
|---|---|
| 加密任意数据(同时要完整性) | AESGCM / ChaCha20Poly1305 |
| 加密会话密钥 / 短消息 | RSA-OAEP 或 ECIES |
| 数字签名 | RSA-PSS(兼容) / Ed25519(现代) |
| 密钥协商 | ECDH / X25519 |
| 密码存储 | pbkdf2_hmac / scrypt / Argon2(Day 147) |
| 完整性校验(无密钥) | hashlib.sha256(Day 147) |
4.7 官方文档入口
https://cryptography.io/en/latest/hazmat/primitives/- 选模式时先看 "Authenticated encryption" 章节
五、图解(Mermaid / ASCII)
5.1 两种加密的密钥流向
flowchart LR
subgraph SYM[对称加密 AES]
K1[同一把密钥 K] --> E1[加密] --> C1[密文]
K1 --> D1[解密] --> P1[明文]
end
subgraph ASYM[非对称加密 RSA]
PU[公钥 - 可公开] --> E2[加密] --> C2[密文]
PR[私钥 - 保密] --> D2[解密] --> P2[明文]
end
5.2 混合加密时序
sequenceDiagram
participant C as 客户端
participant S as 服务器
C->>C: 生成一次性会话密钥 K
C->>S: RSA_OAEP(服务器公钥, K)
C->>S: AES_GCM(K, 业务数据)
S->>S: 私钥解出 K → 解密数据
S-->>C: 后续通信都走 AES(K)
Note over C,S: RSA 只搬 32 字节密钥,数据全走 AES
5.3 AEAD 密文结构
nonce(12B) ciphertext(与明文等长) tag(16B)
┌──────────┬───────────────────────────────┬──────────────┐
│ 随机数 │ 加密后的数据 │ 认证标签 │
└──────────┴───────────────────────────────┴──────────────┘
▲ ▲ ▲
可公开传输 AAD 参与认证但不加密 篡改任一字节
→ tag 校验失败
更完整的图解(ECB 对比、RSA 数学直觉、选型决策树)见
diagrams/README.md。
六、实战代码案例
本日三个可运行示例位于 code/,逐层递进(每个文件都能直接
python3 xxx.py 运行,需要 pip install cryptography):
| 文件 | 类型 | 内容 |
|---|---|---|
01-aes-gcm-basics.py |
基础用法 | AES-256-GCM 加解密、AAD 认证、篡改检测、nonce 重用演示 |
02-rsa-asymmetric.py |
进阶 / 避坑 | RSA 密钥对生成与 PEM 序列化、OAEP 加解密、长度上限、PSS 签名验签 |
03-hybrid-secure-channel.py |
实战 | 混合加密安全通信工具:客户端/服务器类、密钥封装、签名、篡改与伪造攻击演示 |
6.1 01 — 基础:把一次加密做对
演示了正确姿势的完整闭环:os.urandom(12) 生成 nonce → AESGCM.encrypt
→ 传输 (nonce, 密文) → AESGCM.decrypt。并连续演示三种篡改(翻密文位、
改 AAD、换密钥)全部被 InvalidTag 拦下——这是 GCM 相比 CBC 的核心价值。
6.2 02 — 进阶:RSA 的正确边界
关键代码与结论:
max_len = 256 - 2 * 32 - 2 # 2048bit 密钥 + SHA-256 ⇒ 190 字节
public_key.encrypt(b"A" * 191, OAEP) # ValueError: Encryption failed
02 用一个真实的 ValueError 让你记住:RSA 装不下业务数据。同时对比
"公钥加密→私钥解密"(机密性)与"私钥签名→公钥验证"(身份+完整性)两条
方向不同的链路。
6.3 03 — 实战:安全通信工具
模拟 TLS 的简化版,SecureServer / SecureClient 两个类完整走通:
客户端 服务器
│ 生成会话密钥 K ── RSA_OAEP(服务器公钥, K) ──► 私钥解出 K
│ AES_GCM(K, 消息) + nonce ─────────────────► 用 K 解密
│ RSA_PSS(客户端私钥, 密文) ─────────────────► 用登记的客户端公钥验签
03 还内置了两场攻击演示:
- 篡改密文 → 验签/解密失败;
- 攻击者自带密钥伪造消息 → 因公钥不在服务器信任名单内被拒。
这解释了真实系统为什么要"证书固定 / CA 信任链":光有加密不够,
还必须确认"你手里的公钥真的是对方的"——否则中间人可以直接换掉公钥。
6.4 运行方式
cd days/day-148-symmetric-asymmetric-encryption/code
pip install cryptography
python3 01-aes-gcm-basics.py
python3 02-rsa-asymmetric.py
python3 03-hybrid-secure-channel.py
七、思考题
-
为什么 AES 分组长度固定 128 bit,但密钥可以是 128/192/256 bit?
密钥变长改变的是轮数还是分组?这样做的好处是什么? -
GCM 的 nonce 只有 12 字节,如果高并发系统每秒加密上百万条消息,
随机 nonce 会不会碰撞? 请从生日悖论角度估算碰撞概率,并说明工业界
如何避免(提示:计数器 / 随机 96 位 / 密钥轮换)。 -
RSA 公钥加密没有前向保密,为什么现代 TLS 1.3 干脆删掉了 RSA 密钥
交换,只保留 (EC)DHE? 如果服务器私钥被盗,两种方案的后果差别在哪? -
"签名"和"HMAC"都能防篡改,什么时候必须用签名而不能用 HMAC?
(提示:HMAC 双方共享同一把密钥,谁都能伪造) -
假设你要做一个"端到端加密聊天软件",设计了:客户端各自生成 RSA
密钥对,互相用对方公钥加密消息。 这个方案有哪些致命问题?
请至少说出两点并给出改进方向(提示:为何实际产品用双棘轮 / Signal 协议)。
做完思考题,再回看
diagrams/README.md的决策树,尝试用它回答:
"给 100 万用户的数据库字段做加密,你会怎么设计密钥体系?"