功能说明
该示例用共享密钥对消息计算 HMAC-SHA256 标签,并以恒定时间函数比较提供的标签与期望值。恒定时间比较遍历全部字节而不过早返回,使攻击者无法通过响应耗时逐字节推断正确标签,这在验签接口上尤为重要,因为普通相等比较会因首个不同字节位置而提前退出。
关键代码
Sign 用 hmac.New 构建写消息并取摘要,密钥与消息均为字节切片。Verify 先算出期望标签,再用 subtle.ConstantTimeCompare 比较,返回一表示相等。二者长度不同时该函数立即返回零,因此长度不匹配也会失败而不会索引越界;调用方无需自行对齐长度。SignHex 提供十六进制字符串形式,便于放进以文本传输的头部。
使用注意
验签应在任何解析或副作用之前进行;计算所用消息必须与签名时的原始字节完全一致,框架若重新编码 JSON 会使校验失败。在该 API 之外自行用普通比较或提前返回会重新引入时序泄漏,务必坚持恒定时间比较。密钥来自密钥管理服务,并支持轮换时同时接受新旧两把;长度比较本身可能轻微泄漏长度信息,但攻击者无法据此推出内容。
代码示例
package signing
import (
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"errors"
)
var ErrInvalidMAC = errors.New("invalid message authentication code")
func Sign(secret, message []byte) []byte {
mac := hmac.New(sha256.New, secret)
mac.Write(message)
return mac.Sum(nil)
}
func Verify(secret, message, provided []byte) error {
expected := Sign(secret, message)
if subtle.ConstantTimeCompare(expected, provided) != 1 {
return ErrInvalidMAC
}
return nil
}
func SignHex(secret, message []byte) string {
return hex.EncodeToString(Sign(secret, message))
}