跳转到主内容
极星编程网:以代码为星,赴技术山海!

C++如何调用SM2/SM3/SM4国密算法?(GMSSL或自研实现)

使用GMSSL的C++接口需手动封装OpenSSL兼容层,严格遵循其内存管理、数据类型(unsigned char*)及EVP接口规范,避免常见错误如未检查错误码、IV复用、padding缺失等。 用 GMSSL 的 C++ 接口调 SM2/SM3/SM4,得自己补 OpenSSL 兼容层 GMSSL 官方只提供 C 接口和命令行工具,C++ 直接
extern "C"
包一层就能用,但别指望有
std::string
友好封装。它的底层就是 OpenSSL 1.1.1 的 fork,所以函数签名、内存管理逻辑和 OpenSSL 高度一致——这意味着你得手动处理
BIO
、
EVP_MD_CTX
、
SM4_KEY
这类结构体的生命周期。 常见错误现象:
SM2_sign
返回
NULL
但没检查
ERR_get_error()
;
SM4_encrypt
输出乱码是因为没初始化
SM4_KEY
或 IV 复用;
SM3_Update
传了
std::string::data()
但长度传了
.size() + 1
(多算了 null terminator)。 所有密钥、IV、明文/密文缓冲区必须是
unsigned char*
,别传
char*
(符号扩展可能翻车)
SM2_do_sign
要求私钥是
EC_KEY*
格式,不能直接喂 PEM 字符串——得先用
PEM_read_bio_ECPrivateKey
解析 SM4 的 ECB 模式不校验 padding,但 CBC 模式必须手动补 PKCS#7,GMSSL 不帮你做 SM3 哈希计算最简路径:绕过 BIO,直用 EVP 接口 别碰
SM3_Init
/
SM3_Update
这套老接口,它不支持多线程且容易忘调
SM3_Final
。直接上 OpenSSL 风格的
EVP_MD
体系,GMSSL 已注册
"sm3"
算法名。 使用场景:计算文件摘要、生成数字信封摘要、拼接签名原文前的哈希预处理。 立即学习 “ C++免费学习笔记(深入) ”;
EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx, EVP_sm3(), nullptr); EVP_DigestUpdate(ctx, data, len); unsigned char md[EVP_MAX_MD_SIZE]; unsigned int md_len; EVP_DigestFinal_ex(ctx, md, &md_len); EVP_MD_CTX_free(ctx);
必须用
EVP_sm3()
,不是
EVP_sha256()
那种宏——GMSSL 把它编译进去了,但不会出现在
OpenSSL_add_all_algorithms()
里
md_len
固定为 32,但务必用输出参数接收,别硬写
sizeof(md)
如果对同一段数据反复哈希,复用
ctx
前必须调
EVP_DigestInit_ex
重置,否则结果错 SM2 签名验签时,公钥格式和 ASN.1 编码是最大坑点 GMSSL 的
SM2_do_verify
要求公钥是
EC_KEY*
,且曲线必须是
SM2_P256v1
(即
OBJ_sm2
)。但你从 Java 或浏览器拿到的 SM2 公钥,大概率是 DER 编码的
SubjectPublicKeyInfo
,不是裸的
EC_POINT
坐标。 C函数速查手册(CHM版) C函数速查手册(CHM版) 下载 常见错误现象:
EC_KEY_set_public_key_affine_coordinates
返回 0;
SM2_do_verify
返回 -1 且
ERR_reason_error_string(ERR_get_error())
显示
EC_R_INVALID_ENCODING
。 别手写坐标解析——用
d2i_EC_PUBKEY
从 DER 数据构造
EC_KEY*
,不是
d2i_ECParameters
PEM 格式的公钥要先用
PEM_read_bio_PUBKEY
,不是
PEM_read_bio_EC_PUBKEY
(后者只认 OpenSSL 原生 EC key PEM) SM2 签名结果是 ASN.1 序列(r,s),长度固定 64 字节,但某些硬件模块返回的是拼接的 r||s(无 ASN.1 头),此时得用
SM2_SIG_recover_r_s
手动拆 自研 SM4 实现?除非你审计过加解密轮函数和 S-Box 网上能找到的 C++ SM4 实现,90% 在 S-Box 查表时用
int
当索引却没做范围检查,或在密钥扩展中把
uint32_t
移位当
int
处理导致符号扩展。更隐蔽的问题是:部分实现把 SM4 的“加密”和“解密”轮函数写反了(国密标准里加解密结构对称但子密钥顺序相反)。 性能影响:纯查表实现比 GMSSL 的 AES-NI 加速版本慢 3–5 倍;移动端 ARM 上若没用 NEON 指令,吞吐量可能不到 1MB/s。 真要自研,必须拿国密局发布的《SM4 分组密码算法》附录 A 测试向量逐字节比对,尤其关注第 32 轮输出和最终异或 别信“兼容 OpenSSL EVP 接口”的封装库——它们多数只是把 GMSSL 的 C 函数包了一层
class
,没解决线程安全问题 Android NDK 下链接 GMSSL,记得加
-lssl -lcrypto -lgmssl
,且
libgmssl.so
必须和
libcrypto.so
版本对齐,否则
dlopen
失败 SM2 密钥协商、SM4 的 GCM 模式、SM3 的 HMAC 变种这些进阶用法,文档极少,出问题基本靠翻 GMSSL 的 test 目录源码。别省这步。

相关文章