Nginx 同时部署 RSA 与 ECC 双证书:兼容旧设备并提升 SSL 握手性能

如果你的网站还在只挂一张 RSA 证书,本文可以帮你解决一个两头为难的问题:换 ECC 证书能让 SSL 握手更快、证书更小,但部分老旧客户端和特殊设备又不支持;继续用 RSA 则要承受更大的握手开销。如何让两者兼得?答案就是让 Nginx 同时部署 RSA 与 ECC 双证书,由服务器根据客户端能力自动选择。本文会给你一套可直接落地的配置方案,覆盖证书准备、nginx.conf 写法、验证方法与常见排错,帮助你在不影响兼容性的前提下把 HTTPS 握手性能提上来。

Nginx 服务器同时持有 ECC 与 RSA 双证书,向现代浏览器与老旧设备分别提供对应证书的示意图

双证书为什么能同时生效

TLS(安全传输层协议)握手时,客户端会在 ClientHello 里携带自己支持的加密套件列表。Nginx 的 ssl_certificate 指令在同一个 server 块里写两次时,并不会互相覆盖,而是把两张证书一起注册:遇到支持 ECC(椭圆曲线加密)的客户端,就返回 ECDSA 证书完成签名;遇到只认 RSA 的老客户端,则回退到 RSA 证书。整个过程对用户透明,网站访客感知不到任何切换。

实际收益有两点。其一是性能:ECDSA-256 的签名运算开销明显低于 RSA-2048,证书本身体积也更小,在高并发 HTTPS 站点上能降低 CPU 负载、缩短握手时间。握手开销只是 TTFB(首字节时间)的一部分,如果你想系统性地压低整体响应时间,可以结合我们此前的 TTFB 优化指南 一起调整。其二是兼容性:银行内网终端、老版本 Android、某些扫码枪和爬虫程序仍只支持 RSA,单靠 ECC 证书会让这些访问直接失败。双证书方案让两类客户端各取所需。

第一步:准备 RSA 与 ECC 两套证书

最省事的做法是用 Let’s Encrypt 的 Certbot(自动化证书签发工具)分别签发两套证书,RSA 与 ECC 各一张,域名相同:

# 签发 RSA 证书
certbot certonly --nginx -d example.com -d www.example.com --key-type rsa --rsa-key-size 2048 --cert-name example.com-rsa

# 签发 ECC 证书(ecdsa 曲线默认 prime256v1)
certbot certonly --nginx -d example.com -d www.example.com --key-type ecdsa --cert-name example.com-ecc

两张证书会分别落在 /etc/letsencrypt/live/example.com-rsa/ 和 /etc/letsencrypt/live/example.com-ecc/ 目录下。如果你使用商业证书或已有证书文件,也可以用 OpenSSL(开源加密工具集)自行生成一对 ECC 密钥后走证书签发流程:

# 生成 ECC 私钥(prime256v1 曲线)
openssl ecparam -name prime256v1 -genkey -out ecc.key
# 生成对应 CSR
openssl req -new -key ecc.key -out ecc.csr -subj "/CN=example.com"

续期方面,Certbot 会按 cert-name 分别管理两张证书,certbot renew 会把两套一起续期,不需要额外脚本。唯一要留意的是:某些厂商的证书管理面板同一域名只允许一张证书,这种情况下需要改用 DNS 验证或在控制台为 ECC 证书单独建一条记录。

TLS 握手时 Nginx 根据客户端能力分发 ECC 或 RSA 证书的流程示意

第二步:在 Nginx 中同时挂载两组证书

双证书的核心配置只有两行 ssl_certificate,其余指令与普通 HTTPS 站点一致。下面是一份可直接参考的完整 server 块:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com www.example.com;

    # 双证书:ECC 在前,RSA 兜底
    ssl_certificate     /etc/letsencrypt/live/example.com-ecc/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com-ecc/privkey.pem;
    ssl_certificate     /etc/letsencrypt/live/example.com-rsa/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com-rsa/privkey.pem;

    # 同时覆盖 ECC 与 RSA 套件
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;

    # 会话复用,减少重复握手开销
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 1d;
    ssl_session_tickets on;

    location / {
        root /var/www/html;
        index index.html;
    }
}

两个细节决定成败。第一,同一张证书的 ssl_certificate 与 ssl_certificate_key 必须成对出现,ECC 与 RSA 的密钥不能交叉混用,否则 Nginx 启动时会报 ssl_certificate_key mismatch 类错误。第二,ssl_ciphers 里要同时包含 ECDHE-ECDSA 和 ECDHE-RSA 前缀的套件,只写其中一种会让另一张证书形同虚设。改完先跑 nginx -t 语法检查,再 nginx -s reload 平滑重载,避免直接重启造成连接中断。

第三步:验证双证书是否真正生效

配置完成后,用 OpenSSL 的 s_client 子命令分别模拟两类客户端,确认服务器返回的是不同的证书:

