国密 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_client 报 tls.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/certs 用 gmssl 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.so、libcrypto.so(底层依赖 libgmssl)

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

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

社区可选 patch(浏览器访问报错时) :
OCL Issue #13 提到在 GmSSL 的 src/tlcp.c 的 tlcp_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
  • 安装运行时依赖:libpcre3、zlib1g(不在镜像里装开发包)
  • 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. 厂商分体 fullchain (sign-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.c 中 PEM_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 -t 已 successful ,nc 443 通。

6.2 原因

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

v3.1.1 的 tls_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 -t 与 tlcp_client 回归。

相关推荐
技术拾荒的人儿6 天前
信创环境部署问卷系统,2026这五款工具的私有化支持情况
私有化部署·信创·系统运维·问卷系统·内网部署
Linux技术支持工程师11 天前
银河麒麟 V10 出问题了该看哪个日志?secure 找不到、journalctl 查不到历史,一次解决
信创·银河麒麟·journalctl·日志排查·国产化运维
DolphinDB11 天前
持续升级!DolphinDB 全面完成银河麒麟 V11 兼容性认证
时序数据库·信创·银河麒麟·dolphindb
企业通信技术笔记11 天前
信创环境下企业即时通讯IM怎么部署?5类方案的架构与适配思路
架构·私有化部署·信息与通信·信创·企业即时通讯
羌俊恩12 天前
信创数据库之华为Gaussdb 概览
信创·gaussdb·国产化·数据库改造评估·gtm-lite
安当加密030123 天前
IEC62351与GB/Z 25320合规解读:电力通信安全与能源局工控安全如何满足
国密·工控安全·iec62351·gb/z 25320·电力通信安全
fo安方23 天前
项目管理—高级集成项目管理师–认证考试–案例分析
项目管理·信创
武汉唯众智创24 天前
云计算实训室建设实战指南(2026版):从课程体系到信创落地的完整路线
云计算·信创·云计算实训室·产教融合·职业教育·云计算教学平台·实训室建设
安当加密030124 天前
政务OA国密改造:电子公文SM2签名与UKEY认证合规路径
国密·sm2签名·ukey·政务oa·电子公文
安当加密030125 天前
电力纵向加密认证与调度数据网隔离实战:从接入防护到隔离装置选型
国密·电力监控系统·纵向加密认证·隔离装置·调度数据网