国密 TLCP 实战:GmSSL / OCL / Nginx 版本选型与全部调试修改说明

本文面向发布到 CSDN,汇总本人在 Windows + WSL2 编译、Docker 部署、CentOS 生产环境跑通 Nginx 国密 HTTPS(TLCP) 时使用的源码版本目录布局 ,以及为调通而做的全部修改(含配置、脚本、证书处理;不含对 Nginx 核心 C 源码的 patch)。

参考博文:基于 GmSSL 搭建 Nginx 国密反代(流程可参考,但部分写法如 ssl_protocols GMTLS 与当前 OCL/Nginx 行为不完全一致,下文会说明)。


一、环境与目录布局

环境 说明
开发机 Windows 10/11 + WSL2 Ubuntu 24.04
生产机 CentOS 7 系(10.131.111.250),Docker 运行
源码根目录 /mnt/d/codes/(Windows 对应 D:\codes\

推荐三仓库并列:

text 复制代码
/mnt/d/codes/
├── GmSSL/                          # 国密算法库
├── OpenSSL-Compatibility-Layer/    # OpenSSL 兼容层(OCL)
└── nginx/                          # Nginx 1.25.3 源码 + Docker 工程

证书调试目录(与仓库分离,便于拷到生产机):

text 复制代码
/mnt/d/nginx_gm/certs/              # 自建 CA + 服务器 TLCP 证书
/opt/nginx/certs/                   # 生产机挂载进容器
/opt/nginx/conf/nginx.conf          # 生产机自定义主配置

二、引用的三个项目及版本(务必对齐)

2.1 版本总表

项目 仓库 推荐 Tag / 版本 用途
GmSSL https://github.com/guanzhi/GmSSL v3.1.1 OCL 依赖;gmssl 命令行生成证书
OpenSSL-Compatibility-Layer https://github.com/GmSSL/OpenSSL-Compatibility-Layer v0.8.1 提供 libssl.so / libcrypto.so,伪装 OpenSSL API
Nginx https://nginx.org 或本仓库 release-1.25.3 1.25.3 OCL README 明确测试过的版本
运行环境 版本
WSL 编译 Ubuntu 24.04 ,gcc 13.3.0
Docker 基础镜像 Ubuntu 24.04
Docker 镜像标签 nginx-gmssl:1.25.3

2.2 版本不对齐时的典型现象

错配 现象
GmSSL 用 master,Nginx/OCL 用 v3.1.1 编译 WSL 上 gmssl tlcp_clienttls.c:2829:tls_init(): error(见下文 §5.3)
Nginx 1.31+ 未在 OCL 列表中 编译或握手阶段未知问题,不建议生产首选用
系统 OpenSSL 3.x 未被子替换 nginx -V 显示 SSL 但握手仍是国际算法
gmssl 与链接进 Nginx 的 libgmssl 不是同一次编译 证书工具能生成,运行时库行为不一致

2.3 编译安装顺序(固定)

text 复制代码
① GmSSL (v3.1.1)  →  sudo make install  →  /usr/local/lib/libgmssl.so
② OCL (v0.8.1)    →  sudo make install  →  /usr/local/lib/libssl.so, libcrypto.so
③ Nginx (1.25.3)  →  ./configure && make →  objs/nginx

安装 OCL 后必须确认默认头文件/库来自 OCL,而不是系统 OpenSSL:

bash 复制代码
grep -i OPENSSL /usr/local/include/openssl/opensslv.h
ldd /usr/local/lib/libcrypto.so | grep gmssl

三、各组件编译参数(最终调通版)

3.1 GmSSL v3.1.1

bash 复制代码
cd /mnt/d/codes/GmSSL
git fetch --tags
git checkout v3.1.1

mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install

# 命令行工具
which gmssl    # 期望 /usr/local/bin/gmssl

说明 :证书在 /mnt/d/nginx_gm/certsgmssl sm2keygen / certgen / reqgen / reqsign 生成;口令示例 Ilove@cn123签名私钥与加密私钥必须相同(OCL 硬性要求)。

对 GmSSL 源码的修改 (仅 git checkout v3.1.1,避免使用 master 做客户端测试)。


3.2 OpenSSL-Compatibility-Layer v0.8.1

bash 复制代码
cd /mnt/d/codes/OpenSSL-Compatibility-Layer
git fetch --tags
git checkout v0.8.1

mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install

安装结果:

  • 头文件:/usr/local/include/openssl/(OCL 头文件)
  • 库:/usr/local/lib/libssl.solibcrypto.so(底层依赖 libgmssl

对 OCL 源码的修改(生产调通路径)无 patch

理解其行为即可,不必改代码(见 §5.1 pem.c 逻辑)。

社区可选 patch(浏览器访问报错时)
OCL Issue #13 提到在 GmSSL 的 src/tlcp.ctlcp_do_accept 中注释客户端扩展校验,以兼容部分国密浏览器。本人生产调通未改此文件;若奇安信/360 握手失败可再评估。


3.3 Nginx 1.25.3

第一次 configure(踩坑,缺 PCRE)
bash 复制代码
./configure \
  --prefix=/usr/local/nginx \
  --with-http_ssl_module \
  --with-stream \
  --with-stream_ssl_module \
  --without-http_rewrite_module

objs/ngx_auto_config.h NGX_PCRE,配置里写 location ~* \.(js|css|...)$ 会报:

text 复制代码
nginx: [emerg] using regex "..." requires PCRE library
最终 configure(调通版)
bash 复制代码
sudo apt install -y libpcre3-dev

./configure \
  --prefix=/usr/local/nginx \
  --with-http_ssl_module \
  --with-stream \
  --with-stream_ssl_module \
  --with-pcre \
  --with-pcre-jit

make -j$(nproc)

当前工程内记录的编译参数(objs/ngx_auto_config.h):

c 复制代码
#define NGX_CONFIGURE " --prefix=/usr/local/nginx --with-http_ssl_module --with-stream --with-stream_ssl_module --without-http_rewrite_module --with-pcre --with-pcre-jit"

验证:

bash 复制代码
objs/nginx -V
# 应含 openssl 相关信息,且 ldd objs/nginx | grep -E 'ssl|crypto|gmssl'

对 Nginx 官方源码 src/ 的修改 。国密能力完全来自链接 OCL 的 libssl,不是 nginx 补丁模块。


四、Docker 工程修改(本仓库 nginx/docker/

采用「WSL 编好二进制 + 拷贝 .so → 镜像只打包」策略,不在镜像内重编三件套。

4.1 docker/prepare.sh(打包动态库)

将 sibling 目录编译产物拷入 vendor/lib

text 复制代码
../GmSSL/build/bin/libgmssl.so*
../OpenSSL-Compatibility-Layer/build/libssl.so*
../OpenSSL-Compatibility-Layer/build/libcrypto.so*

4.2 Dockerfile 要点

  • 基础镜像:ubuntu:24.04
  • 安装运行时依赖:libpcre3zlib1g在镜像里装开发包)
  • COPY objs/nginx/usr/local/nginx/sbin/nginx
  • ENV LD_LIBRARY_PATH=/usr/local/lib
  • EXPOSE 80 4443(生产映射 -p 443:443 时由宿主机 nginx.conf 决定 listen)

4.3 docker-entrypoint.sh 修改(重要)

修改前(旧镜像逻辑,导致容器秒退)

sh 复制代码
if [ ! -f /certs/tlcp_server_certs.pem ] || [ ! -f /certs/tlcp_server_keys.pem ]; then
    echo "error: TLCP certs required under /certs (see docker/README.md)"
    exit 1
fi

生产挂载为 /etc/nginx/certs/,文件名也可能是 sign-fullchain.pem 等,与 /certs/tlcp_server_*.pem 不一致 → entrypoint 直接 exit 1

修改后(当前文件)

sh 复制代码
#!/bin/sh
set -e

NGINX=/usr/local/nginx/sbin/nginx

ldconfig 2>/dev/null || true

if [ "$1" = "$NGINX" ] || [ "$(basename "$1" 2>/dev/null)" = "nginx" ]; then
    "$NGINX" -t
fi

exec "$@"

仅做 nginx -t不再硬编码证书路径。

4.4 docker/default.conf 修改

修改前

nginx 复制代码
ssl_certificate      /certs/tlcp_server_certs.pem;
ssl_certificate_key  /certs/tlcp_server_keys.pem;
ssl_password_file    /certs/tlcp_server_password.txt;

修改后

nginx 复制代码
ssl_certificate      /etc/nginx/certs/tlcp_server_certs.pem;
ssl_certificate_key  /etc/nginx/certs/tlcp_server_keys.pem;
ssl_password_file    /etc/nginx/certs/tlcp_server_password.txt;

docker-compose.yml 挂载 ./certs:/etc/nginx/certs:ro 一致。

4.5 生产 docker run(相对最初命令的修正)

bash 复制代码
docker run -d \
  --name nginx-gm \
  -p 443:443 \
  -v /opt/nginx/certs:/etc/nginx/certs:ro \
  -v /opt/nginx/conf/nginx.conf:/usr/local/nginx/conf/nginx.conf:ro \
  -v /opt/nginx/html:/etc/nginx/html:ro \
  -v /opt/nginx/logs:/usr/local/nginx/logs \
  nginx-gmssl:1.25.3
最初写法 问题
--name nginx-gm 重复 命令非法或覆盖
logs/var/log/nginx:ro 前缀应为 /usr/local/nginx/logs,且不能只读
cache 无关只读挂载 与 nginx 无关,易误导
旧 entrypoint + 旧镜像 证书未放到 /certs/tlcp_server_*.pem 即退出

五、生产 nginx.conf 与证书(运维侧修改)

5.1 证书文件(OCL 要求,非 Nginx 官方格式)

服务器侧三个文件(/opt/nginx/certs/):

文件 内容顺序
tlcp_server_certs.pem 终端签名证 → 终端加密证中间 CA(不要根证)
tlcp_server_keys.pem 签名加密私钥加密加密私钥 (两段 ENCRYPTED PRIVATE KEY
password.txt 一行口令,无 \r

生成示例(与 OCL README 一致):

bash 复制代码
cat signcert.pem enccert.pem cacert.pem > tlcp_server_certs.pem
cat signkey.pem enckey.pem > tlcp_server_keys.pem
printf '%s\n' 'Ilove@cn123' > password.txt

证书处理上的"修改"(非改 C 代码):

  1. 厂商未加密私钥BEGIN PRIVATE KEY)→ 用 gmssl pkcs8 -topk8 加密后再合并。
  2. 厂商分体 fullchainsign-fullchain.pem + enc-fullchain.pem)→ 改为 OCL 推荐的单证链 + 双钥文件
  3. password.txt 与密钥口令不一致 (曾用 12345678 而密钥为 Ilove@cn123)→ 统一口令并 cat -A 检查无 ^M

5.2 生产 server 块(最终)

nginx 复制代码
server {
    listen       443 ssl;
    server_name  10.131.111.250;

    ssl_certificate      /etc/nginx/certs/tlcp_server_certs.pem;
    ssl_certificate_key  /etc/nginx/certs/tlcp_server_keys.pem;
    ssl_password_file    /etc/nginx/certs/password.txt;

    ssl_ecdh_curve       sm2p256v1;
    ssl_ciphers          ECDHE-SM2-WITH-SMS4-SM3:ECDHE-SM2-WITH-SMS4-GCM-SM3;
    ssl_prefer_server_ciphers on;

    location / {
        root   /etc/nginx/html;
        index  index.html index.htm;
    }
}

相对 CSDN 参考文/初稿删除或禁止的项

nginx 复制代码
# 错误 --- Nginx 配置解析不认 GMTLS
ssl_protocols GMTLS TLSv1.2;

# 错误 --- OCL 双钥加密场景不要删
# ssl_password_file ...

说明 :TLCP 由 GmSSL 在 libssl 内完成,不需要 也不能在 ssl_protocols 里写 GMTLS

5.3 OCL 读私钥逻辑(理解即可,解释为何必须加密+口令)

OCL src/pem.cPEM_read_bio_PrivateKey 核心逻辑(v0.8.1 / main 同类):

c 复制代码
// 必须有 password 回调
if (!bio || !cb || !u) { error_print(); return NULL; }

cb(pass, sizeof(pass), 0, u);

// 同一 BIO 连续读两把 SM2 私钥
sm2_private_key_info_decrypt_from_pem(&pkey->signkey, pass, bio);
sm2_private_key_info_decrypt_from_pem(&pkey->kenckey, pass, bio);

因此:

  • 必须配置 ssl_password_file
  • tlcp_server_keys.pem 必须是 PKCS#8 加密 格式(BEGIN ENCRYPTED PRIVATE KEY);
  • 两把钥同一口令

六、GmSSL 命令行版本问题(WSL 调试)

6.1 现象

bash 复制代码
gmssl tlcp_client -get / -host 10.131.111.250 -port 443 -cacert rootcacert.pem
# /mnt/d/codes/GmSSL/src/tls.c:2829:tls_init():
# tlcp_client: error

此时服务器 docker exec nginx-gm nginx -tsuccessfulnc 443 通。

6.2 原因

WSL 中 /usr/local/bin/gmssl 若来自 GmSSL mastertls_init() 会检查 key_exchange_modes != 0,而 tlcp_client 未设置 supported_groups / signature_algorithms,导致客户端在握手前就失败

v3.1.1tls_init 无此检查,且会正确设置 conn->is_client = ctx->is_client

6.3 处理(版本切换,非改 tls.c)

bash 复制代码
cd /mnt/d/codes/GmSSL
git checkout v3.1.1
rm -rf build && mkdir build && cd build && cmake .. && make -j$(nproc)
sudo make install

6.4 不要用这些方式判断服务是否存活

工具 结果 原因
curl https://IP:443 PR_END_OF_FILE_ERROR 国际 TLS vs TLCP
openssl pkey -in tlcp_server_keys.pem unable to load key 系统 OpenSSL 不支持 SM2

七、修改项汇总表(便于 CSDN 读者对照)

序号 对象 是否改上游 C 源码 修改内容
1 GmSSL 固定 tag v3.1.1 ,不用 master 跑 tlcp_client
2 OCL 否(可选 tlcp.c 见 Issue #13) 固定 tag v0.8.1 ;理解 pem.c 双钥+解密
3 Nginx configure 增加 --with-pcre --with-pcre-jit
4 docker-entrypoint.sh 项目脚本 去掉 /certs/tlcp_server_*.pem 强校验,仅 nginx -t
5 docker/default.conf 项目配置 证书路径 /certs/etc/nginx/certs
6 生产 nginx.conf 运维配置 listen 443;删 GMTLS;OCL 三件套路径
7 证书文件 运维 合并证链/双钥;加密私钥;password.txt 去 CRLF
8 docker run 运维 日志可写、去掉错误 volume、更新镜像

八、验证通过标准(本环境)

bash 复制代码
# 1. 编译链
ldd /usr/local/nginx/sbin/nginx | grep gmssl
objs/nginx -V

# 2. 容器
docker exec nginx-gm /usr/local/nginx/sbin/nginx -t
# configuration file ... test is successful

# 3. 监听(镜像可能无 ss)
docker exec nginx-gm sh -c 'cat /proc/net/tcp' | grep 01BB   # 443

# 4. 客户端(GmSSL v3.1.1)
gmssl tlcp_client -get / -host 10.131.111.250 -port 443 -cacert rootcacert.pem

# 5. 浏览器
# 国密浏览器 + 导入 rootcacert.pem → https://10.131.111.250/

九、发布 CSDN 时的建议标签

Nginx 国密 GmSSL TLCP OpenSSL-Compatibility-Layer SM2 Docker HTTPS


十、参考链接


版权声明 :版本号与修改记录基于 2026 年 5 月实际调试环境整理;若上游 tag 更新,请以各项目 Release 说明为准,并重新做 nginx -ttlcp_client 回归。

相关推荐
深圳英康仕1 天前
五网口六USB:一台龙芯2K3000工控机的接口配置解读
嵌入式硬件·信创·工控机·工业计算机·龙芯2k3000
IPHWT 零软网络5 天前
从 SIP 软交换到国密加密:OM1000‑A‑UC 国产化 IPPBX 的架构与实战价值
架构·信息与通信·信创·国产化·ippbx
阿坤带你走近大数据5 天前
GoldenDB的介绍
信创·国产数据库
FORCECON17 天前
力控信创SCADA,全国产化适配,工业数字化监控,无缝迁移,安全可控
自动化·信创·数字化·国产化·scada·组态软件
豆豆9 天前
信创环境下CMS国产化适配实践:以.NET Core路线为例的技术验证
.netcore·cms·信创·国产化·建站系统·内容管理系统·网站管理系统
zuozewei10 天前
国产化之 GoldenDB 配置参数调优指南
信创
月光技术杂谈11 天前
openEuler各镜像目录区别、部署差异及5G基站平台稳定高性能系统构建方案
5g·华为·信创·镜像·openeuler·国产·欧拉
豆豆12 天前
国产化CMS选型实录:从零部署PageAdmin到麒麟系统的实战笔记
笔记·信创·国产化·建站系统·建站·内容管理系统·网站管理系统
豆豆15 天前
国产化CMS怎么选?主流信创产品与平滑迁移方案全解析
cms·网站建设·网站制作·信创·国产化·网站开发·网站改造