1. 项目概述:为什么金融级HTTPS必须拥抱国密?
如果你负责过金融、政务或者大型国企的Web服务项目,大概率遇到过“国密合规”这个硬性要求。这不仅仅是技术选型,更是一项政策性任务。传统的HTTPS依赖RSA、ECC等国际通用算法,而国密SSL则是基于我国自主密码算法标准(SM2、SM3、SM4)构建的安全通信体系。简单来说,它用SM2非对称算法替代RSA/ECC,用SM3哈希算法替代SHA,用SM4对称算法替代AES,实现了一套从底层算法到上层协议完全自主可控的安全链路。
这次实战,我们聚焦于最核心的Web服务入口——Nginx服务器,使用阿里云签发的SM2双证书,搭建一个支持国密算法的HTTPS站点。你可能会问,为什么是“双证书”?这正是国密改造初期为了兼容性而采取的经典方案:一套SM2国密证书用于与支持国密的浏览器(如360安全浏览器国密版、密信浏览器)通信;另一套传统的RSA/ECC国际证书,用于保障其他主流浏览器的正常访问。Nginx国密版则是在原生Nginx基础上,集成了对国密算法和双证书监听能力的特殊编译版本。
整个过程,从证书申请、Nginx国密版编译安装,到复杂的双证书链合并与配置,每一步都有坑。尤其是证书链合并,阿里云下载的证书包往往需要手动拼接中间CA,这一步没做对,浏览器就会亮起刺眼的“证书链不完整”警告。接下来,我会以一个完整的实操流程,带你走通这条金融级HTTPS的部署之路。
2. 核心需求解析与方案设计
2.1 国密HTTPS的核心组件拆解
要搭建一个完整的国密HTTPS服务,我们需要四个核心部件协同工作:
- 国密SSL证书:由权威的国密CA机构(如沃通WoSign、上海CA)签发,证明服务器身份。阿里云作为证书服务商,提供了便捷的申请入口。国密证书通常是“双证书”,即一个用于数字签名的签名证书(Signature Certificate)和一个用于密钥交换的加密证书(Encryption Certificate),分别对应
.pem和.key文件。 - 支持国密的Web服务器:原生Nginx使用的OpenSSL库不支持国密算法,因此必须使用集成了国密算法库(如
wotrus_ssl,即沃通国密SSL模块)的Nginx,或直接使用Tengine(阿里基于Nginx的增强版,内置国密支持)。我们选择从源码编译Nginx并集成沃通模块,以获得更灵活的控制。 - 支持国密的客户端浏览器:服务端部署好后,普通Chrome、Firefox无法使用国密套件连接。需要使用内置了国密算法根的浏览器,如360安全浏览器(需开启国密支持)、密信浏览器、红莲花浏览器等,才能建立完整的国密HTTPS连接。
- 双证书监听与智能选择:服务器需要同时监听在同一个端口(如443)上,根据客户端握手时声明的密码套件(Cipher Suite)来智能选择使用国密证书还是国际证书进行响应。这需要Nginx有相应的配置支持。
2.2 阿里云SM2证书申请与下载要点
在阿里云数字证书管理服务控制台申请国密证书时,有几个关键选择直接影响后续部署:
- 证书类型:选择“国密标准(SM2)”。注意,它会自动包含双证书(签名和加密)。
- 密钥算法:选择“SM2”。
- 生成CSR方式:对于新手或快速部署,强烈建议选择“系统生成”。阿里云会自动为你生成密钥对,并将私钥包含在最终下载的证书包中。如果选择“手动生成”,你需要用
openssl gmssl等工具本地生成CSR和私钥,私钥需自行保管,证书包中不包含私钥,遗失后证书将无法使用。 - 域名验证:根据你的域名管理方式选择DNS验证或文件验证。通常DNS验证(添加一条TXT解析记录)更为方便通用。
证书签发后,在控制台下载时,服务器类型选择“Nginx”。下载的压缩包解压后,你会看到类似以下结构的文件:
yourdomain.com/ ├── yourdomain.com_sm2_sign.pem # SM2签名证书 ├── yourdomain.com_sm2_sign.key # SM2签名证书私钥 ├── yourdomain.com_sm2_enc.pem # SM2加密证书 ├── yourdomain.com_sm2_enc.key # SM2加密证书私钥 └── yourdomain.com.pem # 国际证书(RSA/ECC,可选) └── yourdomain.com.key # 国际证书私钥(可选)这里有一个至关重要的细节:下载的.pem证书文件,可能只包含你的站点证书,而不包含中间CA证书链。浏览器校验证书时,需要构建从你的站点证书到根证书的完整信任链。如果中间CA证书缺失,就会导致“证书链不完整”的错误。这就是我们后面要重点解决的“证书链合并”问题。
2.3 Nginx国密版编译方案选型
你有两个主流选择:直接使用阿里云或社区提供的已编译好的Tengine/国密Nginx二进制包,或者从源码编译。我推荐从源码编译,理由有三:1) 版本可控,能与系统环境更匹配;2) 编译参数可定制,方便后续扩展模块;3) 能更深入地理解其组成。我们选择稳定的Nginx 1.18.0与沃通国密模块wotrus_ssl进行编译。
编译的核心思路是:先准备好国密算法库(wotrus_ssl),然后在编译Nginx时,通过--with-openssl参数指向这个国密库的源码目录,让Nginx链接国密版本的SSL库,而不是系统自带的OpenSSL。
3. 环境准备与国密Nginx编译安装
3.1 服务器基础环境配置
假设我们使用一台全新的CentOS 8或AlmaLinux 8服务器。首先,进行基础准备:
# 1. 更新系统并安装编译依赖 sudo yum update -y sudo yum groupinstall -y "Development Tools" sudo yum install -y gcc gcc-c++ pcre pcre-devel zlib zlib-devel wget tar # 2. 检查并开放443端口(防火墙与安全组) # 查看防火墙状态(如果使用firewalld) sudo systemctl status firewalld # 如果防火墙开启,放行443端口 sudo firewall-cmd --permanent --add-port=443/tcp sudo firewall-cmd --reload # 对于云服务器(如阿里云ECS),务必在控制台的安全组规则中,添加入方向允许TCP 443端口。3.2 下载源码与国密模块
我们计划将软件安装在/usr/local/src目录下。
cd /usr/local/src # 1. 下载Nginx 1.18.0源码包 sudo wget https://nginx.org/download/nginx-1.18.0.tar.gz sudo tar -zxvf nginx-1.18.0.tar.gz # 2. 下载沃通国密SSL模块 (wotrus_ssl) # 注意:沃通官网下载链接可能变更,如果失效,需从其官网查找最新链接。 sudo wget https://www.wotrus.com/download/wotrus_ssl.tar.gz sudo tar -zxvf wotrus_ssl.tar.gz # 解压后通常得到一个名为 `wotrus_ssl2.0` 或类似的目录3.3 编译安装Nginx国密版
进入Nginx源码目录,进行配置。关键点在于--with-openssl参数必须指向我们刚解压的wotrus_ssl目录的上一层,即包含include和lib目录的路径。
cd nginx-1.18.0 # 配置编译参数 # 假设wotrus_ssl解压到了 /usr/local/src/wotrus_ssl2.0 # --prefix 指定安装目录 # --with-openssl 指向国密SSL库源码目录 sudo ./configure --prefix=/usr/local/nginx \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-stream \ --with-stream_ssl_module \ --with-openssl=/usr/local/src/wotrus_ssl2.0 # 编译并安装 sudo make sudo make install踩坑记录:编译错误
Error 127如果编译过程中出现类似make[1]: *** [/usr/local/src/wotrus_ssl2.0/.openssl/include/openssl/ssl.h] Error 127的错误,这是因为Nginx的编译脚本在国密模块的目录结构预期上出了问题。需要手动修复一个配置文件。 解决方法:# 编辑Nginx的OpenSSL配置脚本 sudo vi /usr/local/src/nginx-1.18.0/auto/lib/openssl/conf找到以下四行内容:
CORE_INCS="$CORE_INCS $OPENSSL/.openssl/include" CORE_DEPS="$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h" CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a" CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a"将其修改为:
CORE_INCS="$CORE_INCS $OPENSSL/include" CORE_DEPS="$CORE_DEPS $OPENSSL/include/openssl/ssl.h" CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libssl.a" CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libcrypto.a"保存退出后,重新执行
sudo make clean,然后再次sudo make && sudo make install。
安装完成后,验证Nginx版本及其SSL支持:
cd /usr/local/nginx/sbin sudo ./nginx -V输出中应该能看到--with-openssl=/usr/local/src/wotrus_ssl2.0以及TLS SM4-GCM-SM3等国密相关的密码套件,这说明国密模块已成功集成。
3.4 启动Nginx与初步测试
# 启动Nginx sudo ./nginx # 检查进程 ps -ef | grep nginx # 测试默认HTTP页面 curl http://localhost如果看到Nginx的欢迎页面HTML代码,说明服务已正常启动。
4. 证书处理与配置:合并证书链是关键
4.1 上传证书文件
在Nginx配置目录下创建一个专门存放证书的文件夹,并将从阿里云下载的所有证书文件(.pem和.key)上传至此。
sudo mkdir -p /usr/local/nginx/conf/cert # 使用scp、sftp或其他方式将本地证书文件上传到服务器的 /usr/local/nginx/conf/cert/ 目录下 # 例如:scp yourdomain.com_sm2_sign.pem user@your_server_ip:/usr/local/nginx/conf/cert/4.2 合并国密证书链(核心技巧)
这是整个部署中最容易出错的一步。阿里云下载的yourdomain.com_sm2_sign.pem通常只包含你的站点证书。浏览器需要完整的证书链(站点证书 + 中间CA证书 + 根CA证书)才能验证。
如何获取完整的证书链?
- 从CA机构网站获取:访问为你签发证书的CA机构(如沃通)的网站,在其“技术支持”或“下载中心”页面,查找“国密SM2中间证书”或“证书链”进行下载。
- 通过浏览器导出(推荐):使用360安全浏览器国密版或密信浏览器访问一个已经正确部署了同CA国密证书的网站,点击地址栏锁图标 -> “证书” -> “证书路径”,你会看到从根证书到你的站点证书的完整链条。然后可以逐级导出中间CA和根CA的证书(通常为
.cer或.crt格式)。
假设我们获取到了两个中间CA文件:WoSign SM2 OV SSL CA.crt和WoSign SM2 Root CA.crt。
合并命令:我们需要将站点证书、中间CA证书、根CA证书按顺序合并到一个文件中。顺序是:你的站点证书在最前面,然后是中间CA证书,最后是根CA证书。
cd /usr/local/nginx/conf/cert # 1. 合并SM2签名证书链 sudo cat yourdomain.com_sm2_sign.pem WoSign\ SM2\ OV\ SSL\ CA.crt WoSign\ SM2\ Root\ CA.crt > yourdomain.com_sm2_sign_chain.pem # 2. 合并SM2加密证书链(通常加密证书与签名证书使用相同的CA链,但务必确认) # 如果加密证书的CA链不同,需用对应的文件。这里假设相同。 sudo cat yourdomain.com_sm2_enc.pem WoSign\ SM2\ OV\ SSL\ CA.crt WoSign\ SM2\ Root\ CA.crt > yourdomain.com_sm2_enc_chain.pem # 3. (可选)合并国际证书链(如果部署双证书) # 国际证书的中间CA通常不同,例如可能是“R3”或“ISRG Root X1”。获取对应中间证书后合并。 sudo cat yourdomain.com.pem R3.crt ISRG\ Root\ X1.crt > yourdomain.com_chain.pem重要提示:合并后,务必使用文本编辑器(如
vi)或cat命令检查生成的*_chain.pem文件。一个正确的PEM格式证书链文件,应该包含多个以-----BEGIN CERTIFICATE-----开头,以-----END CERTIFICATE-----结尾的区块,且顺序正确。
4.3 配置Nginx支持国密双证书
编辑Nginx的主配置文件/usr/local/nginx/conf/nginx.conf。我们需要在http块内,配置一个监听443端口的server块。
http { ... # 其他原有配置 server { listen 443 ssl; # 同时监听国密和国际SSL server_name yourdomain.com www.yourdomain.com; # 替换为你的域名 # ====== 国密SM2证书配置 ====== # 签名证书链和私钥 ssl_certificate /usr/local/nginx/conf/cert/yourdomain.com_sm2_sign_chain.pem; ssl_certificate_key /usr/local/nginx/conf/cert/yourdomain.com_sm2_sign.key; # 加密证书链和私钥 ssl_certificate /usr/local/nginx/conf/cert/yourdomain.com_sm2_enc_chain.pem; ssl_certificate_key /usr/local/nginx/conf/cert/yourdomain.com_sm2_enc.key; # ====== 国际证书配置(双证书方案必需) ====== ssl_certificate /usr/local/nginx/conf/cert/yourdomain.com_chain.pem; ssl_certificate_key /usr/local/nginx/conf/cert/yourdomain.com.key; # SSL协议和密码套件配置 ssl_protocols TLSv1.2 TLSv1.3; # 建议禁用TLSv1.0/1.1 # 密码套件顺序:优先国密套件,其次国际强套件 ssl_ciphers ECC-SM4-SM3:ECDHE-SM4-SM3:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK; ssl_prefer_server_ciphers on; # 服务器优先选择密码套件 ssl_session_timeout 5m; ssl_session_cache shared:SSL:10m; # 网站根目录等其他配置 root /usr/local/nginx/html; index index.html index.htm; location / { try_files $uri $uri/ =404; } # 可选:启用HSTS,强制浏览器使用HTTPS add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; } # 可选:将HTTP请求重定向到HTTPS server { listen 80; server_name yourdomain.com www.yourdomain.com; return 301 https://$server_name$request_uri; } }配置要点解析:
ssl_certificate指令可以多次出现,Nginx国密版能够识别并处理多套证书。当客户端握手时,Nginx会根据客户端支持的密码套件列表,自动选择对应的证书进行响应。ssl_ciphers列表中,ECC-SM4-SM3和ECDHE-SM4-SM3是国密算法套件,必须放在最前面,以确保支持国密的浏览器优先使用国密算法连接。- 国际证书的密码套件(如
ECDHE-RSA-AES128-GCM-SHA256)紧随其后,保证兼容性。
4.4 验证配置并重载Nginx
# 1. 测试配置文件语法 sudo /usr/local/nginx/sbin/nginx -t # 预期输出:nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful # 2. 如果测试成功,重载Nginx使配置生效 sudo /usr/local/nginx/sbin/nginx -s reload # 3. 检查443端口是否在监听 sudo netstat -tlnp | grep :4435. 测试验证与问题排查
5.1 使用浏览器测试
- 国际浏览器测试:使用Chrome、Firefox、Safari访问
https://yourdomain.com。应该能正常打开网站,并显示安全锁图标。点击锁图标查看证书详情,应显示你部署的国际证书(RSA/ECC)。 - 国密浏览器测试:使用360安全浏览器(需在设置中开启“国密算法支持”)或密信浏览器访问同一地址。同样应显示安全锁。查看证书详情,此时应显示SM2国密证书。这是验证国密部署是否成功的金标准。
5.2 使用OpenSSL/GMSSL命令行测试
服务器上可以使用链接了国密库的openssl(实际上是gmssl)命令进行测试。
# 测试国际SSL连接 openssl s_client -connect yourdomain.com:443 -servername yourdomain.com # 测试国密SSL连接(需要指定国密套件) # 首先找到gmssl命令,通常在国密模块安装目录下,或者系统PATH中 /usr/local/src/wotrus_ssl2.0/bin/gmssl s_client -connect yourdomain.com:443 -cipher ECC-SM4-SM3在s_client命令的输出中,关注“Certificate chain”部分,查看服务器发送的证书链是否完整,以及“Cipher”部分是否使用了预期的密码套件。
5.3 常见问题与排查技巧实录
问题1:浏览器提示“您的连接不是私密连接”(NET::ERR_CERT_AUTHORITY_INVALID)
- 可能原因A:证书链不完整。这是最常见的原因。使用浏览器检查证书路径,如果只有你的站点证书,缺少中间CA,说明合并步骤有误。
- 排查:在服务器上执行
sudo openssl x509 -in /usr/local/nginx/conf/cert/yourdomain.com_sm2_sign_chain.pem -text -noout | grep -A 1 "Issuer",查看颁发者信息。然后对比浏览器中缺失的中间CA信息。 - 解决:重新获取正确的中间CA证书,并按正确顺序合并。
- 排查:在服务器上执行
- 可能原因B:域名不匹配。证书绑定的域名与当前访问的域名不一致。
- 排查:检查证书的Subject Alternative Name (SAN) 字段:
sudo openssl x509 -in yourdomain.com_sm2_sign.pem -text -noout | grep -A 1 "Subject Alternative Name"。 - 解决:确保证书覆盖了所有需要访问的域名(如带www和不带www)。
- 排查:检查证书的Subject Alternative Name (SAN) 字段:
问题2:国密浏览器无法建立国密连接,自动降级到国际证书
- 可能原因A:Nginx配置中国密密码套件顺序不对或未启用。
- 排查:检查
nginx.conf中ssl_ciphers是否包含ECC-SM4-SM3等国密套件,且是否放在靠前位置。 - 解决:调整
ssl_ciphers顺序,确保国密套件在前。
- 排查:检查
- 可能原因B:Nginx未正确编译国密模块。
- 排查:执行
sudo /usr/local/nginx/sbin/nginx -V,查看输出中是否有国密相关模块和--with-openssl指向正确路径。 - 解决:重新编译安装Nginx国密版。
- 排查:执行
问题3:Nginx启动或重载报错,提示证书或密钥文件找不到或格式错误
- 可能原因A:文件路径或权限错误。
- 排查:使用绝对路径,并确保Nginx进程用户(通常是
nobody或www-data)有读取证书和密钥文件的权限。 - 解决:
sudo chmod 644 /usr/local/nginx/conf/cert/*.pem /usr/local/nginx/conf/cert/*.key。
- 排查:使用绝对路径,并确保Nginx进程用户(通常是
- 可能原因B:证书或密钥文件格式错误(如Windows换行符、多余空格)。
- 排查:使用
cat -A查看文件,不应有^M(Windows回车符)。 - 解决:使用
dos2unix命令转换,或使用vi重新保存。
- 排查:使用
问题4:性能考虑与优化
国密SM2算法的计算开销比RSA大,在超高并发场景下可能成为瓶颈。建议:
- 启用
ssl_session_cache和ssl_session_tickets来复用SSL会话,减少握手开销。 - 考虑使用硬件密码卡(HSM)来加速SM2运算,这对于金融等高性能场景是必备选项。
- 做好监控,关注SSL握手时间、CPU使用率等指标。
部署完成后,一个稳固的监控策略是必不可少的。除了监控服务本身的可访问性,更关键的是监控证书有效期。国密证书和国际证书都有过期时间,一旦过期,服务将立即中断。建议在阿里云控制台开启证书到期提醒,同时也可以在自己的监控系统(如Prometheus)中,通过定期抓取证书信息并解析notAfter字段来设置告警。