HMAC 流程

HMAC 的核心原理步骤

在这里插入图片描述
K1 = K xor ipad
K2 = K xor opad

​hash的数据:​​

H(opad_key || H(ipad_key || message))

​密钥处理:​​

  1. 首先准备一个密钥 K。
  2. 为适应哈希函数的输入块大小,会对密钥进行处理:
  • 如果 K 的长度等于哈希函数的内部块大小(例如 SHA-256 是 64 字节),则直接使用 K。
  • 如果 K 的长度大于内部块大小,则先将 K 用哈希函数 H 哈希一次,生成一个新的、固定长度的密钥(长度等于哈希函数输出长度),然后用 0 补齐这个新哈希值到内部块大小。
  • 如果 K 的长度小于内部块大小,则在 K 后面填充 0 字节(0x00),直到其长度等于内部块大小。
    处理后的结果称为 K0(填充/截断/哈希后的密钥)。

​​构建内部输入:​​

  • 创建一个常量 ipad(inner padding)。ipad 是一个字节,其值为 0x36(即二进制 00110110),这个值是根据经验选择的,没有特殊数学意义。
  • 将 ipad ​重复到与哈希函数内部块大小相同的长度(例如 SHA-256 就是 64 个 0x36 字节)。这称为 ipad_key。
  • 将处理后的密钥 K0 与 ipad_key 进行异或(XOR)​​ 操作:K0 XOR ipad_key,得到结果 Si。
  • 将待认证的原始消息 M 附加在 Si 之后:Si || M。

​​执行内部哈希:​​

  • 对上一步构建的内部输入 Si || M 应用哈希函数 H:H(Si || M)。得到一个哈希结果 Hi(内部哈希结果)。

​​构建外部输入:​​

  • 创建一个常量 opad(outer padding)。opad 是一个字节,其值为 0x5C(即二进制 01011100),同样是根据经验选择的。
  • 将 opad ​重复到与哈希函数内部块大小相同的长度(例如 SHA-256 就是 64 个 0x5C 字节)。这称为 opad_key。
  • 将处理后的密钥 K0 与 opad_key 进行异或(XOR)​​ 操作:K0 XOR opad_key,得到结果 So。
    将上一步的内部哈希结果 Hi 附加在 So 之后:So || Hi。

​​执行外部哈希:​​

  • 对上一步构建的外部输入 So || Hi 应用哈希函数 H:H(So || Hi)。
  • 最终结果就是 HMAC 值(即 MAC),通常表示为 HMAC(K, M)。​​

神奇的事情

func TestName(t *testing.T) {
msg := []byte{1}
	key := []byte{1}

	// - 直接使用hamc
	hm := hmac.New(sha3.New256, key)
	hm.Write(msg)
	res := hm.Sum(nil)
	fmt.Println(base64.StdEncoding.EncodeToString(res))
	3jqRM4tcGbNTsWxMfYwbU43p/Tlg6kz9Qiq932eG5yA=	

	// - 简单是实现 hamc
	bs := sha3.New256().BlockSize()
	innerHash := sha3.New256()
	ipad := make([]byte, bs)
	copy(ipad, key)
	for i := range ipad {
		ipad[i] ^= 0x36
	}
	innerHash.Write(ipad)
	innerHash.Write(msg)
	innerHashRes := innerHash.Sum(nil)

	opad := make([]byte, bs)
	copy(opad, key)
	for i := range opad {
		opad[i] ^= 0x5c
	}
	outHash := sha3.New256()
	outHash.Write(opad)
	outHash.Write(innerHashRes)
	outHashRes := outHash.Sum(nil)
	fmt.Println(base64.StdEncoding.EncodeToString(outHashRes))
	3jqRM4tcGbNTsWxMfYwbU43p/Tlg6kz9Qiq932eG5yA=
}

Go hmac的实现

package hmac

