2、TLS 协议基础:握手流程、证书验证、密钥交换与加密
说实话,很多人配置了多年 TLS,真问起握手细节,能讲清的人不多。我当年刚入门时也觉得这东西就是个黑盒子——直到有一次线上故障,浏览器死活连不上服务端,日志只留了一句 handshake failure。从那天起,我老老实实把握手拆开学了一遍。
这一节我们不浮在表面。我会把 TLS 协议的四个核心环节掰开揉碎:握手流程、证书验证、密钥交换、加密与完整性。你弄懂了它们,以后调密码套件就不会再一头雾水。
2.1 握手流程——不只是“Hello”
我习惯把 TLS 握手比喻成"双方交换家底"的过程。客户端和服务端先互相亮身份、确认加密规则,再悄悄生成会话密钥。你用浏览器访问 HTTPS 网站,背后就是这套流程。
以最常用的 TLS 1.2 为例,核心步骤其实就八个字:打招呼、亮证书、换密钥、验证握手。具体拆开看:
- ClientHello:客户端告诉服务端我支持哪些 TLS 版本、哪些密码套件,顺便带一个随机数。
- ServerHello + 证书:服务端选定套件和版本,发回自己的数字证书(内含公钥),还带另一个随机数。
- ServerKeyExchange(可选):如果用 DHE 或 ECDHE 做密钥交换,服务端会额外发 DH 参数。注意 RSA 交换不需要这步。
- ServerHelloDone:通知客户端“我发完了,轮到你了”。
- ClientKeyExchange:客户端根据服务端公钥或 DH 参数,算出预主密钥(Pre-Master Secret)并发给服务端。
- ChangeCipherSpec + Finished:双方各自从预主密钥派生出对称密钥,然后发送一个加密的 Finished 消息。如果对方能解密,握手就成功了。
2.2 证书验证——信任链的由来
服务端发来的证书,你凭什么信任它?不是因为它长得好看,而是背后有一条证书链(Certificate Chain)。
我见过不少人只配了个自签名证书就上线了。结果用户浏览器直接报“不安全连接”。这不是技术没用对,是信任没建立好。
标准验证路径长这样:
- 客户端预置了根证书(Root CA):操作系统或浏览器里有一堆根证书,比如 DigiCert、GlobalSign。这些是“信任锚”。
- 服务端发来的证书链:一般包含三级——叶子证书(服务端自己)、中间证书(签发者)、根证书(自签名)。服务端不会发根证书,因为客户端已经有。
- 逐级验证签名:客户端用中间证书的公钥验证叶子证书的签名,再用根证书的公钥验证中间证书的签名。只要有一级验签失败,信任链就断了。
- 额外检查:有效期、域名匹配(CN/SAN)、是否被吊销(通过 CRL 或 OCSP)。
2.3 密钥交换——RSA 已死,DH 当立
密钥交换就是客户端和服务端协商出一个只有它俩知道的对称密钥。说白了,就是如何安全地传递一个秘密。
我入行时教科书还在大讲 RSA 密钥交换,后来现实狠狠打了脸。RSA 的致命缺陷是——它不提供前向安全性(Forward Secrecy)。一旦服务端的私钥泄露,之前所有用 RSA 加密的会话都能被解密。我这几年处理过不下五个客户,都是因为这个原因才恐慌式迁移到 ECDHE。
目前主流密钥交换算法对比:
| 算法 | 前向安全性 | 性能 | 现状 |
|---|---|---|---|
| RSA 密钥交换 | ❌ 无 | 快(基于非对称解密) | 强烈建议禁用 |
| DHE (Diffie-Hellman Ephemeral) | ✅ 有 | 慢(大整数运算) | 兼容老旧客户端可用 |
| ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) | ✅ 有 | 快(椭圆曲线,GPU加速友好) | ✳️ 推荐首选 |
ECDHE 为什么好?它用椭圆曲线代替了传统的大整数模幂运算,相同安全强度下计算速度快一个数量级。我建议优先选 曲线 X25519,其次是 secp256r1(P-256)。
2.4 加密与完整性——不只是防偷听
握手完成后,双方用协商好的对称密钥加密 HTTP 数据。但 TLS 不只是保护机密性,还保护完整性和防重放。
早期 TLS 用 HMAC-SHA1 做消息验证,加上 AES-CBC 加密。但 CBC 模式容易受 padding oracle 攻击(如 POODLE、BEAST)。后来改成了 AEAD 加密模式(Authenticated Encryption with Associated Data),比如 AES-GCM 和 ChaCha20-Poly1305。它们把加密和完整性验证合并成一个原子操作,一个函数搞定两件事,既安全又高效。
我个人最喜欢的套件是 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,前向安全 + 椭圆曲线 + 高效 AEAD + 128 位密钥(足够强) + SHA256 哈希。当然如果你的业务合规要求更严,可以用 AES-256-GCM,但实际 CPU 开销增加不明显。
TLS_RSA_WITH_AES_128_CBC_SHA 是经典反面教材。RSA 无前向安全,CBC 有 padding oracle 风险,SHA-1 已被撞库攻破。如果你的服务器上还有这个套件,请立刻移除。
再说一句:完整性不是靠哈希就行。TLS 的 Finished 消息就是用一个密钥派生的 HMAC 来验证之前的握手消息没有被篡改。我见过一个 case,有人试图通过修改 ClientHello 中的密码套件列表来降级攻击——因为 HMAC 校验失败,握手直接拒绝。这就是 AEAD 和 HMAC 的价值。
1. 握手流程是双方协商安全参数的过程,理解每一条消息的作用才能准确排错。
2. 证书验证依赖信任链,服务端必须配完整链。
3. 密码套件选择优先 ECDHE + AEAD(GCM 或 ChaCha20)。
记住这三点,后面配置弱套件禁用就不容易出错了。