4. OpenSSL 与国密支持:编译安装带国密模块的 OpenSSL,验证 SM2/SM3/SM4

嗯,到这里我们开始碰真正硬核的部分了。操作系统自带的 OpenSSL 通常不包含国密算法——说白了,你直接 openssl version 看,是不支持 SM2/SM3/SM4 的。我早期给客户做国密改造时,就遇到过一个坑:客户内部已经有 OpenSSL 1.1.1,我天真地以为直接装个补丁就行,结果发现必须重新编译,而且版本路径、符号链接、证书库全得重新整理。

这一章我们走一遍完整流程:从源码编译带国密模块的 OpenSSL,到验证三个核心算法。

4.1 为什么需要重新编译?

标准 OpenSSL 代码里没有国密算法的实现。虽然 1.1.1+ 预留了 SM2/SM3/SM4 的 OID 标识,但算法逻辑是空的。你不是缺接口,你是缺引擎。

我个人的习惯是使用 GmSSL 或者 铜锁(Tongsuo) 作为国密增强版。为什么?

  • GmSSL 基于 OpenSSL 1.1.1,原生集成 SM2/SM3/SM4,且通过国家密码管理局认证。
  • 铜锁(BabaSSL/Tongsuo)在 OpenSSL 3.0 基础上加了国密支持,API 更现代。
  • 两者都兼容你现有的 TLS 代码——你不用重写握手层,只需替换 SSL_CTX 加载的密码套件。

我的建议: 生产环境优先选铜锁,因为它长期维护且社区活跃;如果只是做协议验证,GmSSL 上手更快。

4.2 编译安装 GmSSL(带国密模块)

以 Ubuntu 20.04 为例,我们一步步来。你想想看,编译过程最头疼的是什么?依赖路径。我曾在某次 CentOS 7 上编译时,因为 libcrypto.so 版本冲突,导致系统自己的 curl 都炸了。所以 一定要指定安装前缀,不要覆盖系统 OpenSSL。

4.2.1 下载源码

wget https://github.com/guanzhi/GmSSL/archive/refs/tags/v3.1.1.tar.gz
tar xzf v3.1.1.tar.gz
cd GmSSL-3.1.1

4.2.2 配置并编译

./config --prefix=/opt/gmssl shared
make -j$(nproc)
make install

这里 --prefix=/opt/gmssl 是铜墙铁壁——所有库文件、头文件、二进制都装在这个目录下,不和系统 OpenSSL 打架。我公司里统一约定所有第三方工具都装 /opt/ 下,避免多个版本混乱。

4.2.3 配置环境变量

export PATH=/opt/gmssl/bin:$PATH
export LD_LIBRARY_PATH=/opt/gmssl/lib:$LD_LIBRARY_PATH
export PKG_CONFIG_PATH=/opt/gmssl/lib/pkgconfig:$PKG_CONFIG_PATH

别忘了写进 ~/.bashrc 或 /etc/profile.d/gmssl.sh,否则每次开终端都得跑一遍。

4.3 验证 SM2 密钥生成与签名

验证很简单,一条命令就搞定。但注意 GmSSL 的 SM2 参数写法跟标准 OpenSSL 不一样。

4.3.1 生成 SM2 私钥

gmssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2p256v1 \
  -pkeyopt ec_param_enc:named_curve -out sm2_private.pem
  • 曲线名必须用 sm2p256v1,不是 prime256v1。我早期拼错过,报错 unknown curve,浪费半小时。
  • ec_param_enc:named_curve 确保公钥中携带曲线 OID,SM2 握手协议强制要求。

4.3.2 导出公钥并签名

gmssl pkey -in sm2_private.pem -pubout -out sm2_public.pem
echo "hello 国密" | gmssl pkeyutl -sign -inkey sm2_private.pem -out msg.sig

4.3.3 验签

echo "hello 国密" | gmssl pkeyutl -verify -pubin -inkey sm2_public.pem -sigfile msg.sig