import (
	"crypto/subtle" // 导入用于常量时间比较的安全库
	"hash"           // 导入哈希接口
)

// hmac 是实现 hash.Hash 接口的结构体
type hmac struct {
	opad, ipad   []byte       // 存储经过填充处理后的密钥
	outer, inner hash.Hash    // 外层和内层的哈希对象
	size         int          // HMAC 结果的长度(字节数)
	blocksize    int          // 哈希函数的块大小(字节数)
}

// New 函数创建并返回一个新的 HMAC 对象
// 参数:
//   h - 哈希函数工厂(如 sha256.New)
//   key - HMAC 使用的密钥
func New(h func() hash.Hash, key []byte) hash.Hash {
	hm := new(hmac)               // 创建 hmac 结构体实例
	hm.blocksize = h().BlockSize() // 获取哈希函数的块大小
	hm.ipad = make([]byte, hm.blocksize) // 初始化内填充缓冲区
	hm.opad = make([]byte, hm.blocksize) // 初始化外填充缓冲区

	// 处理密钥:如果密钥长度大于块大小,先对密钥进行哈希
	if len(key) > hm.blocksize {
		h := h()         // 创建哈希对象
		h.Write(key)     // 写入密钥
		key = h.Sum(nil) // 计算哈希值作为新密钥
	}

	// 将密钥复制到内填充和外填充缓冲区
	// 如果密钥长度小于块大小,剩余部分保持为零(0x00)
	copy(hm.ipad, key)
	copy(hm.opad, key)

	// 使用填充值处理密钥(与 0x36 异或得到内密钥)
	for i := range hm.ipad {
		hm.ipad[i] ^= 0x36 // 内填充常量 0x36
	}

	// 使用填充值处理密钥(与 0x5C 异或得到外密钥)
	for i := range hm.opad {
		hm.opad[i] ^= 0x5c // 外填充常量 0x5C
	}

	// 创建内层哈希对象
	hm.inner = h()
	hm.inner.Write(hm.ipad) // 预先写入内填充密钥(ipad_key)

	// 创建外层哈希对象
	hm.outer = h()
	hm.outer.Write(hm.opad) // 预先写入外填充密钥(opad_key)

	// 设置结果大小
	hm.size = hm.outer.Size()
	return hm
}

// Write 通过 io.Writer 接口向 HMAC 添加数据
// 此方法将数据添加到内层哈希计算
func (h *hmac) Write(p []byte) (n int, err error) {
	return h.inner.Write(p) // 数据被添加到内层哈希中
}

// Sum 方法生成并返回 HMAC 值
// 参数:
//   in - 可选的字节切片,结果会被追加到这个切片之后
func (h *hmac) Sum(in []byte) []byte {
	// 计算内层哈希结果: H(ipad_key || message)
	innerHash := h.inner.Sum(nil)
	
	// 将内层哈希结果添加到外层哈希对象中
	h.outer.Write(innerHash)
	
	// 计算并返回最终结果: H(opad_key || H(ipad_key || message))
	return h.outer.Sum(in)
}

// Reset 重置 HMAC 对象,用于计算新的 HMAC
func (h *hmac) Reset() {
	// 重置内层哈希对象
	h.inner.Reset()
	
	// 重新写入内填充密钥
	h.inner.Write(h.ipad)
	
	// 重置外层哈希对象
	h.outer.Reset()
	
	// 重新写入外填充密钥
	h.outer.Write(h.opad)
}

// Size 返回 HMAC 结果的长度(字节数)
func (h *hmac) Size() int {
	return h.size
}

// BlockSize 返回底层哈希函数的块大小
func (h *hmac) BlockSize() int {
	return h.blocksize
}

// Equal 函数安全地比较两个 HMAC 值是否相等
// 使用恒定时间算法避免时序攻击
func Equal(mac1, mac2 []byte) bool {
	return subtle.ConstantTimeCompare(mac1, mac2) == 1
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值