1. 项目概述:为什么我们需要亲手制作国密SM2证书?
如果你是一名开发者、运维工程师或者对国产密码技术感兴趣的技术爱好者,最近可能频繁听到“国密”、“信创”、“自主可控”这些词。尤其是在一些对数据安全有严格要求的行业,比如金融、政务、能源等领域,使用符合国家密码管理局标准的国密算法,已经从“可选项”变成了“必选项”。而国密算法体系中,SM2椭圆曲线公钥密码算法,正是用来替代我们熟知的RSA算法的核心。
那么,SM2证书是什么?简单来说,它就是一张基于SM2算法的“数字身份证”。和我们平时为网站配置的HTTPS证书(基于RSA或ECC)功能一样,用于身份认证和建立加密通道。但它的“内核”换成了我们自己的国密算法。直接使用现成的商业国密证书当然方便,但成本高、流程长。对于内部测试、开发环境、或者一些特定场景下的自建PKI体系,自己动手用GmSSL制作SM2证书,就成了一个必须掌握的技能。
我最近就因为一个内部系统的国密改造项目,从头到尾折腾了一遍GmSSL制作SM2证书的全流程。网上资料虽然不少,但大多零散,或是基于老版本,实操时各种坑。这篇文章,我就把我踩过的坑、验证过的步骤和核心原理,整理成一份详尽的指南。无论你是想为Nginx配置国密HTTPS,还是为你的Java/Python应用添加国密双向认证,甚至是搭建一个小的测试CA,这篇从生成私钥到签发证书的“保姆级”教程,都能让你避开弯路,直达目标。
2. 核心工具解析:深入理解GmSSL
在动手之前,我们得先搞清楚手里的“兵器”——GmSSL。它不是一个凭空出现的新工具,理解了它的来龙去脉和设计哲学,使用起来才会得心应手。
2.1 GmSSL的定位与优势
GmSSL本质上是一个密码工具箱,而不是一个全新的、独立的密码库。它的一个关键身份是OpenSSL的一个分支(fork)。这个定位非常巧妙,带来了几个核心优势:
- 接口兼容性:这是GmSSL最大的亮点之一。它保持了与OpenSSL高度兼容的API和命令行工具接口。这意味着,一个原本使用OpenSSL进行SSL/TLS通信、证书处理的应用,在绝大多数情况下,只需要将链接的库从
libssl/libcrypto换成GmSSL的库,甚至只是将系统路径下的openssl命令行工具替换为gmssl,就能无缝切换到国密算法栈,而无需修改大量代码。这极大地降低了国密改造的迁移成本。 - 算法全集支持:GmSSL完整支持了国家密码管理局已公开的所有国密算法标准,包括SM2(椭圆曲线公钥密码)、SM3(密码杂凑算法)、SM4(分组密码)、SM9(标识密码)以及ZUC(流密码)。它是一个“一站式”的国密算法实现库。
- 协议栈支持:它不仅提供算法,还实现了基于国密算法的完整安全协议栈,最核心的就是支持SM2证书的SSL/TLS协议。这意味着你可以用GmSSL编译出支持国密HTTPS的Nginx(国密Nginx)、curl等服务器和客户端软件。
- 活跃的开源社区:由北京大学密码学研究组主导开发和维护,在GitHub上持续更新,这意味着它能跟进最新的安全补丁和国密标准演进,比一些闭源的商业实现更透明、更值得信赖。
2.2 版本选择与安装避坑指南
GmSSL目前主要有两个大版本分支:GmSSL 2.x 和 GmSSL 3.x。它们的区别不小,选错了版本,后面的命令可能完全对不上。
- GmSSL 2.x:相对旧的稳定分支,构建系统基于传统的OpenSSL风格(使用
config或Configure脚本)。文档和网上大部分教程基于这个版本。它的兼容性更广,但代码相对老旧。 - GmSSL 3.x:新的开发主干,目标是更现代、更高效、更安全。它用CMake替代了旧的构建系统,并且为了追求更小的体积和更高的性能,移除了对一些老旧操作系统和架构的支持。国密Nginx项目通常基于GmSSL 3.x开发。
对于新手,我强烈建议从GmSSL 3.x开始。因为它代表了未来方向,且与国密生态(如国密Nginx)结合更紧密。下面以在Linux系统(如Ubuntu 20.04/22.04)上编译安装GmSSL 3.x为例,讲解关键步骤和避坑点。
# 1. 获取源代码 git clone https://github.com/guanzhi/GmSSL.git cd GmSSL # 2. 切换到最新的3.x分支(例如develop分支,或查看最新的tag) git checkout master # 或者 git checkout <某个3.x的tag,如v3.1.0> # 3. 创建构建目录并进入 mkdir build cd build # 4. 使用CMake配置。这里有几个关键参数: # -DCMAKE_INSTALL_PREFIX:指定安装路径,建议用/usr/local/gmssl,避免污染系统默认路径。 # -DBUILD_SHARED_LIBS=ON:编译动态库,方便其他程序链接。 # -DENABLE_SM2=ON等:确保国密算法被启用(默认就是ON)。 cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/gmssl -DBUILD_SHARED_LIBS=ON # 5. 编译并安装 make -j$(nproc) # 使用多核编译加快速度 sudo make install安装后的关键操作:
- 环境变量配置:为了让系统找到我们安装的GmSSL,需要将安装目录下的
bin和lib目录加入环境变量。# 编辑 ~/.bashrc 或 ~/.zshrc echo 'export PATH=/usr/local/gmssl/bin:$PATH' >> ~/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/gmssl/lib:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc - 验证安装:执行
gmssl version。如果成功,你会看到类似GmSSL 3.1.0 - OpenSSL 1.1.1w的输出,其中包含了GmSSL自己的版本和兼容的OpenSSL版本号。再执行gmssl ecparam -list_curves | grep -i sm2,应该能看到SM2相关的椭圆曲线参数,如SM2。
避坑提示1:动态库链接问题这是最常见的问题。编译安装后,运行
gmssl命令可能报错:gmssl: error while loading shared libraries: libgmssl.so.3: cannot open shared object file。解决方法:除了设置LD_LIBRARY_PATH,更一劳永逸的方法是更新系统的动态库缓存。# 创建配置文件,告诉系统在 /usr/local/gmssl/lib 目录下搜索动态库 echo '/usr/local/gmssl/lib' | sudo tee /etc/ld.so.conf.d/gmssl.conf # 更新缓存 sudo ldconfig之后再次运行
gmssl命令就应该正常了。
避坑提示2:与系统OpenSSL共存我们安装GmSSL到自定义目录,就是为了不影响系统自带的OpenSSL。两者可以完美共存。你的应用如果想用国密,就链接
-lgmssl并引用GmSSL的头文件;想用国际算法,继续用系统OpenSSL即可。命令行下,通过gmssl和openssl两个不同的命令来区分调用。
3. SM2证书制作全流程实操
掌握了工具,我们进入核心环节:从零开始制作一张SM2证书。这里我以创建一个简单的“根CA -> 签发服务器证书”的两级体系为例,这是理解证书链和PKI的基础。
3.1 第一步:生成SM2私钥
私钥是所有安全操作的基石,必须妥善保管。SM2私钥实际上就是一条椭圆曲线上的一个随机数。
# 使用gmssl生成一个SM2私钥,输出为PEM格式(默认格式) gmssl ecparam -genkey -name sm2p256v1 -out sm2_private_key.pem # 查看生成的私钥信息 gmssl ec -in sm2_private_key.pem -text -noout命令解析与注意事项:
ecparam -genkey: 用于生成椭圆曲线密钥对。-name sm2p256v1: 这是国密标准定义的SM2椭圆曲线参数名称。在GmSSL中,sm2p256v1是SM2算法的标准曲线。务必使用这个参数,而不是其他曲线。-out sm2_private_key.pem: 输出文件。PEM格式是Base64编码的文本格式,以-----BEGIN PRIVATE KEY-----开头,便于查看和传输。-text -noout: 以文本形式输出密钥的详细参数(如曲线方程、基点、私钥大整数),-noout表示不再次输出PEM编码内容。
安全警告:私钥保护
sm2_private_key.pem文件就是你的私钥,一旦泄露,攻击者就可以冒充你的身份进行签名或解密。务必:
- 生成后立即设置严格的文件权限:
chmod 600 sm2_private_key.pem。- 在生产环境中,考虑使用硬件密码设备(如USB Key)生成和存储私钥,私钥永不离开硬件。
- 可以为私钥添加加密口令,在生成时使用
-aes256等参数,但这会带来自动化脚本需要处理口令的问题,需权衡安全性与便利性。
3.2 第二步:生成证书签名请求(CSR)
CSR是向证书颁发机构(CA)申请证书的“申请书”,里面包含了你的公钥(从私钥导出)以及你的身份信息(DN, Distinguished Name)。
# 生成CSR,会交互式地询问你的身份信息 gmssl req -new -key sm2_private_key.pem -out server.csr -sm3 -sigopt "sm2_id:1234567812345678" # 如果你想非交互式生成,可以使用-subj参数(适用于自动化脚本) gmssl req -new -key sm2_private_key.pem -out server.csr -sm3 \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=Dev/CN=myserver.example.com" \ -sigopt "sm2_id:1234567812345678"命令解析与关键参数:
req -new: 创建新的证书请求。-key sm2_private_key.pem: 指定刚才生成的私钥文件。-out server.csr: 输出的CSR文件名。-sm3:这是国密关键参数!它指定在CSR签名过程中使用的哈希算法为国密SM3。国际标准通常用SHA256。如果你忘记加-sm3,默认可能会用SHA256,导致生成的CSR不符合国密规范,后续CA可能无法正确签发。-sigopt "sm2_id:1234567812345678":另一个国密关键参数!SM2签名算法在计算摘要时,需要将一个用户标识符(ID)与公钥等信息一起参与SM3哈希。这个ID通常是一个固定长度的字符串,如1234567812345678(16字节)。虽然在一些测试场景可以随意设置,但在正式应用中,它应与证书持有者的身份关联(如企业统一编码)。务必指定此参数,否则GmSSL可能会使用默认的空ID或报错。-subj: 设置证书主题(Subject)。各字段含义:C: Country,国家,中国用CN。ST: State/Province,省/州。L: Locality,城市。O: Organization,组织/公司名。OU: Organizational Unit,部门。CN: Common Name,通用名。对于服务器证书,这必须是客户端访问该服务器时使用的域名(FQDN)或IP地址,否则会导致证书域名不匹配错误。
你可以用gmssl req -in server.csr -text -noout查看CSR的详细内容,确认算法是sm2-with-sm3,以及Subject信息是否正确。
3.3 第三步:扮演CA——生成根证书并签发
在自签名或内部测试场景,你需要自己扮演CA。首先,CA自己也需要一对SM2密钥和一个自签名的根证书。
# 1. 生成CA的SM2私钥 gmssl ecparam -genkey -name sm2p256v1 -out ca_private_key.pem # 2. 生成CA的自签名根证书 gmssl req -new -x509 -days 3650 -key ca_private_key.pem -out ca_cert.pem -sm3 \ -subj "/C=CN/ST=Beijing/O=MyRootCA/CN=My Root CA" \ -sigopt "sm2_id:CA_ID_12345678"命令解析:
-x509: 直接生成一个自签名的X.509证书,而不是CSR。-days 3650: 证书有效期,这里是10年。根证书有效期通常设得较长。-key ca_private_key.pem: CA自己的私钥。-out ca_cert.pem: 输出的根证书文件。- 同样,必须指定
-sm3和-sigopt参数。
现在,用CA的私钥和根证书,来签发第一步中生成的服务器CSR。
# 3. 使用CA签发服务器证书 gmssl x509 -req -days 365 -in server.csr -CA ca_cert.pem -CAkey ca_private_key.pem -CAcreateserial -out server.crt -sm3 -sigopt "sm2_id:1234567812345678"命令解析:
x509 -req: 处理证书请求。-days 365: 签发的服务器证书有效期,1年。-in server.csr: 输入的CSR文件。-CA ca_cert.pem: CA的证书。-CAkey ca_private_key.pem: CA的私钥。-CAcreateserial: 创建并维护一个序列号文件(默认为ca_cert.srl),确保签发的每张证书有唯一序列号。-out server.crt: 输出的服务器证书文件。- 再次强调:
-sm3和-sigopt参数必须与生成CSR时保持一致。
至此,你已经得到了三个核心文件:
ca_cert.pem: 根证书(CA证书)server.crt: 服务器证书sm2_private_key.pem: 服务器私钥(与server.crt配对)
你可以用gmssl x509 -in server.crt -text -noout查看签发的证书详情,确认签发者(Issuer)是你CA的信息,使用者(Subject)是服务器的信息,签名算法是sm2-with-sm3,并且证书链是完整的。
3.4 第四步:证书格式转换与封装
生成的PEM格式证书和私钥是文本形式的,通用性好。但在某些特定场景,你可能需要其他格式。
1. 转换为DER格式(二进制)
# 证书PEM转DER gmssl x509 -in server.crt -outform DER -out server.der # 私钥PEM转DER (可能需要先去除口令) gmssl ec -in sm2_private_key.pem -outform DER -out private_key.der2. 封装为PKCS#12文件(.p12或.pfx)PKCS#12格式可以将证书、私钥以及CA证书链打包成一个加密文件,方便在Java Keystore、Windows IIS、浏览器导入等场景使用。
gmssl pkcs12 -export -out server.p12 -inkey sm2_private_key.pem -in server.crt -certfile ca_cert.pem执行命令后,会提示你设置一个密码来保护这个.p12文件。这个密码在导入时需要提供。
实操心得:PKCS#12的密码与兼容性一些较老的系统或工具对PKCS#12的加密算法支持有限。如果你在导入.p12文件时遇到“密码错误”或“无法解析”的问题,可以尝试在导出时指定更兼容的加密算法:
gmssl pkcs12 -export -out server.p12 -inkey sm2_private_key.pem -in server.crt -certfile ca_cert.pem -keypbe SM4-CBC -certpbe SM4-CBC -macalg SM3这里用国密SM4算法加密私钥和证书,用SM3做完整性校验。但请注意,接收方的工具也必须支持这些国密算法才能解密。在跨平台/跨工具传递时,使用默认算法(通常是PKCS#12标准算法)兼容性更好。
4. 核心环节:国密证书在Nginx中的配置与应用
证书做出来了,关键还得用起来。最典型的场景就是配置支持国密HTTPS的Web服务器。这里以国密Nginx为例,展示如何配置单向SSL和双向认证。
4.1 国密Nginx的编译与安装
首先,你需要一个支持国密算法的Nginx。通常我们需要编译“国密版Nginx”,它集成了GmSSL。
# 假设你已经安装了GmSSL 3.x在 /usr/local/gmssl # 1. 下载Nginx源码和国密补丁(以某个稳定版本为例,具体请查国密Nginx项目) wget http://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz git clone https://github.com/guanzhi/GmSSL.git # 如果还没克隆GmSSL # 2. 进入Nginx源码目录,应用国密补丁(补丁通常在GmSSL项目的`/tools/nginx/`目录下) cd nginx-1.24.0 patch -p1 < /path/to/GmSSL/tools/nginx/nginx-1.24.0-gmssl.patch # 3. 配置编译参数,关键是指定GmSSL路径 ./configure --prefix=/usr/local/nginx-gm \ --with-http_ssl_module \ --with-openssl=/path/to/GmSSL \ --with-openssl-opt="--prefix=/usr/local/gmssl" \ --with-stream \ --with-stream_ssl_module # 4. 编译安装 make -j$(nproc) sudo make install编译成功后,/usr/local/nginx-gm/sbin/nginx就是你的国密Nginx了。用/usr/local/nginx-gm/sbin/nginx -V查看版本信息,应该能看到built with OpenSSL的版本信息中包含GmSSL字样。
4.2 配置国密HTTPS服务器(单向认证)
这是最常见的场景,客户端验证服务器证书。
# 在 nginx.conf 的 http 块中,配置一个server server { listen 443 ssl; server_name myserver.example.com; # 国密SSL协议配置,使用SM2套件 ssl_protocols TLSv1.2 TLSv1.3; # 推荐启用TLS 1.3 ssl_ciphers ECC-SM2-SM4-CBC-SM3:ECC-SM2-SM4-GCM-SM3:ECDHE-SM2-SM4-CBC-SM3:ECDHE-SM2-SM4-GCM-SM3; ssl_prefer_server_ciphers on; # 指向我们自签的SM2证书和私钥 ssl_certificate /path/to/your/server.crt; ssl_certificate_key /path/to/your/sm2_private_key.pem; # 可选:提高性能和安全 ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; location / { root html; index index.html index.htm; } }配置解析与注意事项:
ssl_ciphers: 这是国密配置的灵魂。这里列出的密码套件名称是国密特有的。例如ECC-SM2-SM4-CBC-SM3表示:密钥交换和签名使用SM2椭圆曲线算法,加密使用SM4的CBC模式,消息认证码(MAC)使用SM3。GCM模式是认证加密模式,更现代。你必须显式配置这些国密套件,Nginx才会启用它们。ssl_certificate和ssl_certificate_key: 分别指向你的SM2服务器证书和私钥文件。- 重要:由于我们使用的是自签名CA颁发的证书,客户端(如浏览器)会因不信任我们的根CA而报安全错误。在测试环境,你需要将
ca_cert.pem导入到客户端的信任根证书库中。在生产环境,你需要向受信的国密商业CA申请证书。
4.3 配置国密双向认证(mTLS)
在金融、物联网等高安全场景,服务器也需要验证客户端的证书。
server { listen 443 ssl; server_name myserver.example.com; # ... 单向认证的ssl_protocols, ssl_ciphers配置同上 ... ssl_certificate /path/to/server.crt; ssl_certificate_key /path/to/sm2_private_key.pem; # !!! 启用客户端证书验证 !!! ssl_verify_client on; # 或 `optional`(可选验证) ssl_client_certificate /path/to/ca_cert.pem; # 指定签发客户端证书的CA证书,用于验证客户端证书 # ssl_verify_depth 2; # 验证深度,默认为1 # 如果客户端证书验证成功,相关信息会传递给后端应用 location /api/ { proxy_set_header X-Client-Certificate $ssl_client_cert; proxy_pass http://backend_server; } }双向认证要点:
ssl_verify_client on;: 强制要求客户端提供证书并进行验证。ssl_client_certificate /path/to/ca_cert.pem;: 指定服务器信任的CA证书。服务器会用这个CA证书去验证客户端提交的证书是否由其签发。这里通常放置你的私有根CA证书,或者一个包含多个可信CA的证书包。- 客户端需要持有由该CA签发的SM2客户端证书及其私钥。
- 在Nginx日志中,如果客户端未提供证书或证书验证失败,会返回
400 Bad Request错误,并且错误日志中会记录SSL_do_handshake()failed等信息。
4.4 客户端访问测试
配置好后,重启Nginx。使用支持国密的客户端进行测试。
使用GmSSL自带的
s_client测试(最直接):/usr/local/gmssl/bin/gmssl s_client -connect localhost:443 -servername myserver.example.com -CAfile /path/to/ca_cert.pem如果连接成功,会打印出完整的SSL握手信息、服务器证书链。注意看“SSL handshake has read...”和“Verify return code: 0 (ok)”表示验证成功。
使用国密浏览器:需要安装支持国密的浏览器(如基于Chromium的国密浏览器),并将你的根证书
ca_cert.pem导入到浏览器的“受信任的根证书颁发机构”中,然后访问https://myserver.example.com。使用curl(需编译支持GmSSL的版本):编译一个链接了GmSSL库的curl,使用
--cacert参数指定CA证书进行测试。
踩坑实录:证书链不完整导致的验证失败一个非常常见的问题是,服务器只发送了
server.crt,但没有发送中间CA证书(如果有的话)。在单向认证中,这可能导致某些严格的客户端(如Java应用、部分移动端)报错“unable to get local issuer certificate”。解决方法:将服务器证书和中间CA证书(如果有)合并成一个文件,然后让ssl_certificate指向这个合并后的文件。cat server.crt intermediate_ca.crt > server_chain.crt然后在Nginx配置中:
ssl_certificate /path/to/server_chain.crt;。这样Nginx会在握手时发送整个证书链。
5. 进阶:搭建简易国密CA系统与自动化
对于需要管理大量内部证书的场景,手动签发效率太低。我们可以用GmSSL配合脚本,搭建一个简易的自动化CA。
5.1 设计CA目录结构
一个规范的CA目录有助于管理。
/my_ca/ ├── root/ # 根CA目录 │ ├── private/ # 存放根CA私钥(严格保密!) │ │ └── ca.key.pem │ ├── certs/ # 存放根CA证书 │ │ └── ca.cert.pem │ ├── newcerts/ # 存放所有签发的证书(备份) │ ├── index.txt # 证书数据库,记录所有签发/吊销的证书 │ ├── serial # 当前序列号文件 │ └── crlnumber # 吊销列表序列号文件 ├── intermediate/ # 中间CA目录(可选,结构类似root) │ └── ... └── scripts/ # 存放自动化脚本 └── sign_cert.sh初始化这个结构,并创建index.txt(空文件)和serial文件(内容为01)。
5.2 编写自动化签发脚本
创建一个sign_cert.sh脚本,实现自动签发服务器或客户端证书。
#!/bin/bash # sign_cert.sh - 使用国密CA自动签发证书 set -e CA_DIR="/my_ca/root" CA_KEY="$CA_DIR/private/ca.key.pem" CA_CERT="$CA_DIR/certs/ca.cert.pem" DAYS=365 SM2_ID="1234567812345678" if [ $# -lt 2 ]; then echo "Usage: $0 <csr_file> <output_cert_file> [days]" exit 1 fi CSR_FILE=$1 CERT_FILE=$2 if [ $# -eq 3 ]; then DAYS=$3 fi # 检查CSR文件 if [ ! -f "$CSR_FILE" ]; then echo "CSR file not found: $CSR_FILE" exit 1 fi # 签发证书 gmssl x509 -req -days $DAYS \ -in "$CSR_FILE" \ -CA "$CA_CERT" \ -CAkey "$CA_KEY" \ -CAcreateserial \ -out "$CERT_FILE" \ -sm3 \ -sigopt "sm2_id:$SM2_ID" # 更新证书数据库 (简化版,实际应记录更多信息) echo -e "V\t$(date +%Y%m%d%H%M%SZ)\t\t$(gmssl x509 -in $CERT_FILE -serial -noout | cut -d= -f2)\tunknown\t$CERT_FILE" >> "$CA_DIR/index.txt" echo "Certificate signed successfully: $CERT_FILE" echo "Validity: $DAYS days"脚本使用方式:
# 1. 为某个服务生成私钥和CSR(假设已生成 server.csr) # 2. 运行签发脚本 ./scripts/sign_cert.sh /path/to/server.csr /path/to/server_new.crt 730 # 3. 脚本会自动使用根CA进行签发,并更新index.txt这个脚本极大地简化了批量签发流程。你可以在此基础上扩展,比如添加配置文件、支持不同的证书类型(服务器/客户端)、自动生成CRL(证书吊销列表)等。
5.3 证书生命周期管理:吊销与CRL
证书可能会因为私钥泄露、员工离职等原因需要提前吊销。
# 1. 吊销证书。假设证书序列号是 1001 gmssl ca -config <(echo "[ca]\ndefault_ca=my_ca\n[my_ca]\ndatabase = $CA_DIR/index.txt\ncertificate = $CA_CERT\nprivate_key = $CA_KEY\nnew_certs_dir = $CA_DIR/newcerts\nserial = $CA_DIR/serial\ndefault_md = sm3\ndefault_days = 365\npolicy = policy_anything\n[policy_anything]\ncountryName = optional\nstateOrProvinceName = optional\norganizationName = optional\norganizationalUnitName = optional\ncommonName = supplied\nemailAddress = optional") -revoke $CA_DIR/newcerts/1001.pem -sm3 -sigopt "sm2_id:$SM2_ID" # 2. 生成证书吊销列表(CRL) gmssl ca -config ...(同上,配置略)... -gencrl -out $CA_DIR/crl/ca.crl.pem -sm3 -sigopt "sm2_id:$SM2_ID"在Nginx中配置CRL检查(可选,性能开销大):
ssl_crl /path/to/ca.crl.pem;重要提醒:自建CA的局限性本文搭建的是私有CA,其根证书不在任何公共信任列表中。这意味着:
- 所有客户端都必须手动导入你的根证书,否则会报不信任错误。这在可控的内部网络(如企业内网、物联网专网)是可行的。
- 不适合公开的互联网服务。面向公众的网站必须使用由全球或国内受信根CA(如CFCA、上海CA等颁发的国密SSL证书)签发的证书。
- 私有CA的安全完全依赖于根CA私钥的保护。一旦根CA私钥泄露,整个体系就崩溃了。务必将其离线保存在绝对安全的地方。
6. 常见问题排查与解决技巧
在实际操作中,你几乎一定会遇到各种报错。这里汇总了最常见的问题和排查思路。
6.1 握手失败:密码套件不匹配
现象:客户端(如gmssl s_client)连接时,在握手阶段失败,错误信息可能包含no shared cipher或handshake failure。
排查步骤:
- 检查Nginx配置的
ssl_ciphers:确保你配置的国密套件字符串完全正确,没有拼写错误。可以先用一个较宽松的配置测试,如ssl_ciphers ALL:!aNULL:!eNULL;,看是否能建立非国密的普通TLS连接,以排除网络和端口问题。 - 检查客户端支持的套件:使用
gmssl ciphers -v查看GmSSL支持的所有套件,确认其中包含你配置的国密套件(如ECC-SM2-SM4-CBC-SM3)。 - 确认GmSSL版本:确保服务器Nginx编译链接的GmSSL和客户端使用的GmSSL版本都支持SM2/SM3/SM4算法。用
gmssl version和Nginx的-V输出确认。 - 抓包分析:在服务器端用
tcpdump或wireshark抓取TLS握手包,查看Client Hello和Server Hello中的Cipher Suites字段,看双方是否列出了共同的国密套件。
6.2 证书验证失败
现象:客户端报错self signed certificate in certificate chain或unable to get local issuer certificate。
排查步骤:
- 确认证书链完整:如前所述,确保服务器发送了完整的证书链。使用
gmssl s_client -showcerts可以查看服务器实际发送的证书链。 - 检查CA证书是否被客户端信任:对于自签名CA,客户端必须显式指定
-CAfile参数(在s_client中)或将其导入到信任存储中(在浏览器或操作系统中)。 - 检查证书主题(Subject)和备用名称(SAN):确保证书的
CN或SAN字段包含了客户端实际访问的域名或IP地址。可以使用gmssl x509 -in server.crt -text -noout查看证书详情。 - 检查证书有效期:确保证书没有过期或尚未生效。
6.3 国密Nginx编译或启动报错
现象:编译国密Nginx时失败,或启动Nginx时报SSL相关错误。
排查步骤:
- 依赖检查:确保系统已安装GmSSL,并且编译Nginx时
--with-openssl指向的路径正确,该路径下必须有GmSSL的源码。 - 补丁兼容性:确保Nginx版本与国密补丁版本匹配。不匹配的补丁可能导致编译错误或运行时崩溃。最好从国密Nginx的官方项目页面获取推荐的版本组合。
- 查看错误日志:Nginx的错误日志(默认
logs/error.log)是首要排查点。SSL握手错误通常会在这里有详细记录。 - 测试GmSSL本身:单独运行
gmssl命令,测试gmssl s_server和gmssl s_client是否能进行国密通信,以隔离Nginx配置问题。
6.4 性能问题与优化
现象:启用国密HTTPS后,服务器CPU负载明显升高。
分析与优化:
- 理解性能特点:SM2签名/验签速度与RSA 2048位相当,但密钥交换和加密解密速度通常优于RSA。SM3哈希速度与SHA-256相当。SM4加解密速度在软件实现上可能略慢于AES-NI硬件加速的AES,但在支持SM4指令集的国产CPU(如鲲鹏、飞腾)上会有硬件加速。
- 启用会话复用:在Nginx配置中,
ssl_session_cache和ssl_session_timeout对于减少完整的TLS握手(特别是SM2密钥交换)至关重要,能显著提升性能。 - 考虑硬件加速:如果性能是瓶颈,调研是否可以使用支持国密算法的硬件密码设备(如密码卡、USB Key),通过GmSSL的ENGINE机制接入,将密码运算卸载到硬件。
- 监控与 profiling:使用
top、vmstat或openssl speed命令(GmSSL也有speed子命令)来基准测试国密算法的性能,并与国际算法对比,量化性能影响。
制作和部署国密SM2证书,从生成密钥到配置服务,每一步都需要对国密标准和工具有清晰的理解。这个过程不仅仅是命令的堆砌,更是在理解一个基于国产密码技术的信任体系如何建立和运作。当你看到浏览器地址栏因为你的自签名国密证书而显示“不安全”时,别灰心,那只是因为它还不认识你这个“私人印章”。在可控的内部环境里,这套自己打造的国密PKI体系,就是保障通信安全自主可控的坚实基石。