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 知识结构速览
为了让你脑子里有一条清晰的链路,我画了张图。
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 了。