
1. 项目概述与背景最近在给一个对数据安全有特殊要求的项目做技术选型客户明确要求通信链路必须支持国密算法。这让我不得不把尘封已久的CentOS 7虚拟机又翻了出来从头搭建一套支持SM2/SM3/SM4的国密HTTPS服务环境。整个过程从编译支持国密的GmSSL到生成SM2证书再到让Nginx正确识别并启用国密套件踩的坑一个接一个。网上的资料要么过于零散要么版本老旧照着做十有八九会卡在某个报错上。所以我决定把这次从零到一的完整过程连同那些让人头疼的报错和解决方案整理成这篇保姆级教程。无论你是运维工程师、安全开发还是单纯对国密技术感兴趣跟着这篇指南走都能在CentOS 7上稳稳地跑起国密HTTPS。国密算法即国家密码管理局认定的商用密码算法标准包括SM2非对称加密、SM3哈希算法、SM4对称加密。在金融、政务、关键基础设施等领域使用国密算法进行通信加密和身份认证已成为合规性要求。而Nginx作为最流行的Web服务器和反向代理让其支持国密是构建国密应用生态的关键一步。本教程的核心就是通过GmSSL支持国密算法的OpenSSL分支来生成SM2证书并配置Nginx使用该证书提供HTTPS服务。2. 环境准备与依赖检查在开始编译和安装之前一个干净、稳定的基础环境至关重要。我强烈建议使用一台全新的CentOS 7 Minimal系统进行操作避免因系统残留的旧版OpenSSL或开发库导致冲突。2.1 系统与资源确认首先登录你的CentOS 7服务器检查系统版本和基础资源。cat /etc/redhat-release free -h df -h /输出类似CentOS Linux release 7.9.2009 (Core)即可。确保内存不少于1GB磁盘剩余空间大于5GB编译过程会产生大量中间文件。2.2 安装必备的开发工具和库CentOS 7 Minimal默认安装的软件包很少我们需要手动安装编译GmSSL和Nginx所需的所有依赖。这一步是后续所有操作的基础务必执行完整。# 更新yum缓存并安装EPEL扩展源提供更多软件包 yum update -y yum install -y epel-release # 安装编译工具链和基础依赖 yum groupinstall -y Development Tools yum install -y wget git make gcc-c pcre-devel zlib-devel perl # 安装GmSSL和Nginx可能需要的其他库 yum install -y openssl-devel # 注意这里安装的是系统自带的OpenSSL开发库仅用于提供一些头文件和链接库不会影响我们后续编译的GmSSL。注意这里安装openssl-devel可能会让人困惑担心与待会儿安装的GmSSL冲突。实际上这个包主要提供了openssl/头文件目录和libcrypto.so等链接库许多软件包括Nginx的构建系统在编译时会去固定路径查找它们。我们编译GmSSL时会将其安装到独立目录如/usr/local/gmssl并通过环境变量让系统优先使用GmSSL。两者可以共存但运行时需要明确指定使用哪一个。2.3 关闭SELinux和防火墙临时用于测试在学习和测试环境SELinux和防火墙的严格策略可能会给调试带来不必要的麻烦。我们可以先临时关闭它们待所有配置调试无误后再根据安全策略重新细化开启。# 临时关闭SELinux重启后失效 setenforce 0 # 永久关闭需修改配置文件 /etc/selinux/config将 SELINUXenforcing 改为 SELINUXdisabled然后重启。 # 停止并禁用firewalld防火墙测试环境 systemctl stop firewalld systemctl disable firewalld实操心得在生产环境中绝对不建议直接关闭防火墙和SELinux。正确的做法是使用firewall-cmd命令开放必要的端口如80、443并针对Nginx进程和证书文件设置合适的SELinux上下文。但在初学调试阶段先关闭它们可以快速排除因权限和策略导致的问题让我们更专注于软件本身的配置。3. 编译与安装GmSSLGmSSL是北京大学维护的、支持国密算法和国密通信协议如TLCP的OpenSSL分支。我们将从源码编译以确保获得完整功能并控制安装路径。3.1 下载GmSSL源码访问GmSSL在GitHub的官方仓库获取最新稳定版源码。以下以3.2.0版本为例。# 进入一个常用的源码目录 cd /usr/local/src # 使用git克隆仓库推荐便于后续更新 git clone https://github.com/guanzhi/GmSSL.git cd GmSSL # 或者如果你无法访问GitHub可以使用wget下载发布包需自行查找最新版本链接 # wget https://github.com/guanzhi/GmSSL/archive/refs/tags/v3.2.0.tar.gz # tar -zxf v3.2.0.tar.gz # cd GmSSL-3.2.03.2 配置与编译安装GmSSL的编译采用标准的configure make make install流程。这里有几个关键配置选项需要关注。# 运行配置脚本指定安装前缀为 /usr/local/gmssl # --prefix 参数决定了GmSSL的安装根目录。 # 启用共享库编译--shared方便其他动态链接。 # 关闭模块组装--disable-module-assembly避免在某些虚拟化环境下出现汇编错误。 ./config --prefix/usr/local/gmssl shared --disable-module-assembly # 编译源码-j$(nproc) 表示使用所有CPU核心并行编译以加快速度。 make -j$(nproc) # 运行测试套件可选但推荐确保编译出的库功能正常。 make test # 安装到系统这会将可执行文件、库文件和头文件拷贝到 /usr/local/gmssl 下。 sudo make install编译过程通常需要5-15分钟取决于服务器性能。如果make test阶段有个别非关键测试失败可以尝试忽略但如果有大量核心算法测试失败则需要检查系统环境或GmSSL版本。3.3 配置系统环境变量安装完成后我们需要让系统知道GmSSL的存在并优先使用它而不是系统自带的OpenSSL。# 创建GmSSL库文件的链接以便系统动态链接器能够找到。 echo /usr/local/gmssl/lib64 | sudo tee /etc/ld.so.conf.d/gmssl.conf sudo ldconfig # 将GmSSL的bin目录添加到PATH环境变量最前面这样命令行输入openssl时会优先调用GmSSL。 echo export PATH/usr/local/gmssl/bin:$PATH ~/.bashrc source ~/.bashrc现在验证安装是否成功which openssl # 应该输出 /usr/local/gmssl/bin/openssl openssl version # 应该输出 GmSSL x.x.x后面跟着编译日期和选项其中会包含 gmssl 字样。如果openssl version仍然显示系统自带的OpenSSL版本请检查~/.bashrc文件是否生效或者尝试重新登录终端。4. 生成SM2国密证书有了GmSSL我们就可以用它来生成国密算法证书了。国密证书通常使用SM2算法作为非对称加密SM3算法进行摘要签名。我们将生成一个自签名的根CA证书和一个服务器证书。4.1 生成SM2私钥与自签名根CA证书首先创建一个专用目录来存放我们的证书文件。mkdir -p /etc/nginx/certs/gmssl cd /etc/nginx/certs/gmssl生成SM2算法私钥。国密标准中SM2私钥通常使用-engine gmssl参数指定引擎但GmSSL已将国密算法集成为主引擎所以可以直接使用ecparam生成。# 生成SM2私钥曲线参数为sm2p256v1国密标准曲线 openssl ecparam -genkey -name sm2p256v1 -out ca.key # 为私钥文件设置严格的权限防止泄露 chmod 600 ca.key接下来使用刚生成的私钥创建一个自签名的根CA证书。这个过程需要你交互式地输入一些信息。openssl req -new -x509 -days 3650 -key ca.key -out ca.crt你会看到一系列提示Country Name (2 letter code): 国家代码如CN。State or Province Name: 省份。Locality Name: 城市。Organization Name: 组织名称如My Company。Organizational Unit Name: 部门名称。Common Name:至关重要这是CA的通用名可以设为My GmSSL Root CA。Email Address: 邮箱地址。这些信息会嵌入到证书中。对于测试用途可以随意填写但在生产环境必须使用真实、合规的信息。4.2 生成服务器证书签名请求CSR和证书服务器证书需要由CA签发。我们先为服务器生成一个私钥和CSR。# 生成服务器SM2私钥 openssl ecparam -genkey -name sm2p256v1 -out server.key chmod 600 server.key # 生成证书签名请求(CSR) openssl req -new -key server.key -out server.csr在填写CSR信息时特别注意Common Name (CN)。这里必须填写你服务器将要使用的域名或IP地址。例如如果你打算用https://test.gm.com访问这里就填test.gm.com。如果使用IP访问则填写IP地址。不匹配会导致浏览器报证书错误。现在使用我们自建的CA来签发服务器证书。# 创建一个证书扩展配置文件定义服务器证书的基本约束和用途 cat server.ext EOF basicConstraints CA:FALSE keyUsage digitalSignature, nonRepudiation, keyEncipherment, keyAgreement extendedKeyUsage serverAuth subjectAltName alt_names [alt_names] DNS.1 test.gm.com # 这里替换为你的域名或添加多个DNS.x、IP.x条目 EOF # 使用CA证书和私钥签发服务器证书有效期为365天 openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile server.ext执行成功后你会得到server.crt服务器证书和ca.srl序列号文件。现在/etc/nginx/certs/gmssl目录下应该有以下文件ca.key: CA私钥务必保密ca.crt: CA根证书需要导入到客户端浏览器server.key: 服务器私钥server.csr: 证书请求文件可存档备用server.crt: 服务器证书server.ext: 扩展配置文件ca.srl: 序列号文件5. 编译支持国密的Nginx默认的Nginx发行版不支持国密算法。我们需要下载Nginx源码并加入对GmSSL国密OpenSSL的支持进行编译。5.1 下载Nginx源码并解压访问Nginx官网下载稳定版源码。这里以nginx-1.24.0为例。cd /usr/local/src wget http://nginx.org/download/nginx-1.24.0.tar.gz tar -zxf nginx-1.24.0.tar.gz cd nginx-1.24.05.2 配置Nginx编译选项这是最关键的一步我们需要在configure时指定使用我们编译的GmSSL。./configure --prefix/usr/local/nginx \ --with-http_ssl_module \ --with-openssl/usr/local/src/GmSSL \ --with-openssl-optshared --prefix/usr/local/gmssl \ --with-stream \ --with-stream_ssl_module \ --with-http_v2_module参数详解--prefix/usr/local/nginx: 指定Nginx的安装目录。--with-http_ssl_module: 启用HTTP SSL模块这是HTTPS的基础。--with-openssl/usr/local/src/GmSSL:核心参数告诉Nginx使用GmSSL的源码路径进行编译而不是系统自带的OpenSSL。--with-openssl-optshared ...: 传递给GmSSL被当作OpenSSL的配置选项确保编译出共享库。--with-stream和--with-stream_ssl_module: 启用TCP/UDP代理的SSL支持用途更广。--with-http_v2_module: 启用HTTP/2支持提升性能。运行./configure后仔细查看输出。确保在OpenSSL library和OpenSSL headers这两行路径指向的是/usr/local/gmssl或你的GmSSL源码目录而不是系统的/usr/include/openssl。5.3 编译与安装Nginx配置无误后开始编译和安装。# 编译同样使用多核加速 make -j$(nproc) # 安装 sudo make install安装完成后Nginx的可执行文件位于/usr/local/nginx/sbin/nginx配置文件位于/usr/local/nginx/conf/nginx.conf。5.4 创建系统服务文件方便管理为了方便使用systemctl启动、停止Nginx我们为其创建一个service文件。sudo vi /etc/systemd/system/nginx.service将以下内容粘贴进去[Unit] DescriptionThe nginx HTTP and reverse proxy server Afternetwork.target remote-fs.target nss-lookup.target [Service] Typeforking PIDFile/usr/local/nginx/logs/nginx.pid ExecStartPre/usr/local/nginx/sbin/nginx -t ExecStart/usr/local/nginx/sbin/nginx ExecReload/bin/kill -s HUP $MAINPID ExecStop/bin/kill -s QUIT $MAINPID PrivateTmptrue [Install] WantedBymulti-user.target保存退出后重新加载systemd配置并设置开机自启。sudo systemctl daemon-reload sudo systemctl enable nginx6. 配置Nginx使用SM2证书现在我们有了支持国密的Nginx和SM2证书接下来就是修改Nginx配置让其加载我们的证书并启用国密套件。6.1 备份与编辑Nginx主配置文件首先备份原始配置然后进行编辑。sudo cp /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.bak sudo vi /usr/local/nginx/conf/nginx.conf我们需要在http块内添加一个server块来配置HTTPS监听。找到http {这一行在其内部添加如下配置。你可以替换掉文件中已有的示例server块。http { # ... 其他原有配置 ... server { listen 443 ssl http2; # 监听443端口启用SSL和HTTP/2 server_name test.gm.com; # 替换为你的域名或服务器IP # 指定SM2证书和私钥路径 ssl_certificate /etc/nginx/certs/gmssl/server.crt; ssl_certificate_key /etc/nginx/certs/gmssl/server.key; # 国密SSL协议配置GmSSL扩展 # 启用TLSv1.1, TLSv1.2, TLSv1.3。根据GmSSL支持情况可能还有国密特有的协议如NTLS。 ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; # 国密密码套件优先 # ECC-SM2-WITH-SM4-SM3 是典型的国密套件。 # 后面兼容了一些国际通用套件以确保最大程度的兼容性。 ssl_ciphers ECC-SM2-WITH-SM4-SM3:ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_prefer_server_ciphers on; # SSL会话缓存优化 ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 安全相关的HTTP头部可选但推荐 add_header Strict-Transport-Security max-age63072000; includeSubdomains; preload; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; location / { root html; index index.html index.htm; } # 可选配置错误页面 error_page 500 502 503 504 /50x.html; location /50x.html { root html; } } # 可选将HTTP请求重定向到HTTPS server { listen 80; server_name test.gm.com; return 301 https://$server_name$request_uri; } }6.2 测试配置并启动Nginx在启动服务前务必测试配置文件语法是否正确。sudo /usr/local/nginx/sbin/nginx -t如果输出nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful说明配置语法正确。现在启动Nginx服务sudo systemctl start nginx sudo systemctl status nginx检查服务状态是否为active (running)。同时检查443端口是否在监听sudo netstat -tlnp | grep :443你应该能看到nginx进程正在监听443端口。6.3 本地主机名解析测试用由于我们证书的CN是test.gm.com在测试服务器本机访问时需要修改hosts文件使其解析到本机。echo 127.0.0.1 test.gm.com | sudo tee -a /etc/hosts7. 验证与测试国密HTTPS服务服务启动后我们需要从多个维度验证国密HTTPS是否真正生效。7.1 使用GmSSL的s_client工具测试这是最直接的验证方式使用我们安装的GmSSL客户端连接服务器并查看协商出的密码套件。openssl s_client -connect test.gm.com:443 -servername test.gm.com在命令输出中你需要重点关注以下几行SSL handshake has read ... bytes and written ... bytes表示握手成功。在SSL-Session:部分查看Cipher一行。如果配置成功这里应该显示ECC-SM2-WITH-SM4-SM3或你配置的其他国密套件。同时Peer signature type应该显示SM2。如果Cipher显示的是ECDHE-RSA-AES256-GCM-SHA384之类的国际套件说明国密套件协商失败需要回头检查Nginx的ssl_ciphers配置和GmSSL的编译情况。7.2 使用浏览器测试需安装根证书普通浏览器如Chrome、Firefox默认不识别SM2证书也不信任我们的自签名CA。需要将之前生成的ca.crt导入到操作系统的受信任根证书颁发机构存储中。将ca.crt文件从服务器下载到你的本地电脑。Windows双击ca.crt选择“安装证书”存储位置选择“受信任的根证书颁发机构”。macOS双击ca.crt将其添加到“登录”或“系统”钥匙串然后找到该证书右键“显示简介”在“信任”部分选择“始终信任”。Linux拷贝到/usr/local/share/ca-certificates/然后执行sudo update-ca-certificates。导入CA证书后在浏览器访问https://test.gm.com。虽然可能还会因为证书扩展属性等细节有轻微警告但主要的安全警告未知颁发机构应该消失。你可以点击地址栏的小锁图标查看证书详情应该能看到签名算法是sm2-with-sm3公钥算法是SM2。7.3 使用国密浏览器测试为了获得最佳的国密体验可以使用支持国密算法的专用浏览器进行测试例如密信浏览器对国密协议支持较为完善。零信浏览器同样专注于国密生态。在这些浏览器中访问你的站点它们能更好地识别和优先协商国密套件。8. 常见报错与深度排查指南在实际操作中你几乎一定会遇到各种报错。下面我整理了从环境准备到服务上线全流程中最常见的错误及其解决方案。8.1 GmSSL编译与安装阶段报错报错1configure: error: no acceptable C compiler found in $PATH原因系统未安装GCC等编译工具链。解决执行yum groupinstall -y Development Tools。报错2relocation R_X86_64_32 against.rodata‘ can not be used when making a shared object; recompile with -fPIC原因编译GmSSL时未生成位置无关代码PIC导致Nginx链接时出错。解决在编译GmSSL时确保./config命令中包含了shared参数。如果已经编译过需要make clean后重新./config。报错3执行openssl version仍然显示系统OpenSSL版本原因环境变量未生效或PATH顺序不对。解决检查~/.bashrc中export PATH/usr/local/gmssl/bin:$PATH是否正确。执行source ~/.bashrc或重新打开终端。使用绝对路径测试/usr/local/gmssl/bin/openssl version。检查/etc/ld.so.conf.d/下是否有其他文件将系统OpenSSL库路径放在前面。8.2 Nginx编译阶段报错报错4./configure: error: SSL modules require the OpenSSL library.原因Nginx的configure脚本没有找到正确的OpenSSLGmSSL开发库。解决确认--with-openssl参数指向的GmSSL源码路径绝对正确且存在。确认已成功安装GmSSL/usr/local/gmssl/lib下有libssl.so和libcrypto.so。尝试在configure命令中显式指定库和头文件路径但通常不需要./configure ... --with-cc-opt-I/usr/local/gmssl/include --with-ld-opt-L/usr/local/gmssl/lib64报错5make编译过程中出现undefined reference toSSL_CTX_set1_sigalgs_list‘ 等链接错误原因Nginx源码版本与GmSSL版本不兼容。某些较新的Nginx版本使用了较新OpenSSL才有的API而GmSSL可能基于较旧的OpenSSL分支。解决尝试降低Nginx版本。例如使用nginx-1.20.x或nginx-1.18.x这类长期支持版它们与GmSSL的兼容性通常更好。这是一个非常常见的坑。8.3 Nginx启动与运行阶段报错报错6nginx: [emerg] SSL_CTX_set_ciphersuites(ECC-SM2-WITH-SM4-SM3) failed (SSL: error:1410D0B9:SSL routines:SSL_CTX_set_ciphersuites:no cipher match)原因Nginx在启动时无法识别ssl_ciphers指令中配置的国密套件名称。解决首要检查确认Nginx确实是链接到GmSSL库的。运行/usr/local/nginx/sbin/nginx -V查看输出中的built with OpenSSL一行其版本号应包含GmSSL字样且路径是/usr/local/gmssl相关的。如果显示的是系统OpenSSL版本则说明编译失败Nginx并未链接GmSSL。如果链接正确可能是套件名称写法问题。尝试使用GmSSL支持的完整套件名。可以通过openssl ciphers -v命令查看GmSSL支持的所有套件列表找到确切的国密套件名称。报错7nginx: [emerg] BIO_new_file(/etc/nginx/certs/gmssl/server.crt) failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen(/etc/nginx/certs/gmssl/server.crt,r) error:2006D080:BIO routines:BIO_new_file:no such file)原因Nginx配置文件里指定的证书或私钥文件路径错误或文件不存在或权限不足。解决使用绝对路径并检查路径中每一个目录和文件是否存在ls -la /etc/nginx/certs/gmssl/。确保Nginx工作进程用户默认是nobody或nginx有读取这些文件的权限。通常需要sudo chmod 644 server.crt ca.crt和sudo chmod 600 server.key。检查SELinux上下文如果SELinux开启可能需要调整sudo chcon -Rt httpd_sys_content_t /etc/nginx/certs/。报错8浏览器访问提示“不安全”、“NET::ERR_CERT_AUTHORITY_INVALID”原因浏览器不信任颁发该服务器证书的CA即我们自签的ca.crt。解决按照7.2节的步骤将ca.crt导入到客户端操作系统的受信任根证书存储中。注意手机浏览器也需要单独导入。报错9GmSSLs_client测试能连接但浏览器无法连接原因可能的原因较多。排查步骤防火墙确认服务器防火墙firewalld/iptables已开放443端口sudo firewall-cmd --list-ports或sudo iptables -L -n。Nginx监听确认Nginx正确监听了443 sslsudo netstat -tlnp | grep nginx。协议与套件兼容性浏览器可能不支持国密套件。尝试在Nginx的ssl_ciphers中添加一个强国际套件如ECDHE-RSA-AES256-GCM-SHA384到列表最前面测试如果浏览器能访问了则证明是国密套件协商问题。此时需要检查浏览器兼容性或使用国密浏览器。证书域名不匹配确保浏览器访问的URL中的域名与证书中的Common Name (CN)或Subject Alternative Name (SAN)完全一致。localhost、127.0.0.1和test.gm.com被视为不同的域名。8.4 性能与调试技巧问题10如何验证HTTPS流量确实使用了国密算法解决除了查看协商的密码套件还可以使用Wireshark等抓包工具在客户端在TLS握手阶段的Client Hello和Server Hello报文中查看Cipher Suites字段。如果看到0xE0, 0x13之类的标识对应TLS_SM4_GCM_SM3或TLS_ECC_SM4_GCM_SM3则说明国密套件被成功协商。GmSSL的国密套件有特定的标识符。问题11Nginx支持国密双证书吗解决国密标准中有时会要求同时使用签名证书和加密证书双证书。GmSSL和部分国密浏览器支持此特性。在Nginx配置中可以通过ssl_certificate和ssl_certificate_key指令分别指定两套证书和密钥但需要Nginx使用支持双证书的特定分支或补丁。社区版的Nginx GmSSL标准编译方式通常只支持单证书签名加密合一模式。如需双证书需要寻找打了国密双证书补丁的Nginx版本。整个流程走下来从系统准备到服务验证虽然步骤繁多但每一步都有其明确的目的。最关键的三个点在于第一确保GmSSL编译安装成功并被系统正确识别第二确保Nginx在编译时正确链接到了GmSSL库第三证书的生成和配置细节要准确无误。遇到问题别慌按照上面报错指南的思路从环境、配置、权限、兼容性这几个维度逐一排查总能找到突破口。国密改造是趋势早点掌握这套流程无论是在合规项目还是技术储备上都会让你更有优势。