国密SSL/TLS证书申请与部署全流程:从CSR生成到Nginx配置的完整实战
title: "国密SSL/TLS证书申请与部署全流程:从CSR生成到Nginx配置的完整实战" slug: "gm-ssl-certificate-deployment-full-process" excerpt: "从SM2密钥对生成、CSR证书签名请求制作,到CA签发、Nginx国密双证书部署及双向认证配置,提供可直接复用的完整工程流程与生产环境踩坑记录。" category: gm tags: - SM2 - 国密 - TLS - 证书 - Nginx
前言
国密改造已进入深水区。很多团队卡在证书申请与部署环节——不是不会配Nginx,而是不知道CSR怎么生成、双证书怎么用、双向认证怎么做。本文以真实项目为背景,从零开始走完全流程,重点讲清楚每个环节的技术细节和常见陷阱。
环境准备
工具链清单:
| 工具 | 用途 | 版本要求 |
|---|---|---|
| Tongsuo | 国密OpenSSL实现 | >= 1.1.1l |
| gmssl | Python国密库 | >= 3.2.0 |
| OpenSSL CLI | 证书转换与验证 | >= 3.0 |
| cryptography | Python密码学库 | >= 43.0 |
- 已申请到有效的SM2服务器证书(含私钥)
- 已申请到有效的SM2客户端证书(含私钥)
- 根证书CA链完整
步骤一:生成SM2密钥对与CSR
使用Tongsuo生成SM2密钥对
# 生成SM2私钥(输出为Tongsuo原生格式)
tongsuo ecparam -genkey -name sm2p256v1 -out server.key
# 查看私钥内容确认生成成功
tongsuo ec -in server.key -text -noout生成CSR(证书签名请求)
# 生成CSR,需提供完整DN信息
tongsuo req -new -key server.key -out server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=YourCompany/OU=IT Dept/CN=yourdomain.com" \
-config <(cat <<EOF
[req]
default_bits = 256
prompt = no
distinguished_name = dn
req_extensions = v3_req
[dn]
C = CN
ST = Beijing
L = Beijing
O = YourCompany
OU = IT Dept
CN = yourdomain.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = yourdomain.com
DNS.2 = www.yourdomain.com
IP.1 = 192.168.1.100
EOF
)⚠️ 常见陷阱:
- CSR中的CN必须与服务器域名完全一致,否则浏览器会报证书名称不匹配
- SAN扩展必须包含所有需要保护的域名,缺少SAN会导致部分客户端拒绝连接
- 国密CSR不支持通配符域名(如
*.example.com),需逐个列出
使用Python验证CSR
from gmssl.sm2 import CryptSM2
from gmssl import func
# 注意:gmssl库的CryptSM2使用硬编码的user_id '1234567812345678'
# 如需自定义user_id,请使用Tongsuo或BabaSSL
print("gmssl库已加载,可进行SM2操作")
## 步骤二:证书申请与获取
### 向CA提交CSR查看CSR内容确认信息正确
tongsuo req -in server.csr -noout -text提交到CA(具体流程因CA而异,以下为示例)
通常通过CA提供的Web界面或API提交CSR内容
### 接收并验证书从CA获取证书后,验证证书信息
tongsuo x509 -in server.crt -noout -text验证证书链完整性
tongsuo verify -CAfile ca-chain.crt server.crt**证书验证要点:**
1. 检查证书有效期是否在预期范围内
2. 验证Subject字段与CSR中的信息一致
3. 检查Issuer字段是否来自可信CA
4. 确认Key Usage扩展包含digital signature和key encipherment
5. 验证Extended Key Usage包含serverAuth
## 步骤三:Nginx国密TLS配置
### 基础国密TLS配置**⚠️ 关键说明:**
- 国密TLS使用双证书机制:签名证书用于身份认证,加密证书用于密钥交换
- 签名证书对应GM/T 0024-2023中的SM2签名算法
- 加密证书对应GM/T 0024-2023中的SM2加密算法
- Tongsuo会自动根据客户端支持的算法协商选择证书
### 双证书配置详解方式一:双证书分开配置(推荐)
ssl_certificate /path/to/sign.crt; ssl_certificate_key /path/to/sign.key; ssl_certificate /path/to/encrypt.crt; ssl_certificate_key /path/to/encrypt.key;方式二:双证书合并配置
将签名证书和加密证书按顺序合并到一个文件
证书顺序:签名证书在前,加密证书在后
cat sign.crt encrypt.crt > combined.crt注意:Nginx只支持一个ssl_certificate_key指令,私钥应为签名私钥
ssl_certificate /path/to/combined.crt; ssl_certificate_key /path/to/sign.key;**⚠️ 生产环境注意:**
- 双证书私钥文件权限必须设置为600,且属主为nginx运行用户
- 证书文件建议存放在独立的安全目录,避免被其他进程读取
## 步骤四:双向认证(mTLS)配置
### 服务端配置### 客户端配置客户端也需要配置自己的证书
upstream backend { server backend1:8443; server backend2:8443; }server { listen 80; server_name client.yourdomain.com; location / { proxy_pass https://backend; # 客户端证书 proxy_ssl_certificate /path/to/client_sign.crt; proxy_ssl_certificate_key /path/to/client_sign.key; proxy_ssl_certificate /path/to/client_encrypt.crt; proxy_ssl_certificate_key /path/to/client_encrypt.key; # 服务端CA证书(用于验证服务端证书) proxy_ssl_trusted_certificate /path/to/server-ca.crt; # 启用双向认证 proxy_ssl_verify on; proxy_ssl_verify_depth 2; } }
**⚠️ 双向认证常见问题:**
1. **客户端证书未被信任**:检查`ssl_client_certificate`是否正确指向客户端CA证书链
2. **证书链不完整**:确保服务端提供的证书链包含中间CA证书
3. **CRL检查失败**:如果配置了CRL,确保CRL文件有效且未被吊销
4. **证书过期**:检查所有证书(服务端、客户端、CA)的有效期
## 步骤五:验证与测试
### 使用OpenSSL验证连接测试服务端TLS握手(国密)
tongsuo s_client -connect yourdomain.com:443 -tls1_2测试双向认证
tongsuo s_client -connect yourdomain.com:443 \ -cert client_sign.crt \ -key client_sign.key \ -cert client_encrypt.crt \ -key client_encrypt.key查看握手详情
tongsuo s_client -connect yourdomain.com:443 -tls1_2 -msg### 使用Python验证创建国密SSL上下文
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.check_hostname = False ctx.verify_mode = ssl.CERT_REQUIRED加载客户端证书
ctx.load_cert_chain('client_sign.crt', 'client_sign.key') ctx.load_verify_locations('server-ca.crt')建立连接
with ctx.wrap_socket(socket.socket(), server_hostname='yourdomain.com') as s: cert = s.getpeercert() print(f"服务器证书信息:") print(f" Subject: {cert['subject']}") print(f" Issuer: {cert['issuer']}") print(f" Not Before: {cert['notBefore']}") print(f" Not After: {cert['notAfter']}") # 读取响应 s.sendall(b'GET / HTTP/1.1\r\nHost: yourdomain.com\r\n\r\n') response = s.recv(4096) print(response.decode())### 性能基准测试使用Tongsuo进行性能测试
tongsuo s_client -connect yourdomain.com:443 \ -tls1_2 -num 1000 -quiet使用wrk进行HTTP压测
wrk -t4 -c100 -d30s --latency \ https://yourdomain.com/**预期性能指标(参考值,具体性能受硬件配置、内核参数影响):**
- SM2握手延迟:约50-100ms(首次握手,含算法协商)
- SM4-GCM加密吞吐:约1-3 Gbps(启用硬件加速时)
- 每秒握手次数:约300-600次/秒
> **注意**:以上数据为典型值参考,实际性能需根据具体硬件环境测试。
## 常见踩坑记录
### 坑一:双证书配置顺序错误
**现象:** 客户端无法完成握手,报错`certificate verify failed`
**原因:** 双证书文件中证书顺序错误,导致服务端无法正确选择签名证书或加密证书
**解决:** 确保签名证书在前,加密证书在后,私钥顺序与证书顺序对应
### 坑二:SAN扩展缺失
**现象:** Chrome浏览器报错`ERR_CERT_COMMON_NAME_INVALID`
**原因:** CSR中未包含SAN扩展,或SAN中缺少实际访问的域名
**解决:** 重新生成CSR,确保包含所有需要的域名和IP地址
### 坑三:客户端证书格式不兼容
**现象:** 服务端配置了`ssl_verify_client on`,但客户端连接时报错
**原因:** 客户端证书格式不符合要求,或使用了对服务端不可信 CA 签发的证书
**解决:**
1. 确认客户端证书由服务端信任的CA签发
2. 确保证书格式为X.509 v3
3. 检查证书链完整性
### 坑四:密码套件协商失败
**现象:** 握手失败,报错`no shared cipher`
**原因:** 服务端和客户端支持的国密密码套件没有交集
**解决:**
1. 检查服务端`ssl_ciphers`配置
2. 确认客户端支持相应的密码套件
3. 使用`openssl ciphers`命令查看可用套件列表查看Tongsuo支持的国密密码套件
tongsuo ciphers -v | grep -i sm ```总结
国密SSL/TLS证书申请与部署涉及多个环节,每个环节都有需要注意的细节。本文覆盖了从密钥生成、CSR制作、证书申请到Nginx配置和双向认证验证的完整流程。关键点总结:
- 双证书机制:国密TLS使用签名证书和加密证书分离的设计,配置时需正确处理
- SAN扩展:确保证书包含所有需要保护的域名
- 双向认证:合理配置客户端证书验证,平衡安全与可用性
- 性能考虑:国密算法性能与硬件加速密切相关,生产环境建议启用硬件加速