ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

国密Nginx编译配置与双证书落地实践

国密Nginx编译配置与双证书落地实践 这几个月找我聊“国密 Nginx”的人特别多场景高度统一要么是项目赶着过等保、密评要么是内网业务系统做信创适配总之最后都落到一个动作上——把线上跑的 Nginx 整成支持国密算法SM2/SM3/SM4的版本。这事听起来像是个“换证书”的活真上手才发现是个从底层 TLS/SSL 库到编译参数、证书格式、协议套件都要重新过一遍的工程。我刚开始做的时候也踩了不少坑尤其是双证书配置和离线环境下编译依赖收集这两块一度把自己绕进去。这篇文章就把我这段时间的完整实践记录、方案选型逻辑和排错经验都整理出来给同样需要搭国密 Nginx 的运维或后端同学一个可以直接抄作业的参考。1. 先从需求说起为什么会有“国密 Nginx”这个事儿1.1 这里说的“国密 Nginx”到底指什么先把概念说透。Nginx 本身不是一个算法库它处理 HTTPS 用的 TLS/SSL 能力全部来自底层依赖的加密库常见的是 OpenSSL。所谓“国密 Nginx”严格来说不是 Nginx 这个软件自己多了什么神奇功能而是让底层加密库能够支持国密算法定义的密码套件cipher suite然后通过 Nginx 把这层能力暴露给外部连接。国密算法指的是国内发布的商用密码算法标准主要三个SM2基于椭圆曲线的非对称算法对应签名、密钥交换这些场景类似 RSA/ECDSA 的定位。SM3密码杂凑算法输出 256 位摘要对标 SHA-256。SM4对称分组加密算法分组长度和密钥长度都是 128 位对标 AES。日常听到的“国密 HTTPS”“国密 SSL”就是在 TLS 握手时使用这类套件比如TLS_ECC_SM2_SM4_CBC_SM3、TLS_ECDHE_SM2_SM4_GCM_SM3这种。服务端和客户端都得支持握手才能正常走完。1.2 你大概率属于哪种情况如果你正在搜这个主题下面三种情况里大概率有一种是你要面对的第一等保三级或者密评整改要求。测评项里往往明确要求核心业务系统使用国密算法实现身份认证和数据加密这时候 Web 服务的 HTTPS 证书和 TLS 套件都得换成国密体系。第二信创项目供应链要求。从芯片、操作系统到中间件全面国产化加密这块自然也要跟着换Nginx 作为前端流量入口首当其冲。第三内外网互通对接要求。上游主管单位或者合作方规定必须支持国密算法但同时你又不能丢掉老客户端的访问能力这就逼着你上“双证书”方案。不管哪种情况核心结论都一样不能只在配置里改一行“ssl_ciphers”就完事因为默认 OpenSSL 根本不认识 SM2/SM4 这些算法。你得先有一个能认识它们的加密库。1.3 关键概念单证书与双证书这里先铺垫一个贯穿全文的概念——双证书。正常 HTTPS 是一张证书完成身份认证和密钥协商国密场景常用的是“双证书”方案一张 SM2 国密证书用来走国密套件一张国际算法证书RSA 或 ECDSA用来兼容不支持国密的客户端。打个比方国密证书是给自家大院里的人用的专用门禁国际证书是给外来访客用的临时通行证。两套体系在同一个门上共存内部访客走专用通道外部访客走通用通道互不干扰。后面第 3 章会详细讲它在 Nginx 配置里怎么落地这里先记住一句话双证书不是让你开两个 server block而是在同一个 TLS 服务上同时挂两种证书让底层库根据客户端协商结果自动选。2. 技术方案选型别一上来就编译先看清几个选项2.1 普通 Nginx 为什么不行很多人在项目里第一步就是把系统自带的 nginx 装上然后配一个.crt结尾的 SM2 证书发现浏览器直接打不开看错误日志发现是no suitable key share或者unsupported protocol。原因很直接发行版默认的 Nginx 依赖系统 OpenSSL而标准 OpenSSL 官方主线很长一段时间是不带国密套件支持的。就算你手上有合法 CA 签发的 SM2 证书OpenSSL 不认 SM2 密钥握手在第一个环节就失败。所以方案的核心不是 Nginx而是“选一个带国密能力的 TLS/SSL 库再把 Nginx 绑到它上面编译出来”。2.2 市面上常见的四条路线我整理了一下当前能落地的方案大致分四类各有各的适用场景。方案实现方式优点缺点适合场景商业国密网关直接用厂商打包的 WAF/负载均衡设备开箱即用证书双栈管理成熟贵架构上多了一层设备预算充足、采购流程规范的政企项目商业 Nginx 生态版使用官方商业版本中的国密模块有官方支持和现有配置兼容需要采购订阅非开源对服务等级有要求的生产环境开源库编译用 Tongsuo/GmSSL 等开源库重新编译 Nginx代码可控离线部署可复现需要自己维护编译和版本兼容绝大多数技术团队的首选第三方补丁包给 Nginx 源码打上国密补丁再用系统库编译改动小不引入新库补丁维护滞后风险高临时验证、个人实验2.3 为什么主流方案是“开源库编译”我最终推荐并采用的路线是Tongsuo原名 BabaSSL 官方 Nginx 源码编译安装。选择它的理由有几个一是可控可复现。整个编译过程在构建机上执行一遍后可以顺便打好 RPM 或者 tar 包拿到纯内网环境直接部署这在政企项目里几乎是刚需。二是生态相对成熟。Tongsuo 是开源的 OpenSSL 衍生项目国密套件、双证书支持都做得比较齐全社区也一直在维护不至于出现“补丁打了三年前就没人管”的情况。三是配置上的兼容性。用--with-openssl方式把 Tongsuo 编译进 Nginx理论上只需要把系统库路径指过去Nginx 的配置主体不用大改迁移成本低。2.4 版本匹配这件事必须提前确认编译前最容易踩的坑是版本不匹配。Nginx 官方源码下载下来不一定能和所有版本的 Tongsuo 顺利编过。我踩到过编译时报类型不匹配也遇到过编完后启动正常但握手直接失败的情况后来发现是 Tongsuo 的版本太新、Nginx 的 configure 脚本还没适配。建议的做法是先看 Tongsuo 项目文档里明确测试过哪几个 Nginx 版本再挑一个同期的 Nginx 稳定版。不要习惯性“拉最新”生产环境没必要用最前沿的版本替自己趟坑。我这边验证过比较稳的组合是 Nginx 1.24.x Tongsuo 8.4.x 这个梯队组合不同自己有验证能力的话也可以自行组合。3. 核心细节拆解算法、证书、配置到底怎么配合3.1 国密算法的握手和国际算法有什么不同普通 HTTPS 握手服务端拿到客户端支持的算法列表挑一个双方都认的。国密握手多做了一件事客户端在 ClientHello 里会带一个“是否支持国密套件”的信号服务端如果配置了双证书这时候要判断是该给国密证书还是国际证书。具体协议细节各家实现有差别但整体逻辑一致TLS 1.2 的扩展字段里携带国密标识服务端拿到后用对应的证书完成握手。套件里的算法分工是这样SM2完成服务端身份证书的签名验签和密钥协商类似 ECDHE 里的 ECC 角色。SM4负责握手之后的数据对称加密。SM3作为哈希算法出现在套件名里用于消息验证码。所以如果你看到一份国密套件表里面一定有 SM2、SM4、SM3 三个角色的同时出现。少了任何一个或其中一环用的还是国际算法严格来讲都不能叫“国密”。3.2 双证书在 Nginx 里是怎么体现的双证书的配置在不同构建版本里语法略有不同但核心思路一致一个 server 块里同时提供两张证书。以 Tongsuo 支持的双证书配置为例典型的配置长这样server { listen 443 ssl; server_name www.example.com; # 国际算法证书 ssl_certificate /etc/nginx/certs/rsa.crt; ssl_certificate_key /etc/nginx/certs/rsa.key; # 国密 SM2 证书 ssl_certificate /etc/nginx/certs/sm2.crt; ssl_certificate_key /etc/nginx/certs/sm2.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECC-SM2-SM4-CBC-SM3:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers on; }这里有个点要留意不同补丁对“第二张证书”的指令名不一样。有的实现里是重复用ssl_certificate有的单独给了ssl_certificate_sm2。我上面的写法是其中一种常见形式你拿到一个构建好的二进制后第一件事就是看它文档里对双证书的指令命名别照抄网上的配置就往上填。3.3 证书文件格式和路径规划国密证书通常也是 PEM 格式和普通证书一样有.crt、.key、.chain.crt这种结构。但要注意两件事第一服务端证书和 CA 证书链要在一个文件里时顺序通常是“服务器证书放最上面中间 CA 往下排根 CA 最后”。这个顺序错了握手时客户端可能报证书链不完整。第二SM2 密钥对生成时一定要指定正确的曲线参数。SM2 用的是sm2p256v1曲线不是国际常用的prime256v1两个名字长得像混用之后生成的密钥不是 SM2 算法对应的私钥后续签名验签全是问题。就像你不能拿美标充电器直接插欧标插座一样曲线参数搞错算法第一步就废了。3.4 没有 CA 颁发的国密证书能不能自测测试环境当然可以自己签用 Tongsuo 或 GmSSL 的命令行工具就行。我常用的是 GmSSL 命令# 生成 SM2 私钥 gmssl ecparam -genkey -name sm2p256v1 -out sm2.key # 生成自签证书 gmssl req -new -x509 -key sm2.key -out sm2.crt -subj /CNtest.example.com注意这种方式签发的是自签证书浏览器会有不受信任的告警只适合本地验证握手流程生产环境必须用具备 CA 资质的机构签发的合规国密证书并且在测评报告里这种证书才算数。4. 实操过程在 AlmaLinux 9 上从零编译国密 Nginx4.1 环境准备和依赖安装我这次实践用的是 AlmaLinux 9 系统镜像源走的是国内镜像站。如果构建机是纯内网环境方法在后面第 5 章的单列小节里讲这里先按能联网的构建机来。先装编译工具和基础依赖dnf groupinstall Development Tools -y dnf install pcre pcre-devel zlib zlib-devel openssl openssl-devel -y然后下载 Nginx 源码和 Tongsuo 源码。Nginx 可以到官网拉取也可以走国内镜像站下载源码包速度快一点。Tongsuo 在 GitHub 上就有发布包找对应版本的 release 下载 tar.gz 即可。把两个包放到同一份工作目录下比如/opt/gm-build/解压备用。cd /opt/gm-build tar zxf nginx-1.24.0.tar.gz tar zxf tongsuo-8.4.2.tar.gz4.2 configure 阶段的参数选择Nginx 的 configure 脚本提供了--with-openssl参数可以把整个 Tongsuo 源码目录交给它编译时会让 Nginx 链接到 Tongsuo 而不是系统 OpenSSL。这一步要把配置参数写清楚避免遗漏模块cd /opt/gm-build/nginx-1.24.0 ./configure \ --prefix/opt/nginx-gm \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-http_gzip_static_module \ --with-http_v2_module \ --with-openssl/opt/gm-build/tongsuo-8.4.2 \ --with-cc-opt-O2 -g make -j$(nproc) make install几个参数的说明--prefix/opt/nginx-gm指定安装目录建议单独目录别把国密版本覆盖到系统已有 Nginx 的路径上两个版本共存时排查问题方便。--with-http_ssl_module必须有没有它 Nginx 不支持 HTTPS。--with-http_v2_module建议加上现在 HTTP/2 几乎是标配。--with-openssl后面的路径一定要指向 Tongsuo 目录这一步写错了后面一切都白干。编译完成后验证一下二进制是不是真的带上了国密能力/opt/nginx-gm/sbin/nginx -V看输出里 OpenSSL 的版本信息是不是指向 Tongsuo。如果显示的还是系统 OpenSSL说明 configure 阶段路径没生效回去检查路径。4.3 配置文件与证书放置新建一个独立的配置目录按习惯整理mkdir -p /opt/nginx-gm/conf/certs cp /path/to/sm2.crt /opt/nginx-gm/conf/certs/ cp /path/to/sm2.key /opt/nginx-gm/conf/certs/ cp /path/to/rsa.crt /opt/nginx-gm/conf/certs/ cp /path/to/rsa.key /opt/nginx-gm/conf/certs/然后在主配置里打开 HTTPS server 块。如果是全新安装直接编辑/opt/nginx-gm/conf/nginx.conf把上一章示例里的 server 块填进去。如果是给现有业务加国密支持建议先复制原 server 块出来改保证路径和原有 location 规则不变。配好之后先测配置/opt/nginx-gm/sbin/nginx -t显示syntax is ok之后启动/opt/nginx-gm/sbin/nginx看进程确认ps -ef | grep nginx4.4 验证国密套件是否真的生效启动服务以后最关键的环节是验证握手。这里我用的是 curl 和 openssl 客户端双验证。先验证国际算法套件是否正常curl -I https://127.0.0.1 --resolve www.example.com:443:127.0.0.1能返回 HTTP 头说明国际证书和套件没问题。再验证国密套件curl 需要带出指定的套件名curl -I https://www.example.com --resolve www.example.com:443:127.0.0.1 \ --ciphers ECC-SM2-SM4-CBC-SM3 \ --tls-max 1.2 -k这里--tls-max 1.2要特别说明一下很多国密套件只在 TLS 1.2 里有定义TLS 1.3 的套件机制完全不同国密目前的落地形态大多还是集中在 TLS 1.2不要期望用 TLS 1.3 的配置去匹配国密套件。用 openssl 客户端更直观openssl s_client -connect 127.0.0.1:443 -servername www.example.com \ -cipher ECC-SM2-SM4-CBC-SM3 -tls1_2如果握手成功输出里会看到 Cipher 一栏精确显示你指定的国密套件名同时证书信息显示的是 SM2 证书的信息。如果失败去/opt/nginx-gm/logs/error.log翻日志重点看有没有no shared cipher或者unknown option这类报错。5. 常见问题与排查技巧实录这一章把我实际踩过的坑和给朋友排查时遇到的高频问题整理成速查表建议直接保存下来。现象可能原因处理方式nginx -t报配置错误ssl_certificate_sm2指令在使用的构建中不存在确认你用的构建对双证书指令的命名按实际指令名配置握手时报no appropriate key share客户端不支持 SM2 密钥协商或套件不匹配回退到国际证书或用支持国密的客户端再测报no shared cipher服务端 ssl_ciphers 里没有写的那个套件检查配置是否启用了国密套件名且 TLS 版本是 1.2浏览器显示证书不受信任自签证书或者证书链没配完整生产环境用合规 CA 证书检查证书链顺序编译报 OpenSSL 版本冲突系统里既有 OpenSSL 又被手动指定清理环境变量确保 configure 里只通过--with-openssl指定服务器重启后 Nginx 没自动起来没有配置 systemd 服务写一个独立的 nginx-gm.service不要和系统自带 nginx 混淆5.1 内网离线环境怎么快速复现很多项目构建机不能连外网这是最常见的问题。我当时处理方式是在能联网的构建机上把所有 RPM 依赖和源码包全部拉下来然后通过移动介质搬运到内网环境。源码包本身很好办tar.gz 直接拷进去就能解压编译。系统依赖包我建议用dnf download把涉及的 RPM 一次性拉下来dnf download --resolve --alldeps pcre-devel zlib-devel openssl-devel到了内网机器上用dnf install ./*.rpm本地安装再继续编译步骤。整个流程在 x86_64 和 aarch64 环境我都跑过注意 aarch64 机器要提前确认编译工具链完整。另外内网机器的/etc/hosts里域名解析可能和证书里的 CN 对不上测试时建议用--resolve参数或改 hosts 文件。5.2 排查时最值得看的几个日志位置出问题时别急着到处乱翻核心看四个地方/opt/nginx-gm/logs/error.logNginx 运行时的握手报错都在这里no suitable key share、sslv3 alert handshake failure这些关键字一眼就能扫出来。/opt/nginx-gm/logs/access.logHTTP 层请求日志排除是不是根本没到 TLS 阶段。openssl s_client的交互输出可以看到证书链、协商的协议版本和套件名。构建机的 configure 日志确认--with-openssl是否真实生效。5.3 老客户端的兼容性和配置注意点做国密改造最大的痛苦在于你不能假设所有客户端都认国密。Windows 7 自带的浏览器、各种旧版本手机客户端、部分内部自研程序都可能不支持 SM2 套件。这时候双证书机制的价值就体现出来了国密客户端走国密证书其他客户端自动回退到国际证书服务端配置上不用额外做客户端区分。但有个细节要注意老客户端往往连 TLS 1.3 都不支持至少得等到 TLS 1.2 才能握手。所以对外提供服务的国密站点不建议把 TLS 版本下限设得太高我一般不会为了“看起来更安全”就把 TLS 1.2 禁掉除非客户明确要求且客户端覆盖面可控。6. 我再多啰嗦几句实际操作心得这个项目做完之后我有几个比较深的体会。第一个体会是国密 Nginx 不是“装上就完事”的一次性投入。编译完了、证书配好了、curl 能通了只是第一步。真正麻烦的是后续的证书更新SM2 证书同样有有效期CA 续期时如果换了密钥对得在 Nginx 里替换证书和私钥并 reload。这里建议把证书到期时间加进监控系统里别等客户端全挂了才想起来。第二个体会是二进制的版本信息一定要沉淀到团队的文档里。我当时同时维护了两套环境一套是 x86 的测试机一套是 aarch64 的生产机编译参数不完全一样配置文件名也有细微差异。没有记录的话过几个月自己回来看都容易懵。建议把 configure 参数、依赖包版本、证书路径、验证命令全部写成一份部署记录放进项目仓库。最后再分享一个小技巧验证国密套件是否生效时可以用抓包工具看 ClientHello 和 ServerHello 里选中的套件名。如果手头不方便抓包就用 openssl s_client 把输出导出来搜索Cipher :那行的内容。看到是ECC-SM2-SM4-CBC-SM3这种具体名字比看什么都踏实。这个过程可能有点繁琐但一旦搞顺了后续再做其他国密改造项目基本就是把同样的流程再走一遍不会再有大坑了。
返回列表