输出 Signature Verified Successfully 就对了。

避坑: 我曾试过用 OpenSSL 自带的 dgst 去验 SM2 签名,结果报 unknown digest。记住:SM2 签名用的摘要算法是 SM3,不是 SHA256。只能用 GmSSL 或铜锁的 pkeyutl 来做,不要混用。

4.4 验证 SM3 哈希运算

SM3 和 SM2 配合用,但也可以单独测试。

echo -n "国密SM3测试" | gmssl dgst -sm3

输出类似 SM3(stdin)= 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0。

你想想看,如果系统里同时有标准 OpenSSL 和 GmSSL,openssl dgst -sm3 会报 Unknown option -sm3,而 GmSSL 的 gmssl dgst -sm3 则正常。我建议项目里统一软链接 /usr/local/bin/gmssl 或者直接用全路径调用。

4.5 验证 SM4 对称加解密

SM4 是 128 位分组密码,模式通常用 CBC 或 GCM。国密规范里推荐 SM4-GCM。

4.5.1 SM4-CBC 示例

# 生成随机密钥和 IV
gmssl rand -hex 16 > sm4.key
gmssl rand -hex 16 > sm4.iv

# 加密
echo "明文数据" | gmssl enc -sm4-cbc -K $(cat sm4.key) -iv $(cat sm4.iv) > cipher.bin

# 解密
gmssl enc -d -sm4-cbc -K $(cat sm4.key) -iv $(cat sm4.iv) -in cipher.bin

GmSSL 的 enc 命令直接支持 -sm4-cbc,而标准 OpenSSL 没有这个参数。这就是为什么你一定要编译带国密模块的版本。

4.5.2 SM4-GCM 示例(推荐)

gmssl rand -hex 16 > gcm.key
gmssl rand -hex 12 > gcm.iv  # GCM 推荐 12 字节 IV

echo "TLS 记录层数据" | gmssl enc -sm4-gcm \
  -K $(cat gcm.key) -iv $(cat gcm.iv) -out cipher_gcm.bin

解密时指定 -d 即可。GCM 自带认证标签,省去了 HMAC 的麻烦。这一点在后面的 TLS 套件实现中会大量用到。

4.6 知识结构速览

为了让你脑子里有一条清晰的链路,我画了张图。

编译安装带国密模块的 OpenSSL 核心流程 下载 GmSSL / 铜锁源码 ./config --prefix=/opt/gmssl 设置 PATH / LD_LIBRARY_PATH SM2 密钥与签名验证 genpkey → pkeyutl SM3 哈希验证 dgst -sm3 SM4 加解密验证 enc -sm4-cbc/gcm Signature Verified / 哈希值匹配 提示:务必使用 GmSSL 或铜锁的命令,不要用系统自带的 openssl 命令去验证国密算法

4.7 命令速查表

算法 GmSSL 命令 标准 OpenSSL 兼容性 典型错误
SM2 密钥生成 gmssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:sm2p256v1 ❌ 不支持 sm2p256v1 unknown curve
SM2 签名 gmssl pkeyutl -sign -inkey key.pem ❌ 需要引擎 unknown digest
SM3 哈希 gmssl dgst -sm3 ❌ 缺少算法实现 unknown option
SM4-CBC 加解密 gmssl enc -sm4-cbc ❌ 无此参数 unknown cipher
SM4-GCM 加解密 gmssl enc -sm4-gcm ❌ 无此参数 unknown cipher

一句话总结: 三个算法验证通过后,你的 OpenSSL 环境才算真正具备了国密能力。下一步就可以用它来编译 Go 代码里调用的 CGO 库,或者直接作为 TLS 协议栈的底层引擎。记住,千万别图省事去改系统默认 OpenSSL 的软链接——你永远不知道 yum/apt 依赖了哪个版本。

好,这一章就到这里。接下来你就可以放心地在 Go 程序里 import “github.com/……/gmssl” 包,或者直接写 CGO 绑定调用 libcrypto.so 里的国密 API 了。