国密双证书在nginx协商失败本质是tls握手时客户端与服务端在密码套件、证书类型或协议能力上未达成一致;需先通过error.log定位失败方向,再验证openssl是否真支持sm2、抓包确认client hello是否含国密套件,最后检查ssl_ciphers配置与证书链完整性。

国密双证书在 Nginx 上协商失败,本质是 TLS 握手阶段客户端(如国密浏览器)与服务端无法就密码套件、证书类型或协议能力达成一致。排查不能只看配置是否“写了”,而要验证两端真实行为——尤其要区分清楚:失败发生在 Nginx 作为服务端接收国密客户端时,还是它作为客户端转发请求时。
第一步:确认失败方向,锁定 error.log 关键线索
打开 /var/log/nginx/error.log,搜索包含 SSL_do_handshake() failed 的行,重点看紧邻的上下文:
- 出现 while SSL handshaking, client: xxx.xxx.xxx.xxx → 问题在 Nginx 服务端,即国密浏览器连你这台 Nginx 失败
- 出现 while SSL handshaking to upstream → 问题在代理链路,和国密浏览访问无关,先排除
95% 的国密访问失败属于前者。若日志中同时出现 no shared cipher 或 handshake failure,基本可判定为密码套件或证书协商层面不匹配。
第二步:验证 Nginx 是否真正支持 SM2,而非“假配置”
标准 Nginx(含主流发行版 apt/yum 包)完全不识别 SM2 证书。即使你把 SM2 证书路径写进 ssl_certificate,Nginx 启动时不会报错,但实际握手时会静默降级或失败。验证方式:
- 执行
nginx -V 2>&1 | grep -i openssl,确认输出中 OpenSSL 版本来自 Tongsuo 或明确标注“GM/T”、“SM2”支持 - 检查启动日志:
systemctl status nginx或journalctl -u nginx | grep -i sm2,有 “loading SM2 private key” 类提示才可信 - 若用的是阿里云/腾讯云等平台证书,注意它们通常提供 RSA+SM2 双证书包,但 Nginx 必须用国密版才能加载 SM2 私钥,否则会报
X509_check_private_key: key value mismatch
第三步:抓取真实 Client Hello,看国密客户端到底发了什么
国密浏览器(如红莲花、360国密版、奇安信可信浏览器)在发起连接时,会在 Client Hello 的 Supported Groups 和 Cipher Suites 扩展中声明 SM2/SM4/SM3 能力。Nginx 需要能解析并响应。实操方法:
- 用支持国密的客户端(非 Chrome)访问,同时在服务器侧抓包:
tcpdump -i any port 443 -w gm_handshake.pcap - 用 Wireshark 打开 pcap,过滤
tls.handshake.type == 1(Client Hello),查看 “Cipher Suites” 字段是否含类似TLS_ECDHE_SM2_WITH_SM4_SM3的套件 - 若 Client Hello 中压根没出现国密套件,说明客户端未启用国密栈,或系统策略禁用了——此时不是 Nginx 问题,需查终端环境
第四步:检查 ssl_ciphers 与证书链是否真正协同生效
双证书不是“两个证书放一起就行”,Nginx 会根据协商出的密码套件自动选证。常见错误:
-
ssl_ciphers 过严:配置中只留了
ECDHE-ECDSA-AES256-GCM-SHA384等国际套件,却没加国密套件,导致国密客户端无交集。必须显式加入,例如:ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-SM2-WITH-SM4-SM3'; -
证书链缺失中间 CA:国密证书通常由国内 CA(如 CFCA、BJCA)签发,其根证书不在国际信任库中。Nginx 必须完整下发证书链(叶子证书 + 中间证书),否则国密客户端校验失败。用命令验证:
openssl s_client -connect your-domain.com:443 -servername your-domain.com -showcerts 2>/dev/null | grep "s:"
输出应至少有两行带s:的证书信息











