C++如何调用HMAC-SHA256生成消息认证码?(OpenSSL示例)

1次阅读

最简路径是直接用 openssl 的 hmac() 函数,但需严守参数顺序、正确传入密钥/消息长度(避免 strlen 截断)、用 &digest_len 获取实际长度,并注意返回指针指向静态缓冲区;若需流式计算或精细控制上下文,应选用 hmac_ctx(1.0.x 用 init/cleanup,1.1.1+ 用 new/free);base64 编码必须通过 bio_f_base64() 实现并禁用换行,不可用 hex 拼接;签名前的字符串拼接须严格对齐服务端规则,包括小写头名、去空格值、n 分隔等,建议封装验证函数并比对原始字节数组。

C++如何调用HMAC-SHA256生成消息认证码?(OpenSSL示例)

直接用 HMAC() 函数是最简路径,但参数顺序和内存管理容易翻车

OpenSSL 的 HMAC() 是一个纯函数式接口,不用初始化上下文,适合一次性计算。但它要求你手动传入密钥长度、消息长度、摘要算法指针,且返回的 digest 内存由你负责释放(或用空间)。常见错误是把 key.Length() 写成 strlen(key.c_str()) ——当 key 含 字节时结果截断;或者忽略 digest_len 输出参数,直接按固定长度读取 32 字节,导致越界。

  • 密钥和消息都必须用 const unsigned char* 传入,std::String 要显式转 .c_str() 并确保生命周期覆盖调用全程
  • HMAC() 返回的 digest 指针指向内部静态缓冲区,不可 free,也不可长期持有(下次调用会覆盖)
  • SHA256 的输出长度恒为 32 字节,但务必用 &digest_len 接收,别硬写 32

HMAC_CTX 方式更可控,但初始化和清理不能省

老版本 OpenSSL(1.0.x)必须用 HMAC_CTX,新版本(1.1.1+)虽支持单次 HMAC(),但若需多次 update(比如流式计算),或想明确控制上下文生命周期,HMAC_CTX 仍是首选。问题在于:忘记 HMAC_CTX_init() 或漏掉 HMAC_CTX_cleanup() 会导致内存泄漏或后续调用崩溃;而 1.1.1+ 已弃用 HMAC_CTX_init(),改用 HMAC_CTX_new() + HMAC_CTX_free()

  • 1.0.x:HMAC_CTX ctx; HMAC_CTX_init(&ctx); ... HMAC_CTX_cleanup(&ctx);
  • 1.1.1+:HMAC_CTX* ctx = HMAC_CTX_new(); ... HMAC_CTX_free(ctx);
  • 不要混用:在 1.1.1+ 中调用 HMAC_CTX_init() 会编译失败或运行时 SIGSEGV

Base64 编码必须用 BIO,手搓 hex 转字符串不等于 Base64

很多示例代码把 HMAC 结果转成 64 位十六进制字符串(如 "a1b2c3..."),这**不是** Base64。真实通信场景(如 http Sign 头)要求的是 Base64 编码的二进制摘要。OpenSSL 的 BIO_f_base64() 是最稳妥的选择,但要注意关闭换行符(BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL)),否则生成的字符串带 n,服务端校验必失败。

  • 别用 sprintf("%02x") 拼接——那是 hex 编码,长度是 64 字符,Base64 是约 44 字符
  • BIO_get_mem_ptr() 返回的 BUF_MEM*data 不以 结尾,构造 std::string 必须传 length
  • 如果用第三方 Base64 库(如 cppcodec),确认它处理的是原始 32 字节,而非 hex 字符串

签名拼接顺序必须和服务端完全一致,空格和换行都是破坏性差异

HMAC 本身不关心内容语义,只认字节流。所谓“按顺序合并所有消息”,意味着请求头字段名要小写、字段值要去首尾空格、多头同名要按出现顺序拼、n 分隔符不能少也不能多。例如 GETn/v1/apina=1&b=2ntoken:abcnuser-id:123GETn/v1/apina=1&b=2ntoken: abcnuser-id:123 算两个完全不同输入,HMAC 值必然不同。

立即学习C++免费学习笔记(深入)”;

  • 建议把拼接逻辑封装成独立函数,并用固定测试用例验证前后端输出一致
  • 开发期用在线工具(如 lddgo.net/encrypt/hmac)比对中间结果,避免怀疑加密库有问题
  • 服务端若用 Java 的 Mac.getInstance("HmacSHA256"),注意其默认使用 PKCS#5 填充——但 HMAC 本身无填充,实际只需确保密钥和消息字节数组完全一致

真正卡住人的往往不是算法调用,而是拼接规则没对齐、编码方式选错、或者密钥里有不可见字符。上线前拿一个已知明文+密钥,在两端各跑一次,逐字节比对 digest 原始字节数组,比看 Base64 更可靠。

text=ZqhQzanResources