SM2 国密算法实战:从编译国密 OpenSSL 到生产级 TLS 部署全流程
前言
国密 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 脚本)
一、环境准备
1.1 目标环境
| 项目 | 版本 |
|---|---|
| 操作系统 | Ubuntu 22.04 LTS |
| GCC | ≥ 11.0 |
| GmSSL | 3.1.x(基于 OpenSSL 3.x 引擎架构) |
| Nginx | 1.24.x / 1.26.x |
为什么选 GmSSL 而不是 OpenSSL 3.x 原生国密? OpenSSL 3.x 虽然通过 Provider 机制支持了部分国密算法,但对 GM/T 0024-2014 定义的国密 TLS 密码套件(ECDHE-SM2-SM4-SM3、ECC-SM2-SM4-SM3)支持不完整,且缺少 SM2 证书签名验证的完整实现。GmSSL 是国密局认可的参考实现,生产环境应优先使用。
1.2 安装编译依赖
sudo apt update
sudo apt install -y build-essential wget git \
libpcre3-dev zlib1g-dev \
perl cmake pkg-config二、编译 GmSSL
2.1 下载源码
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 编译安装
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/gmssl \
-DENABLE_SM2=ON \
-DENABLE_SM3=ON \
-DENABLE_SM4=ON \
-DENABLE_SM9=ON \
-DBUILD_SHARED_LIBS=ON
make -j$(nproc)
sudo make install
# 配置动态库路径
echo "/usr/local/gmssl/lib" | sudo tee /etc/ld.so.conf.d/gmssl.conf
sudo ldconfig
# 验证安装
/usr/local/gmssl/bin/gmssl version
# 预期输出:GmSSL 3.1.12.3 配置环境变量(可选但推荐)
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 源码
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.13.2 编译配置
./auto/configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_v3_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre \
--with-file-aio \
--with-threads \
--with-stream \
--with-stream_ssl_module \
--with-openssl=/usr/local/src/GmSSL-3.1.1 \
--with-cc-opt="-I/usr/local/gmssl/include" \
--with-ld-opt="-L/usr/local/gmssl/lib -Wl,-rpath,/usr/local/gmssl/lib"
make -j$(nproc)
sudo make install踩坑 1:--with-openssl指向的是 GmSSL 源码目录(不是安装目录),Nginx 会在编译时自动编译其中静态链接的 OpenSSL(即 GmSSL)。不要指向/usr/local/gmssl,否则会找不到源码树。
3.3 验证 Nginx 国密支持
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 创建证书目录结构
mkdir -p /etc/nginx/ssl/{root-ca,intermediate,server}
cd /etc/nginx/ssl4.2 生成根 CA
# 根 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
# 中间 CA 密钥
gmssl ecparam -genkey -name sm2p256v1 -out intermediate/intermediate.key
# 中间 CA CSR
gmssl req -new -sm3 \
-key intermediate/intermediate.key \
-out intermediate/intermediate.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=Security/CN=Intermediate CA SM2"
# 用根 CA 签发中间 CA 证书(1825 天 = 5 年)
cat > intermediate/ext.cnf << 'EOF'
[v3_intermediate_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
EOF
gmssl x509 -req -sm3 \
-in intermediate/intermediate.csr \
-CA root-ca/root-ca.crt \
-CAkey root-ca/root-ca.key \
-CAcreateserial \
-out intermediate/intermediate.crt \
-days 1825 \
-extfile intermediate/ext.cnf \
-extensions v3_intermediate_ca4.4 生成终端实体(服务器)证书
# 服务器密钥
gmssl ecparam -genkey -name sm2p256v1 -out server/server.key
# 服务器 CSR
gmssl req -new -sm3 \
-key server/server.key \
-out server/server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=Web/CN=gm.example.com"
# 用中间 CA 签发服务器证书(365 天)
cat > server/ext.cnf << 'EOF'
[v3_server]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = gm.example.com
DNS.2 = *.gm.example.com
IP.1 = 192.168.1.100
EOF
gmssl x509 -req -sm3 \
-in server/server.csr \
-CA intermediate/intermediate.crt \
-CAkey intermediate/intermediate.key \
-CAcreateserial \
-out server/server.crt \
-days 365 \
-extfile server/ext.cnf \
-extensions v3_server4.5 构建完整证书链
# 服务器证书链(服务器证书 + 中间 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 一键生成脚本
将上述流程封装为脚本,方便后续证书轮换:
#!/bin/bash
# /usr/local/bin/gen-sm2-cert.sh
# 用法:gen-sm2-cert.sh <域名> <IP> <有效期天数>
set -euo pipefail
DOMAIN="${1:?Usage: $0 <domain> <ip> [days]}"
IP="${2:-127.0.0.1}"
DAYS="${3:-365}"
SSL_DIR="/etc/nginx/ssl"
CA_DIR="${SSL_DIR}/intermediate"
# 生成密钥
gmssl ecparam -genkey -name sm2p256v1 \
-out "${SSL_DIR}/server/${DOMAIN}.key"
# 生成 CSR
gmssl req -new -sm3 \
-key "${SSL_DIR}/server/${DOMAIN}.key" \
-out "${SSL_DIR}/server/${DOMAIN}.csr" \
-subj "/C=CN/ST=Beijing/O=MyOrg/CN=${DOMAIN}"
# 生成扩展配置
cat > "${SSL_DIR}/server/${DOMAIN}.ext.cnf" << EXTEOF
[v3_server]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:${DOMAIN},DNS:*.${DOMAIN},IP:${IP}
EXTEOF
# 签发证书
gmssl x509 -req -sm3 \
-in "${SSL_DIR}/server/${DOMAIN}.csr" \
-CA "${CA_DIR}/intermediate.crt" \
-CAkey "${CA_DIR}/intermediate.key" \
-CAcreateserial \
-out "${SSL_DIR}/server/${DOMAIN}.crt" \
-days "${DAYS}" \
-extfile "${SSL_DIR}/server/${DOMAIN}.ext.cnf" \
-extensions v3_server
# 构建证书链
cat "${SSL_DIR}/server/${DOMAIN}.crt" \
"${CA_DIR}/intermediate.crt" \
> "${SSL_DIR}/server/${DOMAIN}-fullchain.crt"
# 验证
gmssl verify \
-CAfile "${SSL_DIR}/root-ca/root-ca.crt" \
-untrusted "${CA_DIR}/intermediate.crt" \
"${SSL_DIR}/server/${DOMAIN}.crt"
echo "✅ 证书已生成:${SSL_DIR}/server/${DOMAIN}-fullchain.crt"五、配置生产级 Nginx 国密 TLS
5.1 主配置文件
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" ssl=$ssl_protocol '
'cipher=$ssl_cipher';
access_log /var/log/nginx/access.log main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 10m;
# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml;
# 国密 SSL 配置
include /etc/nginx/conf.d/*.conf;
}5.2 国密 SSL 站点配置
# /etc/nginx/conf.d/gm-ssl.conf
# ===== 国密 HTTPS 服务器 =====
server {
listen 8443 ssl;
server_name gm.example.com *.gm.example.com;
# === 证书配置 ===
# SM2 签名证书(用于身份认证)
ssl_certificate /etc/nginx/ssl/server/gm.example.com-fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/server/gm.example.com.key;
# === SSL 协议与密码套件 ===
# GM/T 0024-2014 定义的国密 TLS 密码套件
# 优先使用 ECDHE-SM2-SM4-SM3(支持前向保密)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "ECDHE-SM2-SM4-SM3:ECC-SM2-SM4-SM3:ECDHE-SM2-WRAPPED-SM4-SM3";
ssl_prefer_server_ciphers on;
# === 会话缓存 ===
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off; # 生产环境关闭 session tickets,确保前向保密
# === DH 参数(ECDHE 密钥交换) ===
# gmssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# === 安全响应头 ===
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# === OCSP Stapling ===
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/server/ca-trust-chain.pem;
resolver 8.8.8.8 114.114.114.114 valid=300s;
resolver_timeout 5s;
# === 站点根目录 ===
root /var/www/gm.example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# 健康检查端点(不需要 TLS 验证)
location /health {
access_log off;
return 200 "OK\n";
}
}
# === HTTP 自动跳转 HTTPS ===
server {
listen 80;
server_name gm.example.com *.gm.example.com;
return 301 https://$host:8443$request_uri;
}5.3 双栈配置(同时支持国密和 RSA/ECDSA)
生产环境通常需要同时支持国密客户端和标准客户端:
# /etc/nginx/conf.d/dual-stack.conf
# 国密优先服务器(8443 端口)
server {
listen 8443 ssl;
server_name gm.example.com;
# 国密 SM2 证书
ssl_certificate /etc/nginx/ssl/server/sm2-fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/server/sm2-server.key;
# 同时配置 RSA 证书作为 fallback(需要 Nginx 1.11+)
ssl_certificate /etc/nginx/ssl/server/rsa-fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/server/rsa-server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "ECDHE-SM2-SM4-SM3:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384";
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
root /var/www/gm.example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}踩坑 2:Nginx 的ssl_certificate指令在 1.11.0+ 后支持多证书,但要求所有证书使用相同的ssl_protocols。国密和密码套件由客户端和服务器协商决定,Nginx 会根据客户端支持的密码套件自动选择合适的证书。
5.4 生成 DH 参数
gmssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
# 这一步可能需要几分钟5.5 测试配置并启动
# 测试配置语法
nginx -t
# 创建站点目录
mkdir -p /var/www/gm.example.com
cat > /var/www/gm.example.com/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>国密 HTTPS 测试</title></head>
<body>
<h1>✅ 国密 HTTPS 部署成功</h1>
<p>如果您能看到此页面,说明 SM2 TLS 握手成功。</p>
</body>
</html>
EOF
# 启动 Nginx
nginx
# 检查是否启动成功
ss -tlnp | grep -E "8443|80"六、客户端验证
6.1 使用 GmSSL 自带的 curl 验证
GmSSL 编译时会自带支持国密的 curl 工具:
# 使用 GmSSL 的 curl(如果编译了的话)
/usr/local/gmssl/bin/gmssl s_client \
-connect gm.example.com:8443 \
-CAfile /etc/nginx/ssl/server/ca-trust-chain.pem \
-cipher "ECDHE-SM2-SM4-SM3" \
-servername gm.example.com << EOF
GET / HTTP/1.1
Host: gm.example.com
Connection: close
EOF
# 查看完整握手信息
/usr/local/gmssl/bin/gmssl s_client \
-connect gm.example.com:8443 \
-CAfile /etc/nginx/ssl/server/ca-trust-chain.pem \
-state -debug 2>&1 | grep -E "SSL_connect|Cipher|Protocol|Verify"6.2 使用 Python 验证
#!/usr/bin/env python3
"""
verify_sm2_tls.py - 国密 TLS 连接验证脚本
验证 SM2 证书链、密码套件、证书有效期
"""
import ssl
import socket
import sys
from datetime import datetime
# 注意:标准 Python ssl 模块不支持国密套件
# 以下脚本使用 GmSSL 的 Python 绑定或 subprocess 方式验证
import subprocess
def verify_cert_chain(chain_path: str, ca_path: str) -> bool:
"""验证证书链完整性"""
result = subprocess.run(
["gmssl", "verify", "-CAfile", ca_path, chain_path],
capture_output=True, text=True
)
ok = "OK" in result.stdout
print(f" 证书链验证: {'✅ 通过' if ok else '❌ 失败'}")
if not ok:
print(f" {result.stderr.strip()}")
return ok
def check_cert_dates(cert_path: str) -> None:
"""检查证书有效期"""
result = subprocess.run(
["gmssl", "x509", "-in", cert_path, "-noout", "-dates"],
capture_output=True, text=True
)
for line in result.stdout.strip().split("\n"):
print(f" {line}")
def test_tls_connection(host: str, port: int, ca_path: str) -> bool:
"""测试 TLS 连接并获取握手信息"""
print(f"\n🔗 测试 TLS 连接: {host}:{port}")
# 构建 s_client 命令
cmd = [
"gmssl", "s_client",
"-connect", f"{host}:{port}",
"-CAfile", ca_path,
"-servername", host,
"-brief"
]
result = subprocess.run(cmd, capture_output=True, text=True, input="Q\n")
output = result.stdout + result.stderr
# 解析关键信息
protocol = "Unknown"
cipher = "Unknown"
verified = False
for line in output.split("\n"):
line = line.strip()
if line.startswith("Protocol :"):
protocol = line.split(":", 1)[1].strip()
elif line.startswith("Cipher :"):
cipher = line.split(":", 1)[1].strip()
elif "Verification: OK" in line:
verified = True
print(f" TLS 协议: {protocol}")
print(f" 密码套件: {cipher}")
print(f" 证书验证: {'✅ 通过' if verified else '❌ 失败'}")
return verified
def main():
HOST = "gm.example.com"
PORT = 8443
CERT_DIR = "/etc/nginx/ssl"
print("=" * 60)
print("国密 TLS 部署验证")
print("=" * 60)
# 1. 验证证书链
print("\n📜 证书链验证")
verify_cert_chain(
f"{CERT_DIR}/server/gm.example.com.crt",
f"{CERT_DIR}/root-ca/root-ca.crt"
)
# 2. 检查证书有效期
print("\n📅 证书有效期")
check_cert_dates(f"{CERT_DIR}/server/gm.example.com.crt")
# 3. 检查中间 CA 有效期
print("\n📅 中间 CA 有效期")
check_cert_dates(f"{CERT_DIR}/intermediate/intermediate.crt")
# 4. 测试 TLS 连接
tls_ok = test_tls_connection(
HOST, PORT,
f"{CERT_DIR}/server/ca-trust-chain.pem"
)
# 5. 总结
print("\n" + "=" * 60)
if tls_ok:
print("✅ 国密 TLS 部署验证全部通过")
else:
print("❌ 国密 TLS 部署存在问题,请检查配置")
print("=" * 60)
if __name__ == "__main__":
main()运行验证脚本:
chmod +x verify_sm2_tls.py
python3 verify_sm2_tls.py6.3 浏览器验证
国密浏览器(如 360 安全浏览器、奇安信可信浏览器)原生支持国密 TLS。标准浏览器(Chrome/Firefox)需要额外配置:
# 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 抓包验证
# 抓取 TLS 握手包
sudo tcpdump -i any -w /tmp/sm2-tls.pcap port 8443 &
# 发起连接
gmssl s_client -connect gm.example.com:8443 \
-CAfile /etc/nginx/ssl/server/ca-trust-chain.pem
# 停止抓包
kill %1
# 分析:在 Wireshark 中过滤 tls.handshake.type == 1 (Client Hello)
# 查看 Supported Cipher Suites 中是否包含国密套件
# GM/T 0024-2014 定义的密码套件 TLS 代码点:
# ECDHE-SM2-SM4-SM3: 0xE011 (老) 或 0xE051 (新)
# ECC-SM2-SM4-SM3: 0xE013 (老) 或 0xE053 (新)七、生产环境注意事项
7.1 证书轮换策略
# 证书续期自动化(建议通过 cron 每月检查)
#!/bin/bash
# /usr/local/bin/check-cert-expiry.sh
CERT="/etc/nginx/ssl/server/gm.example.com.crt"
DAYS_BEFORE_EXPIRY=30
# 获取到期日期
EXPIRY=$(gmssl x509 -in "$CERT" -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ "$DAYS_LEFT" -lt "$DAYS_BEFORE_EXPIRY" ]; then
echo "⚠️ 证书将在 ${DAYS_LEFT} 天后过期,开始续期..."
/usr/local/bin/gen-sm2-cert.sh gm.example.com 192.168.1.100 365
nginx -s reload
echo "✅ 证书已续期并重新加载 Nginx"
else
echo "✅ 证书有效,剩余 ${DAYS_LEFT} 天"
fi7.2 关键安全配置清单
| 配置项 | 推荐值 | 说明 |
|---|---|---|
ssl_protocols | TLSv1.2 TLSv1.3 | 禁用 TLS 1.0/1.1 |
ssl_prefer_server_ciphers | on | 服务端控制密码套件优先级 |
ssl_session_tickets | off | 确保前向保密 |
ssl_stapling | on | OCSP 装订,减少客户端验证延迟 |
HSTS max-age | 63072000 (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-2048 | 2048-bit | 基准 (1x) | 基准 (1x) | 基准 (1x) |
| RSA-3072 | 3072-bit | 0.4x | 1.1x | 1.8x |
| ECDSA-P256 | 256-bit | 15x | 8x | 0.6x |
| SM2 | 256-bit | 12x | 7x | 0.7x |
SM2 签名速度显著快于 RSA,与 ECDSA-P256 相当。验证速度略低于 ECDSA,但整体握手性能优于 RSA-2048。
7.4 常见问题排查
问题 1:SSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
原因:客户端和服务端没有共同支持的密码套件。 排查:
# 查看服务端支持的套件
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 证书。 解决:
# 确保使用完整的证书链文件
gmssl s_client -connect host:8443 -CAfile fullchain-ca.pem问题 3:Nginx 启动报错 SSL_CTX_use_certificate:ee key too small
原因:GmSSL 的安全级别要求与 Nginx 配置冲突。
解决:在 nginx.conf 的 http 块中添加:
ssl_conf_command Options = PrioritizeChaCha;八、总结
完整的国密 HTTPS 部署流程可以归纳为:
源码编译 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