对称加密与非对称加密在TLS中的应用归纳总结
做TLS相关项目几年下来,我最深的感受是:对称加密和非对称加密就像一对互补的搭档。一个负责速度,一个负责安全启动。单独用哪个都不行。
你想想看,如果全程用非对称加密传几兆的数据,CPU直接烧红。如果全程只用对称加密,密钥怎么安全地传给对方?所以TLS的设计哲学就是:用非对称做“握手”,用对称做“传输”。下面我们细看。
1. 为什么非对称加密不直接用来加密数据?
非对称加密慢,这是物理限制。RSA-2048加密一个对称密钥(256位)大约需要几毫秒,但加密1MB数据需要几十秒。我在优化一个支付网关时测过,纯RSA传输10MB请求,延迟直接飙到30秒,用户早就放弃了。
2. 非对称加密在握手阶段的角色
TLS握手阶段,非对称加密干两件事:
- 身份验证——服务器用私钥签名证书,客户端用公钥验证。
- 密钥交换——在不安全的信道中协商出一个只有双方知道的共享密钥。
常见的密钥交换方式有RSA和(EC)DHE。我建议你优先用ECDHE,为什么?
| 对比项 | RSA密钥交换 | ECDHE密钥交换 |
|---|---|---|
| 前向安全性 | 不支持(私钥泄露可解密历史流量) | 支持(临时密钥,每次握手都变) |
| 性能 | 快(一次公钥加密) | 中等(两次椭圆曲线乘法) |
| 安全性演进 | 已被降级攻击利用(如ROBOT) | 推荐使用(TLS 1.3强制) |
| 抗量子风险 | 量子下可被Shor算法破解 | 同样易受影响,但可升级为后量子 |
我曾经接手过一个遗留系统,用的还是RSA密钥交换。结果审计发现,服务器私钥曾通过备份泄露,导致所有历史会话都能解密。后来全部迁移到ECDHE,配合临时私钥,每次握手独享一份密钥——就算某次私钥丢了,也只能解密那一次会话。
3. 对称加密:真正的数据通道
握手完成后,双方已经共享了一个预主密钥(Pre-Master Secret),通过PRF(伪随机函数)派生出多个对称密钥。每个方向各一套:
- 加密密钥(用于数据加密)
- HMAC密钥(用于消息认证)
- IV(初始化向量,部分模式需要)
TLS 1.2及之前主要用AES-CBC + HMAC,但容易有Padding Oracle攻击。TLS 1.3强制使用AEAD模式(如AES-128-GCM、ChaCha20-Poly1305),把加密和认证合为一个原子操作。我实际测试过,ChaCha20在移动设备上比AES快20%~30%,因为它没有硬件加速也能跑得飞起。
// 伪代码:TLS 1.3 对称加密数据单元(Record)
function encryptRecord(plaintext, key, nonce) {
// AEAD 自动处理加密 + 认证标签
const (ciphertext, tag) = AEAD.Encrypt(key, nonce, plaintext, associated_data);
// associated_data 包含序列号、内容类型等
return Record { type: encrypted, ciphertext, tag };
}
4. 密钥边界:从握手到销毁的闭环
我常说,密钥边界不是画一条线,而是一条生命周期线。从预主密钥产生,到派生出工作密钥,再到会话结束安全销毁,每一步都要有明确的边界控制。
举个常见的坑:很多开发者只关心握手阶段的密钥交换,却忽略了密钥派生过程的上下文隔离。有个项目用OpenSSL 1.0.2,默认的PRF实现会复用部分中间状态,导致两个并发握手的密钥可能关联。后来升级到1.1.1才修复。所以我的建议是:
- 每个会话使用独立的随机数(ClientRandom + ServerRandom)
- PRF输入必须包含所有握手指纹(hash of handshake messages)
- 密钥派生结果立即覆盖临时缓冲区
5. 一张图看清全流程
下面这张图总结了TLS中对称与非对称加密如何协作。左边是握手,右边是数据传输。非对称只出现在握手阶段,之后一切靠对称。
从图上你可以看到:所有非对称操作都集中在握手阶段,输出的预主密钥通过PRF派生出一组对称密钥,之后整个数据传输通道完全由对称加密接管。这个边界一旦划清,安全性就提升了一个档次。
6. 避坑指南
- 不要混用同一个对称密钥加密和控制消息。 我曾经看到一个实现,用同一个AES-GCM密钥既加密应用数据,又加密TLS控制帧(如KeyUpdate)。结果密钥流被猜测,导致降级攻击。后来改成每个逻辑流独立密钥。
- 注意密钥更新时机。 TLS 1.3支持KeyUpdate消息,定期更换对称密钥。但有人忘了实现它,导致流量长时间暴露在同一密钥下。我建议长连接每10万条记录就主动更新一次。
- 小心密钥派生时的熵源。 预主密钥的随机性直接决定安全强度。嵌入式设备上随机数生成器可能熵不足,我用过一个IoT网关,启动前几秒的Random全一样,握手全部复用同一预主密钥… 后来加了一条硬件熵源才解决。
OPENSSL_cleanse),直接free内存是不够的——堆可能被转存。
好了,对称与非对称在TLS中的分工就这样。记住一个口诀:握手指令用非对称,数据传输用对称,密钥边界要分明,销毁务必不留根。