hmac.new必须传hash.hash而非直接选算法,因crypto/hmac是基于hash.hash接口的封装,不实现具体哈希算法;需先用sha256.new等创建实例再传入,否则编译失败或panic。

为什么 hmac.New 必须传入 hash.Hash 而不是直接选算法?
因为 crypto/hmac 是基于已有哈希接口构建的,它不自己实现 SHA256 或 MD5,而是包装一个 hash.Hash 实例。你得先用 sha256.New(或 md5.New 等)创建哈希对象,再交给 hmac.New —— 否则会编译失败或 panic。
常见错误是试图传入字符串如 "sha256",或者误以为 hmac.New 有类似 hmac.NewSHA256 的快捷函数(Go 标准库没有)。
- 正确写法:
hmac.New(sha256.New, key) - key 类型必须是
[]byte;传string需显式转:[]byte("my-key") - 如果 key 比哈希块长度长(如 SHA256 块长 64 字节),
hmac会自动先哈希 key,无需手动处理
签名时调用 h.Write 和 h.Sum 的顺序不能错
HMAC 不是“一次调用出结果”的函数,它复用了 hash.Hash 接口,所以流程和普通哈希一样:先 Write 数据,再 Sum 获取结果。漏掉 Write 会导致空签名;重复 Sum 不重置状态,可能拼接错误字节。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 必须在
Write后调用Sum(nil),不能写成Sum([]byte{})(虽能运行但语义不清) - 若需多次签名不同数据,每次都要新建
hmac.Hash实例,不能复用同一个对象 - 示例片段:
h := hmac.New(sha256.New, key) h.Write([]byte("hello")) sig := h.Sum(nil) // 返回 []byte,长度等于 underlying hash.Size()
验证签名时别直接比较 []byte,要用 hmac.Equal
直接用 == 比较两个 []byte 会报错(切片不可比较);而用 bytes.Equal 会有时序攻击风险 —— 攻击者可通过响应时间差异推测签名字节。
- 必须用
hmac.Equal(got, want),它做了恒定时间比较 - 两个参数都必须是非 nil 切片;任一为 nil 会 panic,建议提前判空
- 注意:
hmac.Equal不校验长度是否相等?它会先比长度,再逐字节恒定时间比较,安全
常见兼容性陷阱:key 编码、数据编码、大小写与填充
HMAC 签名对输入字节完全敏感。看似相同的字符串,因编码不同(UTF-8 vs UTF-16)、换行符(\n vs \r\n)、空格、JSON 键序、base64 填充等,都会导致签名不匹配。
- 和服务端联调前,先打印 hex:
fmt.Printf("%x", sig),确认双方输入字节完全一致 - 如果对方用 Python 的
hmac.new(key, data, 'sha256').digest(),确保 Go 侧 key 和 data 都是 raw bytes,没额外 base64 或 url encode - HTTP Header 中传递签名时,常用 base64.StdEncoding.EncodeToString(sig),注意不要用 base64.URLEncoding(末尾
=处理不同)










