2. TLS/SSL 基础:协议工作原理与证书链
2.1 TLS 握手:怎么建立安全连接
TLS 握手,说白了就是客户端和服务器在正式传输数据前,先互相打个招呼,商量好用什么加密算法、交换密钥、验证身份。这个过程大概在几十毫秒内完成,但里面细节不少。我早年调试一个 HTTPS 服务时,就碰到过握手卡在 Certificate 阶段,整个页面加载超时,排查了半天才发现是证书链没发全——嗯,这就是我们今天要聊的重点。
一个典型的 TLS 1.2 握手流程如下:
- 客户端发送 ClientHello,包含支持的 TLS 版本、密码套件列表、随机数。
- 服务端回复 ServerHello,选定版本和密码套件,并带上自己的随机数。
- 服务端发送 Certificate 消息,携带服务器证书以及中间证书(证书链)。
- 服务端可选发送 ServerKeyExchange(用于密钥协商的参数)。
- 服务端发送 ServerHelloDone,表示 Hello 阶段结束。
- 客户端验证证书链(关键步骤),然后发送 ClientKeyExchange 传递预主密钥(用服务端公钥加密)。
- 双方各自计算会话密钥,发送 ChangeCipherSpec 切换加密,最后 Finished 消息确认握手成功。
你可能会问:TLS 1.3 是不是快很多?没错,1.3 把握手压缩到一次往返,还把不安全的算法全扔了。我习惯用这个表格对比两者的核心差异:
| 对比项 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手往返次数 | 2 次完整 RTT | 1 次 RTT(初次),0-RTT 可复用 |
| 密码套件协商 | 支持多种旧算法(RC4, 3DES) | 只保留 AEAD 强算法(AES-GCM, ChaCha20) |
| 密钥交换 | RSA 或 (EC)DHE | 强制 (EC)DHE,前向安全性 |
| 证书链传递 | 明文在 Certificate 消息中 | 加密后传递(除 ServerHello 外) |
| 握手消息数量 | 约 10 条 | 减少到 5 条 |
我的建议:如果在 KMS 集成场景下做 TLS Termination,优先选 TLS 1.3。一方面减少延迟,另一方面前向安全性对密钥保护更友好。但要注意有些老旧客户端(比如某些银行系统)只支持 1.2,得留个降级通道。
2.2 证书链:信任是如何传递的
证书链,说白了就是一层一层证明你的身份。根 CA 给中间 CA 签发证书,中间 CA 再给你的服务器签发证书。客户端拿到服务器证书后,会顺着这条链往上找,直到找到一个它本身就信任的根 CA 为止。这个“信任锚”通常是操作系统的受信任根证书存储。
我举个例子:你公司买了一台负载均衡器做 TLS Termination,运维配置了一个证书,结果手机访问提示“证书无效”。查了半天发现,他们只上传了服务器证书,没把中间 CA 证书带上。客户端自然找不到通往根 CA 的路径——这就是典型的“证书链不完整”!
证书链验证的完整过程:
- 检查服务器证书的签名是否由上级 CA 签发(用上级公钥解密签名,比对证书摘要)。
- 逐级向上检查每个中间 CA 证书的签名,直到根 CA。
- 检查根 CA 是否在客户端信任存储中。
- 检查每个证书的有效期、吊销状态(CRL/OCSP)、密钥用法等扩展域。
下面这张图直观展示了证书链的层级结构和验证方向:
我曾经踩过的坑:有一次线上 KMS 网关做 TLS Termination,配置了完整的证书链,结果某个内部客户端死活连不上,报“certificate chain too long”。查才发现中间 CA 链深度超过了 4 级,而那个旧系统的 SSL 库限制链深度为 3。自那以后,我习惯把中间 CA 控制在 2 级以内,最多 3 级。
2.3 证书链验证实战要点
你如果在终端里想验证证书链是否完整,可以用 openssl 来模拟客户端验证:
# 查看服务器证书 + 中间链(假设已下载到本地)
openssl verify -CAfile <根CA证书> -untrusted <中间链文件> server.crt
# 或者直接连接测试
openssl s_client -connect example.com:443 -showcerts
s_client 的输出会打印整个证书链,你可以一眼看出中间证书是否都发过来了。如果只有服务器证书,没有中间证书,那客户端大概率会报“unable to get local issuer certificate”。
这里我总结几个证书链的“硬指标”:
| 检查项 | 要求 | 常见问题 |
|---|---|---|
| 链完整性 | 服务器证书 → 中间CA1 → … → 根CA (客户端信任) | 漏发中间CA |
| 链深度 | 建议 ≤ 3 级 | 旧设备限制 3 级 |
| 签名算法 | 禁用 MD5、SHA-1(仅限 2016 年前签发) | 旧系统使用 SHA-1 根 |
| 有效期 | 所有证书均在有效期内 | 根CA过期但未更新信任库 |
| 密钥长度 | RSA ≥ 2048 位;ECC ≥ 256 位 | 旧证书用 1024 位已被弃用 |
我个人习惯在 TLS Termination 设备上额外开启 OCSP Stapling,让服务端主动提供证书的吊销状态,避免客户端每次都去 OCSP 服务器查询,既减少延迟又保护隐私。但注意中间 CA 也要支持 Stapling,否则会降级。