SM2 国密算法实战:从编译国密 OpenSSL 到生产级 TLS 部署全流程

实践教程 · 2026-06-03 · 14 阅读

前言

国密 HTTPS 部署是等保 2.0 和密评中的常见要求,但网上大多数教程停留在"复制粘贴就跑"的 Demo 级别,缺乏生产环境所需的双证书策略、密码套件优先级、HSTS 和 OCSP Stapling 等关键环节。更麻烦的是,GmSSL 的编译流程、Nginx 的集成方式、证书链的签发逻辑,每一步都有坑。

本文基于 GM/T 0015-2012《SM2 数字证书格式规范》和 GM/T 0024-2014《SSL VPN 技术规范》两个核心标准,从源码编译开始,一步步完成:

  • 编译支持国密的 GmSSL(替代 OpenSSL)
  • 编译支持 GmSSL 的 Nginx
  • 生成 SM2 证书链(根 CA → 中间 CA → 终端实体证书)
  • 配置生产级 Nginx 国密 TLS(双栈兼容、密码套件调优、安全头)
  • 多客户端验证(curl、浏览器、Python 脚本)
跟着做,你能在 30 分钟内跑通一套生产可用的国密 HTTPS 服务。

一、环境准备

1.1 目标环境

项目版本
操作系统Ubuntu 22.04 LTS
GCC≥ 11.0
GmSSL3.1.x(基于 OpenSSL 3.x 引擎架构)
Nginx1.24.x / 1.26.x
为什么选 GmSSL 而不是 OpenSSL 3.x 原生国密? OpenSSL 3.x 虽然通过 Provider 机制支持了部分国密算法,但对 GM/T 0024-2014 定义的国密 TLS 密码套件(ECDHE-SM2-SM4-SM3ECC-SM2-SM4-SM3)支持不完整,且缺少 SM2 证书签名验证的完整实现。GmSSL 是国密局认可的参考实现,生产环境应优先使用。

1.2 安装编译依赖

BASH
sudo apt update
sudo apt install -y build-essential wget git \
    libpcre3-dev zlib1g-dev \
    perl cmake pkg-config

二、编译 GmSSL

2.1 下载源码

BASH
cd /usr/local/src
wget https://github.com/guanzhi/GmSSL/archive/refs/tags/v3.1.1.tar.gz
tar xzf v3.1.1.tar.gz
cd GmSSL-3.1.1
如果 GitHub 访问慢,可使用 Gitee 镜像:https://gitee.com/GmSSL/GmSSL

2.2 编译安装

2.3 配置环境变量(可选但推荐)

BASH
cat >> ~/.bashrc << 'EOF'
export PATH=/usr/local/gmssl/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/gmssl/lib:$LD_LIBRARY_PATH
export PKG_CONFIG_PATH=/usr/local/gmssl/lib/pkgconfig:$PKG_CONFIG_PATH
EOF
source ~/.bashrc

三、编译支持国密的 Nginx

Nginx 不直接支持 GmSSL,需要通过补丁或编译参数指定 GmSSL 路径。

3.1 下载 Nginx 源码

BASH
cd /usr/local/src
wget https://nginx.org/download/nginx-1.26.1.tar.gz
tar xzf nginx-1.26.1.tar.gz
cd nginx-1.26.1

3.2 编译配置

踩坑 1--with-openssl 指向的是 GmSSL 源码目录(不是安装目录),Nginx 会在编译时自动编译其中静态链接的 OpenSSL(即 GmSSL)。不要指向 /usr/local/gmssl,否则会找不到源码树。

3.3 验证 Nginx 国密支持

BASH
nginx -V 2>&1 | grep -o "GmSSL\|gmssl"
# 检查支持的密码套件
gmssl s_server -www -port 18443 \
    -cert /tmp/test.pem -key /tmp/test.key 2>&1 &
# 如果启动成功,说明 SSL 模块已正确链接

四、生成 SM2 证书链

根据 GM/T 0015-2012 规范,SM2 数字证书使用与 X.509 v3 兼容的格式,但签名算法为 SM2-with-SM3,公钥曲线为 sm2p256v1(OID 1.2.156.10197.1.301)。

4.1 创建证书目录结构

BASH
mkdir -p /etc/nginx/ssl/{root-ca,intermediate,server}
cd /etc/nginx/ssl

4.2 生成根 CA

BASH
# 根 CA 密钥(SM2 曲线)
gmssl ecparam -genkey -name sm2p256v1 -out root-ca/root-ca.key
gmssl ecparam -in root-ca/root-ca.key -text_noout | head -3
# 预期输出包含:ASN1 OID: sm2p256v1

# 生成自签名根 CA 证书(3650 天 = 10 年)
gmssl req -new -x509 -sm3 \
    -key root-ca/root-ca.key \
    -out root-ca/root-ca.crt \
    -days 3650 \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=Security/CN=Root CA SM2"

4.3 生成中间 CA

4.4 生成终端实体(服务器)证书

4.5 构建完整证书链

BASH
# 服务器证书链(服务器证书 + 中间 CA)
cat server/server.crt intermediate/intermediate.crt > server/fullchain.crt

# 完整信任链(用于客户端验证)
cat intermediate/intermediate.crt root-ca/root-ca.crt > server/ca-trust-chain.pem

# 验证证书链
gmssl verify -CAfile root-ca/root-ca.crt -untrusted intermediate/intermediate.crt server/server.crt
# 预期输出:server/server.crt: OK

# 查看证书详情
gmssl x509 -in server/server.crt -noout -text | grep -A2 "Signature Algorithm\|Public-Key\|Subject:"

4.6 一键生成脚本

将上述流程封装为脚本,方便后续证书轮换:

五、配置生产级 Nginx 国密 TLS

