1. TLS 协议概述
TLS 做安全传输的基石。这章我们不绕弯子,直接把 TLS 的来历、握手流程和加密套件说清楚。你在项目中肯定迟早要跟它打交道。
1.1 从 SSL 到 TLS:一点历史
说到 TLS,就绕不开它的前身 SSL。我记得刚开始做网络编程那会儿,还在用 SSLv3。后来爆出 POODLE 漏洞,整个行业紧急迁移到 TLS 1.0。嗯,那次升级真是让人头疼——好多老旧设备不兼容。
- SSL 1.0 (1994) —— 从未公开发布,据说有严重安全问题。
- SSL 2.0 (1995) —— 第一个商用版,但漏洞太多,现在谁还敢用?
- SSL 3.0 (1996) —— 相对成熟,但 2014 年被 POODLE 击穿。我建议直接把它 drop 掉。
- TLS 1.0 (1999) —— 实际是 SSL 3.1,改了个名。现在已弃用。
- TLS 1.1 (2006) —— 加了 CBC 保护,但不够彻底。
- TLS 1.2 (2008) —— 目前最广泛使用,支持 AEAD、GCM。很多硬件加速器就是针对它做的。
- TLS 1.3 (2018) —— 握手从 2-RTT 降到 1-RTT,还砍掉了一堆过时套件。我在项目中遇到过 TLS 1.3 的 0-RTT 模式,延迟确实低,但重放攻击风险也要留意。
避坑指南:我之前有个项目一直用 TLS 1.2,后来发现某些客户端强制协商成 TLS 1.0。查了半天,是因为服务器上残留了 SSLv2 的兼容代码。建议直接禁用所有低于 TLS 1.2 的版本,除非你有很老的设备要兼容。
说白了,TLS 版本演进就是在“安全”和“性能”之间反复博弈。
1.2 TLS 握手流程:到底在干嘛?
握手是 TLS 最核心的环节。客户端和服务器通过四步(或者三步,取决于版本)协商出对称密钥。你想想看,公钥加密太慢,所以只用来交换一个临时密钥。后面的大流量都由对称加密保护。
下面这张 SVG 描述了典型的 TLS 1.2 全握手流程,你可以把它当作对照图来理解接下来的文字。
握手具体做这几件事:
- Client Hello:客户端发送支持的 TLS 版本、加密套件列表、随机数。
- Server Hello + 证书:服务器选定套件和版本,发回证书(含公钥),同时给随机数。
- 密钥交换:客户端验证证书后,用服务器公钥加密一个预主密钥(Pre-Master Secret)发过去;或者用 ECDHE 生成临时密钥对。
- 双方计算主密钥:然后各自生成会话密钥。
- Change Cipher Spec + Finished:告诉对方“我要开始用这个密钥加密了”,并发一段验证密文。对方解密成功就握手完成。
性能关键点:握手延迟主要来自两次网络 RTT(TLS 1.2 全握手)。如果是 ECDHE 密钥交换,还涉及椭圆曲线计算。我在之前做移动端 SDK 时,发现每次握手多花 50ms,用户体验就很明显。后来切了 TLS 1.3 的 1-RTT 握手,效果拔群。
1.3 加密套件:一串神秘字符解码
你见过这种字符串吧:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256。说白了,它把四样东西打包在一起:密钥交换算法、身份认证算法、对称加密算法、消息认证码(MAC)或 AEAD。
下面这个表格总结了常见套件的组成,方便你对照。
| 套件示例 | 密钥交换 | 认证 | 对称加密 | MAC / AEAD | 适用场景 |
|---|---|---|---|---|---|
ECDHE-RSA-AES128-GCM-SHA256 |
ECDHE | RSA | AES-128-GCM | GCM (AEAD) | 通用,主流 |
ECDHE-ECDSA-AES256-GCM-SHA384 |
ECDHE | ECDSA | AES-256-GCM | GCM | 高安全 & 硬件加速支持 |
RSA-WITH-AES-128-CBC-SHA |
RSA | RSA | AES-128-CBC | SHA-1 (HMAC) | 老设备 (不建议) |
CHACHA20-POLY1305 |
ECDHE | RSA 或 ECDSA | ChaCha20 | Poly1305 (AEAD) | 移动端 & 低功耗 |
讲两点我踩过的坑。
- 关于 CBC 模式:以前很多套件用 AES-CBC,但遭遇了 Lucky13、BEAST 等攻击。即使打了补丁,效率也不如 GCM。我建议新系统全用 AEAD 套件(GCM 或 ChaCha20-Poly1305)。
- 关于 ECDHE vs RSA 密钥交换:RSA 密钥交换不支持前向安全性——如果服务器私钥泄露,之前所有会话加密都能被破解。ECDHE 每次握手生成临时密钥,属于“一次一密”。在我的项目里,一律强制要求 ECDHE。
# 查看 OpenSSL 支持的套件(只显示 TLSv1.2 的 ECDHE 套件)
openssl ciphers -v 'ECDHE:!aNULL:!eNULL' | head -10
# 输出示例:
# ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
# ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
警告:不要随便把 ALL:COMPLEMENTOFDEFAULT 放进套件列表。我曾经在测试环境图省事,结果协商到了一个 56-bit DES 套件,Wireshark 一点就能解密。生产环境一定要显式指定白名单。
总结下这一节的核心:
- 了解 TLS 版本历史,避免用老版本。
- 握手流程的核心是协商密钥,延迟大头在网络 RTT 和公钥运算。
- 加密套件选择:AEAD + ECDHE 是标配,放弃 CBC 和静态 RSA。