3. TLS参数详解:加密套件、证书链、密钥交换算法

TLS 握手能不能成功,最终看的就是这三个参数。你想想看,加密套件决定如何加密,证书链决定你是谁,密钥交换决定怎么安全地交换密钥——缺一不可。我见过不少生产事故,说白了就是这三项配置出了问题。

3.1 加密套件:选择与优先级

加密套件就是 TLS 握手的“密码本”。它规定了认证、密钥交换、加密和 MAC 算法。格式大致像这样:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384。

我个人习惯把套件分成三组:

  • 顶级安全:ECDHE + RSA/ECDSA + AES-GCM/SHA384
  • 主流兼容:ECDHE + RSA + AES-CBC/SHA256
  • 遗留备用:DHE + RSA + AES-CBC/SHA1

我在项目中遇到过一件事:某次升级后,部分老旧客户端突然连不上。查了一圈,原来是 GCM 套件排太靠前,对方不支持。后来我把 CBC 套件提到前面,问题才解决。避坑指南:我曾经在灰度发布时直接把 CBC 全砍了,结果报销了一批 POS 机。所以,你先用 openssl ciphers -V 看看实际支持的套件列表,再慢慢调顺序。

提示:Nginx 配置示例——
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:…";
注意把 ECDHE 系列放前面,CBC 放后面做降级。

3.2 证书链:信任是从根开始的

证书链说白了就是一系列互相签名证书的列表。浏览器拿到服务器证书后,会沿着链向上找,直到根证书。如果链不完整,客户端就报错。你想想看,中间缺了一张证书,就像户口本少了一页——根本没法验证。

常见的坑有两个:

  • 漏发中间证书:服务器只发了叶子证书,没发中间CA。我在项目里用 openssl s_client -showcerts 抓包,发现只有一张证书。补上 CA 中间证书后,握手就正常了。
  • 证书顺序错误:正确的顺序是叶子证书→中间证书→根证书。我曾经遇到有人把根证书放在最前面,结果链校验失败。检查方法很简单,用 openssl verify -CAfile root.pem -untrusted chain.pem server.pem。
注意:千万不要把私钥文件与证书链放在一起!很多泄漏事故就是因为配置文件里写错了路径。最好把 ssl_certificate 指向 fullchain.pem(含中间证书),ssl_certificate_key 指向 privkey.pem。

3.3 密钥交换算法:安全与性能的平衡

密钥交换算法决定了通信双方如何安全地协商出对称密钥。早期用 RSA 密钥交换,现在几乎不用了。为什么不推荐?RSA 交换本身没有前向安全性——一旦私钥泄漏,之前的会话全完蛋。

目前主流的算法对比:

算法前向安全性能推荐场景
RSA 密钥交换❌快仅向后兼容,不建议新项目
DHE✅慢(模运算大)需要兼容极端老旧客户端时
ECDHE(推荐)✅快(椭圆曲线)绝大多数生产环境

我建议所有新服务都用 ECDHE。你想想看,性能比 DHE 好两个数量级,而且前向安全性完全足够。记得配置 ssl_ecdh_curve prime256v1 或 X25519。我曾经用错了曲线,导致某些 Android 设备握手失败,后来统一用 X25519 和 prime256v1 就稳了。

实战技巧:用 openssl s_server -cert server.pem -key privkey.pem -cipher ECDHE 快速测试 ECDHE 是否正常工作。如果看到 Server Temp Key: X25519 就对了。

3.4 参数之间的依赖关系——一张图看懂

这三个参数不是孤立的。加密套件里包含了密钥交换和认证算法,而证书链又决定了认证算法的可用性。我把它们的关系画成一张结构图:

TLS 参数核心结构 TLS 参数 加密套件 AES-GCM CBC/SHA256 证书链 根证书 中间证书 叶子证书 密钥交换 ECDHE DHE 加密套件决定了选用哪组密钥交换和证书类型, 三者必须兼容才能完成TLS握手。

你看,加密套件里写着 ECDHE 就意味着要使用 ECDHE 密钥交换,同时需要对应的签名算法(RSA 或 ECDSA),而签名算法又依赖证书链中公钥的类型。我经常说:选套件就是选一整个故事。

3.5 实际配置检查清单

好了,整理一下你部署前要确认的几件事:

  1. 用 openssl ciphers -v 'HIGH:!aNULL:!MD5' 列出实际可用的套件。
  2. 证书链文件是否包含中间证书?用 openssl x509 -in chain.pem -text -noout 看 Subject/Issuer。
  3. 确认密钥交换算法不是 RSA 交换,而是 ECDHE 或 DHE。
  4. 检查 DH 参数长度是否在 2048 位以上(用 openssl dhparam -text -noout)。
小窍门:你可以用 Mozilla SSL Configuration Generator(本地离线版本有脚本)一键生成配置,但一定要手工核对套件顺序和证书路径。

我个人通常在灰度发布前跑一遍 sslyze --regular yourdomain.com,它会自动扫出弱套件、证书链问题、DH 参数弱项。基本上跑完就放心了。