原生Nginx无法启用SM2/SM3/SM4,因其依赖的OpenSSL默认不识别国密算法标识,且Nginx源码未适配SM2公钥格式、SM3哈希标识及SM4套件命名规范,导致配置后握手失败或启动报错。

目前 Nginx 官方核心模块不支持国密 SM2/SM3/SM4 算法,直接编译启用或配置 ssl_protocols、ssl_ciphers 无法达成金融级国密合规要求。
为什么原生 Nginx 无法启用 SM2/SM3/SM4
Nginx 依赖 OpenSSL(或 BoringSSL)提供 TLS 底层密码学能力,而 OpenSSL 1.1.1 及更早版本完全不识别 SM 系列算法标识;OpenSSL 3.0+ 虽通过国密引擎(gmssl)引入实验性支持,但需手动加载引擎、注册算法、重写证书解析逻辑——Nginx 源码中所有 SSL 握手、证书验证、密钥交换路径均未适配 SM2 公钥格式(如 ASN.1 中的 id-sm2 OID)、SM3 哈希标识或 SM4 加密套件命名规范(如 TLS_SM4_GCM_SM3)。
常见错误现象包括:
- 配置
ssl_ciphers包含SM4后 Nginx 启动失败,报错unknown cipher - 使用国密证书(含 SM2 私钥)后,
nginx -t通过但客户端握手直接断连,日志仅显示SSL_do_handshake() failed - 即使替换为
gmssl编译的 OpenSSL,Nginx 仍无法正确协商 SM2 密钥交换,因ngx_ssl_handshake_handler未实现TLSEXT_TYPE_client_hello中对国密扩展的识别
可行路径:用 Tengine + gmssl 补丁或 openresty-gm 分支
国内已有成熟落地方案,本质是 fork Nginx 并深度修改 SSL 握手栈。主流选择有两个:
-
Tengine 3.0+ + gmssl 补丁:淘宝系维护,已合并部分国密 patch,需从 Tengine GitHub 获取带
ngx_http_ssl_sm_module的分支,编译时指定--with-openssl=指向已编译gmssl(非标准 OpenSSL)源码目录 -
openresty-gm:OpenResty 官方支持的国密分支(
https://github.com/openresty/openresty-gm),内置resty-sm2Lua 模块,支持在ssl_certificate_by_lua*阶段动态加载 SM2 证书,并通过ssl_protocols TLSv1.3+ssl_ciphers显式启用TLS_SM4_GCM_SM3等套件
注意:gmssl 不是简单替换 OpenSSL 动态库——必须用其专用工具链生成证书:gmssl genpkey -algorithm sm2 -out sm2.key,且证书需含正确 OID:Subject Public Key Info: sm2,否则 Nginx 加载即失败。
SM2 双证体系与 Nginx 配置关键点
金融场景要求“双证”:SM2 签名证书(用于服务端身份认证) + RSA 或 SM2 加密证书(用于密钥传输)。Nginx 不支持单 listen 上绑定两套证书,必须拆分为两个 server 块或使用 ssl_certificate_by_lua* 动态切换。
- 若用 openresty-gm,可在
ssl_certificate_by_lua_block中根据ssl_client_hello_get_ext判断 ClientHello 是否携带国密扩展,再调用resty_sm2:new():load_cert()加载对应证书 -
ssl_ciphers必须显式列出国密套件,例如:ssl_ciphers "TLS_SM4_GCM_SM3:ECDHE-SM2-SM4-GCM-SM3";,顺序影响优先级,且不能混用非国密套件(否则协商失败) - SM3 哈希仅用于证书签名和 PRF,无需额外配置;但若需在响应头中输出 SM3 摘要(如 Content-Digest),需用 Lua 计算:
require "resty.sm3".hex("data")
性能与灰度上线注意事项
SM2 签名验签比 RSA-2048 慢约 3–5 倍,SM4 加解密吞吐略优于 AES-GCM,但整体 TLS 握手延迟上升明显。实测 1k QPS 下,Tengine-gm 的 CPU sys 占用比标准 Nginx 高 40%+。
- 务必开启
ssl_session_cache shared:SSL:10m和ssl_session_timeout 4h,减少全握手频次 - 不要在
http块全局启用国密,先针对特定server_name或location灰度,配合 WAF 日志过滤TLSv1.3+SM4字段确认客户端真实协商结果 - 国密证书链必须完整,GMSSL 根 CA 需导入系统信任库,否则 Android/iOS 客户端会因证书链校验失败静默断连
真正卡点不在编译或配置,而在客户端兼容性——很多国产密码 SDK 对 TLS 扩展字段填充不规范,导致 Nginx 侧解析 ClientHello 失败,这类问题只能靠抓包看 supported_groups 和 signature_algorithms 扩展内容定位。


















