1 简介
HMAC-SHA256,即基于 SHA-256 哈希算法的消息认证码,是一种用于创建消息认证码 (MAC) 的特定结构,它将加密哈希函数 (SHA-256) 与密钥相结合。
HMAC 提供数据完整性和身份验证,确保消息未被篡改并确认发送者的身份。
HMAC算法的规范称为RFC2104,一般哈希值仅能验证数据的真实性(即,您收到的数据是否与最初发送的数据一致)。而 HMAC 则能同时验证数据的真实性和来源。

本文把 HMAC-SHA256 的原理、计算步骤、在 Go 中的标准用法与手工实现都展示出来,并解释它与对称加密算法(例如 AES)在场景和性质上的差异。
2 了解算法原理
- HMAC-SHA256 算法原理(高层)
要执行 HMAC,需要将密钥的每个字节与 0x5c 进行异或运算,从而生成一个 opad;将密钥的每个字节与 0x36 进行异或运算,从而生成一个 ipad。
消息与 ipad 和密钥进行异或运算后连接在一起,然后传递给哈希函数。哈希函数的结果再与 opad 和密钥进行异或运算后连接在一起,最终传递给哈希函数。结果就是 HMAC,表示为:
scss
H((K xor opad) || H((K xor ipad) || m)) 其中 || 表示字符串连接。
HMAC(Hash-based Message Authentication Code)是基于哈希函数的消息认证码。公式:
scss
HMAC(K, m) = H( (K' ⊕ opad) || H( (K' ⊕ ipad) || m ) )
H:哈希函数(这里用 SHA-256)
K:原始密钥
K':长度为块大小(blockSize,SHA-256 的 blockSize = 64 字节)的密钥:
若 len(K) > blockSize,则 K' = H(K)(hash 后截断/使用)
若 len(K) < blockSize,则在右侧补 0 到 blockSize
ipad:字节 0x36 重复 blockSize 次
opad:字节 0x5c 重复 blockSize 次
||:字节串连接
⊕:按位异或
- 关键点:
HMAC 不把密钥直接拼接到消息里,而是通过内外填充(ipad/opad)把密钥以安全方式和哈希结合。
抵抗常见的哈希"长度扩展攻击",比简单的 H(secret || message) 更安全。
3 更推荐用 HMAC 而不是 sha(secret + message + secret)
直接拼接(secret||m||secret 或 secret||m)会遭受长度扩展攻击(针对某些 Merkle-Damgård 结构的哈希函数,如 SHA-1、SHA-256),攻击者可利用中间状态扩展消息而不知密钥。
HMAC 使用固定的内外填充并在内部做两次哈希,设计上抵抗长度扩展,并且对密钥长度的处理更严格和安全。
SHA-256(即 256 位安全哈希算法)是一种加密哈希函数,可生成固定大小的 256 位(32 字节)哈希值。 它属于 SHA-2 系列,由美国国家安全局 (NSA) 设计并于 2001 年发布。SHA-256 广泛应用于各种应用领域,包括数据完整性验证、数字签名和区块链技术。
SHA-256 的主要目的是通过为给定输入生成唯一的哈希值来确保数据完整性。即使输入发生微小变化,也会导致哈希值明显不同,因此很容易检测到篡改。SHA-256 具有确定性,这意味着相同的输入始终会产生相同的输出。
然而,需要注意的是,SHA-256 并非加密;它是一种单向函数,因此无法逆转该过程以从哈希值中检索原始数据。
HMAC 的流程包括获取输入消息,将其与密钥组合,然后应用 SHA-256 哈希函数生成唯一的哈希值。这意味着即使发送两条相同的消息,如果使用不同的密钥,HMAC 也会有所不同,从而提供额外的安全保障。
HMAC-SHA256 广泛应用于各种安全协议,包括 TLS(传输层安全性)和 API,以确保通信安全。
SHA-256 只是一些数据(称为消息)的哈希值。它被表示为 H(m)。
HMAC-SHA256 是使用 SHA-256 的 HMAC。HMAC 是一种密钥哈希算法,可与任何哈希算法配合使用。
为了最大限度地提高安全性,HMAC 使用的密钥长度应与哈希函数输出长度相同。如果提供的密钥长度大于哈希输出长度,则使用 H(K) 而不是 K。
4 Go 中的标准(推荐)实现
用标准库 crypto/hmac + crypto/sha256,示例代码(最简单、推荐):
go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func ComputeHMACSHA256(key, message []byte) []byte {
mac := hmac.New(sha256.New, key)
mac.Write(message)
return mac.Sum(nil)
}
func main() {
key := []byte("secretkey")
message := []byte("The quick brown fox jumps over the lazy dog")
mac := ComputeHMACSHA256(key, message)
fmt.Println("HMAC-SHA256 (hex):", strings.ToUpper(hex.EncodeToString(mac)))
}
用 hmac.New(sha256.New, key),库会正确处理 key 长度(>block/<block)和 ipad/opad。
比较签名时要用 hmac.Equal(a,b)(常量时间比较)以防止时序攻击。
- 手工实现(展示内/外填充流程)------用于教学验证
下面代码与标准库输出相同,但展示了内部细节(pad、inner/outer):
go
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func ManualHMACSHA256(key, message []byte) []byte {
blockSize := sha256.BlockSize // 64
// 1. 规范化 key 到 blockSize
if len(key) > blockSize {
sum := sha256.Sum256(key)
key = sum[:]
}
if len(key) < blockSize {
key = append(key, bytes.Repeat([]byte{0}, blockSize-len(key))...)
}
// 2. 生成 ipad/opad
ipad := make([]byte, blockSize)
opad := make([]byte, blockSize)
for i := 0; i < blockSize; i++ {
ipad[i] = key[i] ^ 0x36
opad[i] = key[i] ^ 0x5c
}
// 3. inner = H(ipad || message)
innerHash := sha256.Sum256(append(ipad, message...))
// 4. outer = H(opad || inner)
outerHash := sha256.Sum256(append(opad, innerHash[:]...))
return outerHash[:]
}
func main() {
key := []byte("secretkey")
msg := []byte("The quick brown fox jumps over the lazy dog")
mac := ManualHMACSHA256(key, msg)
fmt.Println("Manual HMAC-SHA256 (hex):", strings.ToUpper(hex.EncodeToString(mac)))
}
手工实现只是用于教育和验证;生产代码应使用标准库 crypto/hmac(安全、经过审核、且更容易避免实现错误)。
5 小结
SHA-256 主要用于验证数据的完整性。它计算代表原始数据的哈希值,使用户能够确认数据未被篡改。然而,SHA-256 不提供身份验证,这意味着任何人都可以根据同一输入生成哈希值,无论他们是否获得授权。
相比之下,HMAC-SHA256 将哈希过程与密钥相结合,从而确保数据完整性和身份验证。这意味着只有拥有密钥的各方才能为特定消息生成正确的 HMAC,从而显著增强其抵御单纯利用哈希函数的攻击的安全性。
这项关键的加密技术是数字世界中数据安全的基础。无论您是经验丰富的开发人员、网络安全爱好者,还是仅仅对信息保护方式感到好奇,它都将为您揭开这些强大算法的神秘面纱。
HMAC-SHA256 则添加了一个重要的身份验证层,确保消息保持不可篡改且可验证。 深入了解它们的差异、应用以及它们在保护从在线交易到个人信息等各个领域所发挥的关键作用。