5.1 主配置文件

5.2 国密 SSL 站点配置

5.3 双栈配置(同时支持国密和 RSA/ECDSA)

生产环境通常需要同时支持国密客户端和标准客户端:

踩坑 2:Nginx 的 ssl_certificate 指令在 1.11.0+ 后支持多证书,但要求所有证书使用相同的 ssl_protocols。国密和密码套件由客户端和服务器协商决定,Nginx 会根据客户端支持的密码套件自动选择合适的证书。

5.4 生成 DH 参数

BASH
gmssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
# 这一步可能需要几分钟

5.5 测试配置并启动

六、客户端验证

6.1 使用 GmSSL 自带的 curl 验证

GmSSL 编译时会自带支持国密的 curl 工具:

6.2 使用 Python 验证

运行验证脚本:

BASH
chmod +x verify_sm2_tls.py
python3 verify_sm2_tls.py

6.3 浏览器验证

国密浏览器(如 360 安全浏览器、奇安信可信浏览器)原生支持国密 TLS。标准浏览器(Chrome/Firefox)需要额外配置:

BASH
# Chrome 启动时加载国密证书
google-chrome \
    --ignore-certificate-errors-spki-list=$(gmssl x509 -in /etc/nginx/ssl/root-ca/root-ca.crt -noout -pubkey | gmssl pkey -pubin -outform der | gmssl dgst -sha256 -binary | base64) \
    https://gm.example.com:8443
踩坑 3:标准 Chrome/Firefox 不支持 ECDHE-SM2-SM4-SM3 密码套件,即使安装了 SM2 根证书也无法完成握手。这是密码套件层面的限制,不是证书问题。国密 HTTPS 必须使用国密浏览器或通过网关转换。

6.4 使用 Wireshark 抓包验证

七、生产环境注意事项

7.1 证书轮换策略

7.2 关键安全配置清单

配置项推荐值说明
ssl_protocolsTLSv1.2 TLSv1.3禁用 TLS 1.0/1.1
ssl_prefer_server_cipherson服务端控制密码套件优先级
ssl_session_ticketsoff确保前向保密
ssl_staplingonOCSP 装订,减少客户端验证延迟
HSTS max-age63072000 (2年)强制 HTTPS
证书签名算法SM2-with-SM3符合 GM/T 0015-2012
密钥长度SM2 256-bit等效 RSA 3072-bit 安全强度
证书有效期≤ 398 天行业标准建议

7.3 性能对比参考

SM2 与 RSA/ECDSA 在 TLS 握手阶段的性能对比(测试环境:Intel Xeon Gold 6248, 1000 次握手平均值):

算法密钥长度签名速度验证速度TLS 握手时间
RSA-20482048-bit基准 (1x)基准 (1x)基准 (1x)
RSA-30723072-bit0.4x1.1x1.8x
ECDSA-P256256-bit15x8x0.6x
SM2256-bit12x7x0.7x
SM2 签名速度显著快于 RSA,与 ECDSA-P256 相当。验证速度略低于 ECDSA,但整体握手性能优于 RSA-2048。

7.4 常见问题排查

问题 1:SSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure

原因:客户端和服务端没有共同支持的密码套件。 排查:

BASH
# 查看服务端支持的套件
gmssl ciphers -v 'ECDHE-SM2-SM4-SM3:ECC-SM2-SM4-SM3'

# 查看客户端 Client Hello 中的套件列表
gmssl s_client -connect host:8443 -msg 2>&1 | grep -A20 "Cipher Suites"

问题 2:unable to verify the first certificate

原因:客户端缺少中间 CA 证书或根 CA 证书。 解决:

BASH
# 确保使用完整的证书链文件
gmssl s_client -connect host:8443 -CAfile fullchain-ca.pem

问题 3:Nginx 启动报错 SSL_CTX_use_certificate:ee key too small

原因:GmSSL 的安全级别要求与 Nginx 配置冲突。 解决:在 nginx.confhttp 块中添加:

NGINX
ssl_conf_command Options = PrioritizeChaCha;

八、总结

完整的国密 HTTPS 部署流程可以归纳为:

CODE
源码编译 GmSSL → 源码编译 Nginx(链接 GmSSL)
    ↓
生成 SM2 证书链(根 CA → 中间 CA → 服务器证书)
    ↓
配置 Nginx(密码套件、安全头、OCSP Stapling)
    ↓
多客户端验证(gmssl s_client、Python 脚本、国密浏览器)
    ↓
生产运维(证书轮换、监控告警、安全加固)

关键要点:

  • GmSSL 是核心:不要用 OpenSSL 3.x 的 Provider 机制替代 GmSSL,国密 TLS 密码套件支持不完整
  • 证书链必须完整:GM/T 0015-2012 规定的证书格式与 X.509 v3 兼容,但签名算法是 SM2-with-SM3,验证时必须使用 GmSSL 工具链
  • 双栈部署是常态:生产环境通常需要同时支持国密客户端和标准客户端,通过 Nginx 多证书配置实现
  • 密码套件协商:GM/T 0024-2014 定义了 ECDHE-SM2-SM4-SM3 等套件,优先选择支持前向保密的 ECDHE 变体
  • 浏览器兼容性:标准浏览器不支持国密密码套件,国密 HTTPS 必须配合国密浏览器或通过国密网关转换

参考来源

  • GM/T 0015-2012《SM2 数字证书格式规范》— 国家密码管理局
  • GM/T 0024-2014《SSL VPN 技术规范》— 国家密码管理局
  • GmSSL 官方仓库:https://github.com/guanzhi/GmSSL
  • Nginx 官方文档:https://nginx.org/en/docs/http/configuring_https_servers.html
  • OSCCA 国密标准查询:https://www.oscca.gov.cn