4、国密SSL/TLS协议改造:从国际算法套件切换到国密套件,Nginx/OpenSSL国密适配,双证书部署策略
国密改造里,SSL/TLS协议这块是最容易踩坑的。说实话,很多团队把精力都放在应用层代码替换上,结果到了网络通信层才发现——浏览器连不上、证书链报错、握手失败。我最早做国密网关改造时,光调试TLS握手就花了两天。
今天咱们就把这块掰开揉碎讲清楚。核心就三件事:协议套件怎么换、Nginx怎么配、双证书怎么玩。
4.1 国际套件 vs 国密套件:到底差在哪?
先看一张对比表,心里有个底:
| 对比项 | 国际TLS 1.2/1.3 | 国密TLCP(GM/T 0024) |
|---|---|---|
| 密钥交换 | ECDHE、DHE | ECDHE(SM2曲线) |
| 签名算法 | RSA、ECDSA | SM2(基于椭圆曲线) |
| 对称加密 | AES、ChaCha20 | SM4(128位分组) |
| 摘要算法 | SHA-256、SHA-384 | SM3(256位) |
| 证书格式 | X.509 v3(RSA/ECC) | X.509 v3(SM2公钥) |
| 协议版本 | TLS 1.2 / 1.3 | TLCP v1.0 / v1.1 |
你可能会问:国密协议是不是完全另起炉灶?其实不是。TLCP(Transport Layer Cryptography Protocol)在设计上大量参考了TLS 1.2的框架,但把核心密码组件全换成了国密算法。说白了,握手流程还是Client Hello、Server Hello、证书交换、密钥协商那一套,但里面跑的算法变了。
关键差异点:
- 国密必须使用SM2椭圆曲线(曲线参数固定,不是随便选一条)
- SM4是唯一指定的对称加密算法,不支持AES回退
- 证书链中所有证书必须使用SM2签名,不能混用RSA
4.2 双证书部署策略:为什么需要两本证?
这是国密改造里最让人困惑的地方。我刚开始也纳闷:一个域名为什么要配两本证书?
原因很简单——兼容性。国密浏览器(如360安全浏览器、红莲花)支持国密套件,但Chrome、Edge这些主流浏览器不支持。你不可能让用户先换浏览器再访问你的网站。
所以实际部署时,服务器同时持有两本证书:
- 国密证书(SM2):用于国密浏览器,走TLCP协议
- 国际证书(RSA/ECC):用于普通浏览器,走标准TLS协议
握手时怎么选?靠协议协商。客户端在Client Hello里带上自己支持的密码套件列表,服务端根据优先级选择。国密浏览器会优先发起国密套件,普通浏览器则发起国际套件。Nginx根据这个来决定用哪本证书。
我的经验:双证书部署时,国密证书和国际证书的CN/SAN必须完全一致。否则浏览器会报域名不匹配。我曾经因为国密证书少配了一个SAN,排查了整整一个下午。
4.3 Nginx + OpenSSL 国密适配实战
Nginx本身不支持国密,需要打补丁。目前主流方案有两种:
- 使用Tongsuo(原 BabaSSL):阿里开源的国密增强版OpenSSL,直接替换系统OpenSSL
- 使用GmSSL:北京大学开发的国密开源库,需要重新编译Nginx
我个人更推荐Tongsuo,因为它对Nginx的兼容性更好,而且社区活跃。下面以Tongsuo为例,讲一下具体步骤。
4.3.1 编译安装Tongsuo
# 下载Tongsuo源码
wget https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/8.4.0.tar.gz
tar -xzf 8.4.0.tar.gz
cd Tongsuo-8.4.0
# 编译安装(启用国密支持)
./config --prefix=/usr/local/tongsuo \
enable-ntls \
enable-sm2 \
enable-sm3 \
enable-sm4
make -j$(nproc)
make install
注意:编译时一定要加 enable-ntls 参数,这是国密TLCP协议的支持开关。不加的话,编译出来的OpenSSL只能做国际TLS。
4.3.2 重新编译Nginx,链接Tongsuo
# 下载Nginx源码
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -xzf nginx-1.24.0.tar.gz
cd nginx-1.24.0
# 配置编译参数,指定Tongsuo路径
./configure --prefix=/usr/local/nginx \
--with-openssl=/path/to/Tongsuo-8.4.0 \
--with-openssl-opt="enable-ntls" \
--with-http_ssl_module \
--with-http_v2_module
make -j$(nproc)
make install
编译完成后,用 nginx -V 确认一下,看到 enable-ntls 就说明成功了。
4.3.3 Nginx双证书配置
这是最核心的部分。Nginx从1.11.0版本开始支持 ssl_certificate 指令的多证书配置,但国密场景下需要额外指定 ssl_certificate_sm2 和 ssl_certificate_key_sm2。
server {
listen 443 ssl;
server_name example.com;
# 国际证书(RSA)
ssl_certificate /etc/nginx/certs/rsa.crt;
ssl_certificate_key /etc/nginx/certs/rsa.key;
# 国密证书(SM2)
ssl_certificate_sm2 /etc/nginx/certs/sm2.crt;
ssl_certificate_key_sm2 /etc/nginx/certs/sm2.key;
# 国际密码套件
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:...';
# 国密密码套件
ssl_ciphers_sm2 'ECDHE-SM2-SM4-GCM-SM3:ECDHE-SM2-SM4-CBC-SM3';
# 协议版本
ssl_protocols TLSv1.2 TLSv1.3;
ssl_protocols_sm2 TLCPv1.1;
# 优先使用国密套件(如果客户端支持)
ssl_prefer_server_ciphers on;
ssl_prefer_server_ciphers_sm2 on;
location / {
proxy_pass http://backend;
}
}
配置要点:
ssl_certificate_sm2和ssl_ciphers_sm2是Tongsuo扩展的指令,标准Nginx没有- 国密套件名称以
SM开头,如ECDHE-SM2-SM4-GCM-SM3 - 协议版本要分别指定:国际用
TLSv1.2 TLSv1.3,国密用TLCPv1.1
4.4 国密握手流程:一张图看懂
下面这张图展示了国密TLCP协议的完整握手流程。我建议你保存下来,调试时对照着看。
4.5 避坑指南:我踩过的那些坑
这部分是我最想分享的。国密改造的坑,十个有八个出在证书和协议协商上。
坑1:证书链不完整
我曾经部署国密证书时,只上传了服务器证书,没上传中间CA证书。结果国密浏览器一直报"证书链不完整"。国密PKI体系里,中间CA证书必须和服务器证书一起下发,不能依赖浏览器内置。
解决方法:用 cat server.crt ca.crt > fullchain.crt 合并证书链。
坑2:SM2私钥格式不对
国密SM2私钥有自己特定的ASN.1结构,和RSA私钥完全不同。如果你用OpenSSL的 genrsa 生成私钥,那肯定不对。必须用 gmssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2。
验证方法:用 openssl ec -in sm2.key -text -noout 查看,确认曲线是 SM2。
坑3:Nginx配置顺序导致国密不生效
Nginx加载配置时,如果 ssl_certificate_sm2 写在 ssl_certificate 前面,某些版本会忽略国密配置。我建议严格按照:国际证书 → 国密证书 → 国际套件 → 国密套件的顺序写。
4.6 验证与测试
配置完成后,怎么确认国密生效了?我一般用三步验证法:
- 用国密浏览器访问:打开360安全浏览器,访问你的域名,地址栏出现绿色"国密"标识,说明国密握手成功
- 用curl模拟国密握手:
# 使用Tongsuo编译的curl
/path/to/tongsuo-curl -v --ciphers SM2-SM4-GCM-SM3 https://example.com
# 输出中看到 "Cipher is SM2-SM4-GCM-SM3" 即为成功
- 抓包确认:用Wireshark抓TLS握手包,查看Server Hello中的Cipher Suite字段,如果是
0xE0xx开头的国密套件,就对了
小技巧:如果手头没有国密浏览器,可以用 openssl s_client -connect example.com:443 -ciphersuites "SM2-SM4-GCM-SM3" -groups "SM2" 来测试。注意要用Tongsuo版本的openssl。
4.7 性能与兼容性权衡
最后聊一个实际决策问题。双证书部署后,Nginx需要同时维护两套SSL上下文,内存占用会翻倍。我测过一个线上环境,单台Nginx的SSL内存从80MB涨到了150MB。
另外,国密SM2的签名验签性能比RSA 2048慢大约30%,比ECDSA P-256慢约50%。如果你的业务对TLS握手性能敏感,建议:
- 开启Nginx的
ssl_session_cache,减少重复握手 - 使用硬件密码卡(如国密HSM)加速SM2运算
- 对非国密用户,优先走国际TLS,避免不必要的国密协商开销
嗯,国密SSL这块内容确实不少。但核心思路就一条:协议框架不变,换算法、换证书、换配置。只要把这三个环节打通,剩下的就是调优和运维了。