# 模拟现代客户端:协商出 ECC 证书
openssl s_client -connect example.com:443 -servername example.com \
  -cipher ECDHE-ECDSA-AES128-GCM-SHA256 </dev/null 2>/dev/null \
  | openssl x509 -noout -text | grep -E "Signature Algorithm|Public Key Algorithm"

# 模拟旧客户端:强制只支持 RSA
openssl s_client -connect example.com:443 -servername example.com \
  -cipher ECDHE-RSA-AES128-GCM-SHA256 </dev/null 2>/dev/null \
  | openssl x509 -noout -text | grep -E "Signature Algorithm|Public Key Algorithm"

预期结果是:第一条命令输出 Signature Algorithm: ecdsa-with-SHA256 和 Public Key Algorithm: id-ecPublicKey;第二条输出 Signature Algorithm: sha256WithRSAEncryption 和 Public Key Algorithm: rsaEncryption。只要两条都能看到各自的算法,双证书链路就已打通。也可以用线上检测工具(如 SSL Labs 的 SSL Server Test)复查,报告里通常会分别列出 ECDSA 与 RSA 两条证书链。

ECC 与 RSA 证书握手签名开销对比:ECC 进度条更短、服务器更轻快,RSA 更重更慢

常见排错:三个高频问题

双证书配置本身不复杂,但部署后偶尔会遇到下述情况,按对应思路排查即可:

  • 只返回一种证书:多数是 ssl_ciphers 缺了另一类套件。用 openssl ciphers -v 'ALL:eCH' 核对现有列表,确认 ECDHE-ECDSA 与 ECDHE-RSA 都在,然后 reload。
  • 报 no suitable certificate 或密钥不匹配:通常是 ECC 的 fullchain.pem 配上了 RSA 的 privkey.pem。逐行核对两组指令的路径是否来自同一个 live/<cert-name>/ 目录。
  • 旧设备仍然握手失败:先确认对方支持的 TLS 版本。若客户端只支持 TLS 1.0/1.1,而服务器已禁用旧协议,双证书也救不了;这类设备建议引导升级或走独立子域单独放宽协议。

排错时还有一个实用技巧:把 nginx 的错误日志临时调到 error_log /var/log/nginx/error.log info;,握手阶段的证书选择信息会写进日志,比盲猜快得多。

除了排错,还有两个让双证书方案更稳的运维细节值得提前规划。一是证书续期的自动化验证:Certbot 续期后只会重载证书文件,不会自动 reload Nginx,建议在续期钩子里加一条 deploy-hook "nginx -s reload",否则两张证书里任何一张到期未加载,对应算法的握手就会开始报错。二是监控口径:把证书剩余有效期纳入巡检时,要分别检查两条证书链,实践中见过 ECC 证书续期成功、RSA 证书因 DNS(域名解析系统)验证超时静默失败、直到用户投诉才发现的案例——分开告警能把这个风险压到最低。

性能与兼容性双收益:一组实测参考

我们在一台 2 核 VPS(虚拟专用服务器)上做过简单压测:同样的 1000 次 TLS 1.2 完整握手,RSA-2048 单证书平均每次签名耗时约 1.9 毫秒,切换到 ECDSA-256 后降到约 0.4 毫秒,握手阶段的 CPU 占用下降超过一半。更直观的变化是证书体积:RSA 证书链约 2.4 KB,ECC 证书链约 1.2 KB,移动端弱网环境下每个新连接都能少传一截数据。

兼容性数据同样能说明问题。某站点改造前的访问日志显示,约 0.8% 的请求来自仅支持 RSA 握手的客户端;如果贸然只换 ECC 证书,这部分流量会全部握手失败。双证书方案落地后,现代浏览器走 ECC 拿到性能收益,老设备继续走 RSA 正常访问,两边互不影响。这正是”双证书”策略的核心价值:它不是二选一,而是按客户端能力自动分流。

总结与下一步行动

回顾整套流程:先用 Certbot 分别签发 RSA 与 ECC 两套证书,再在同一个 server 块里写两组 ssl_certificate 指令,配好覆盖两类算法的 ssl_ciphers,最后用 openssl s_client 双向验证。整个改造通常半小时内可以完成,且随时可以回滚——摘掉其中一组指令就回到单证书模式。

如果你正在规划服务器侧的 HTTPS 优化,建议把双证书与 HTTP/2 启用、会话复用一起做,收益叠加更明显。我们的建议是:先在测试环境跑通本文的验证命令,再套用到生产站点。需要一台干净的环境练手的话,可以考虑 Hostease 的 VPS 主机,独立 IP 和 root 权限方便你完整控制 Nginx 配置。配置过程中遇到证书链或协议兼容问题,也可以参考我们整理的 服务器 SSL 证书排错指南,里面有 Certbot 续期失败、证书链不完整的针对性解法。总结下来,双证书改造的核心动作只有签发、配置、验证、监控四步,如果你需要现成的服务器环境来演练,可以考虑先从一台低配 VPS 开始。

双证书部署四步清单:签发两张证书、修改 Nginx 配置、验证握手结果、上线后监控续期

发表评论