TLS/SSL 协议基础回顾
做密钥生命周期自动化之前,我建议大家先花点时间把 TLS 的底子摸透。我在过去几年里处理过上百起证书问题,有九成其实都是对协议理解不透闹的。
从 SSL 到 TLS:版本演进
TLS 的前身是 Netscape 搞的 SSL(Secure Sockets Layer),最初两个版本基本没人用。到了 1996 年的 SSL 3.0,算是打好了地基。后来 IETF 接管并改名为 TLS 1.0(1999),本质上就是 SSL 3.0 的一个小修小补。
真正拉开差距的是 TLS 1.2(2008)和 TLS 1.3(2018)。你想想看,一个协议活了二十年还在用,说明它架构设计得不错。但迭代慢了,旧版本的安全问题也堆了很多。
| 版本 | 发布时间 | 安全状态 | 我给你的建议 |
|---|---|---|---|
| SSL 2.0 / 3.0 | 1995 / 1996 | 已废弃,已知攻击如 POODLE | 不要启用 |
| TLS 1.0 | 1999 | 已禁止(PCI DSS、各大浏览器) | 尽快禁用 |
| TLS 1.1 | 2006 | 大部分合规标准禁止 | 别用了 |
| TLS 1.2 | 2008 | 广泛使用,但需正确配置 | 当前主力版本 |
| TLS 1.3 | 2018 | 最安全、性能最好 | 强烈推荐 |
⚠️ 我遇到过最坑的案例:某客户在生产环境同时保留了 TLS 1.0 和 1.3,因为中间负载均衡器配置的时候漏删了旧版本。结果安全扫描频频告警,排查了整整两天才发现是继承配置的锅。
TLS 握手协议——说白了就是“先对齐,再加密”
握手阶段的功能很简单:双方确认协议版本、选好密码套件、交换密钥参数、验证证书链,最后导出会话密钥。一旦握手完成,后面的数据传输就走对称加密了,效率高得多。
下面这张图展示的是 TLS 1.2 的完整握手流程,也是目前生产环境最需要掌握的版本。
密码套件与 TLS 1.3 的变化
密码套件就是一组算法的组合,决定了密钥交换、身份验证、对称加密和消息认证用啥。TLS 1.2 的套件名字很长,像 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,拆开看就是:
- 密钥交换:ECDHE(临时椭圆曲线 Diffie-Hellman)
- 身份验证:RSA(服务端证书签名)
- 对称加密:AES-256-GCM(带认证加密)
- PRF / 消息认证:SHA-384
💡 我个人习惯:在配置服务端时,直接把 ECDHE + GCM 的套件排在最前面,把 CBC 模式的套件全踢掉。CBC 模式在 TLS 1.2 时代出过太多漏洞(如 Lucky13),不值得冒险。
TLS 1.3 砍掉了好多冗余套件,只保留了 5 个,而且强制使用前向安全(ECDHE 或 DHE),不支持静态 RSA。如果你用 KMS 来管理 TLS 证书,TLS 1.3 的环境会让密钥轮换更省心——因为每次握手都生成临时的消息密钥,证书私钥只用来签名,不会用来加密预主密钥。
| 特性 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手延迟 | 2-RTT(完整握手) | 1-RTT(完整)、0-RTT(快速) |
| 支持的密码套件 | 37+(很多不安全) | 5个(全部要求前向安全) |
| 密钥协商 | 可选 RSA 或 (EC)DHE | 强制 (EC)DHE |
| 证书承载 | 明文传输证书链 | 加密传输(证书被加密) |
| 会话恢复 | Session ID / Session Ticket | PSK + Ticket 预共享密钥 |
证书链验证与 PKI
在我看来,TLS 握手里最难搞明白的一环就是证书链。客户端拿到服务端证书后,会做这几步验证:
- 有效期检查:当前时间在 notBefore 和 notAfter 之间。
- 签名验证:用颁发者公钥验证证书签名。
- 证书链构建:从服务端证书向上找到自签名根证书。
- 吊销检查:通过 CRL 或 OCSP 确认证书未被吊销。
- 主机名校验:检查证书中的 SAN(Subject Alternative Name)是否匹配客户端访问的域名。
⚠️ 我曾经踩过的坑:某次线上排查发现客户端频繁报 “certificate verify failed”,查了半天才发现中间证书过期了,但服务器只发了自己的证书和根证书,缺了中间证书。后来我在所有 NGINX 配置里加了一行 ssl_trusted_certificate 显式加载完整链,再没出过这个问题。
PKI(公钥基础设施)就是管理证书颁发、吊销、存储的一整套体系。大多数公司不会自己搭 PKI,而是用公有 CA(Let's Encrypt、AWS Private CA 等),配合 KMS 来保护 CA 的私钥。我们后面几章会细讲怎么用 KMS 托管 CA 私钥并自动签发证书。
避坑指南:常见握手失败原因
- 协议版本不匹配:客户端只支持 TLS 1.3,服务端只支持 TLS 1.2 → 握手失败。我建议服务端同时开放 TLS 1.2 和 1.3 过渡。
- 密码套件不一致:客户端列出的套件服务端一个都不支持。用 OpenSSL 排查时,可以跑
openssl ciphers -V对比两端支持的套件列表。 - 证书链不完整:服务器没发送中间证书。用
openssl s_client -showcerts就能看到链是否完整。 - 密钥用法错误:证书的 Key Usage 里没有 digitalSignature,不能用于 ECDHE 签名。这个在自签证书里特别常见。
- SNI 缺失:一个 IP 上挂多个域名,客户端没发 SNI 扩展,服务端不知道给哪个证书。现在浏览器默认都发,但老旧 IoT 设备可能漏。
- OCSP 响应超时:如果服务端配置了 OCSP Stapling,但 CA 的 OCSP 响应慢,导致握手超时。建议超时设短一点,或者 fallback 到 CRL。
📌 实用命令:我排查握手问题最常用的工具就是 OpenSSL 的 s_client:
openssl s_client -connect example.com:443 -tls1_2 -servername example.com
openssl s_client -connect example.com:443 -tls1_3 -servername example.com
能看到完整的握手过程、证书链、密码套件协商结果,以及最后是否报错。配合 -debug 还能看到裸的握手消息。