密码学奇妙之旅、03 HMAC单向散列消息认证码、Golang代码

HMAC 单向散列消息认证码消息认证码MAC是用于确认完整性并进行认证的技术,消息认证码的输入包括任意长度的消息和一个发送者和接收者之间共享的密钥(可能还需要共享盐值) 。
HMAC是使用单向散列函数来构造消息认证码的方法,任何高强度单向散列函数都可以被用于HMAC,具体方法如下图所示 。
发送者需要同时把消息和认证码发送给接收者,接收者接收了两者,并根据接收到的消息和共享的密钥生成认证码进行比较 。如果相同则消息未被篡改且认证成功 。
MAC不能保证信息的机密性!MAC无法对第三方"C"证明,因为"A","B"两者都有密钥,都可以生成消息和MAC 。因此第三方不知道是谁生成的,更不知道消息的真实性 。MAC无法防止否认 。

密码学奇妙之旅、03 HMAC单向散列消息认证码、Golang代码

文章插图
加盐盐是通过伪随机数生成器生成的随机数,会和密钥一起被输入单向散列函数 。
主要目的是为了防御字典攻击 。字典攻击是一种事先进行计算并准备好候选密钥列表的方法 。是一种暴力攻击破解手段 。加了盐,密钥便多了n个数量级的可能,加大破解难度 。
HMAC利用单向散列函数的单向性和抗碰撞性来保证无法根据MAC值推测出密钥 。
代码package mainimport ( "crypto/hmac" "crypto/rand" "crypto/sha256" "crypto/sha512" "encoding/base64" "fmt" "hash" "io")var secretKey = "114514abcdefghijklmn"var salt = generateSalt()// 生成一个包含 16 字节数据的盐字符串func generateSalt() string { randomBytes := make([]byte, 16) if _, err := rand.Read(randomBytes); err != nil {return "" } return base64.URLEncoding.EncodeToString(randomBytes)}// 提供散列函数、密钥、盐值、消息返回HMACfunc HMAC(h func() hash.Hash, secretKey string, salt string, message string) []byte { hash := hmac.New(h, []byte(secretKey)) io.WriteString(hash, message+salt) return hash.Sum(nil)}func main() { /* ----------------------------------- 发送方 ---------------------------------- */ message := "A请求B转账10000" fmt.Println("\n\t消息: " + message) fmt.Println("\t加盐: " + salt) fmt.Printf("\n\tHMAC-Sha256: %x", HMAC(sha256.New, secretKey, salt, message)) fmt.Printf("\n\tHMAC-Sha256: %x", HMAC(sha512.New, secretKey, salt, message)) /* ----------------------------------- 修改一个字母 ---------------------------------- */ messageChange := "C请求B转账10000" fmt.Printf("\n\n\tHMAC-Sha256: %x", HMAC(sha256.New, secretKey, salt, messageChange)) fmt.Printf("\n\tHMAC-Sha256: %x\n\n", HMAC(sha512.New, secretKey, salt, messageChange)) /* --------------------------- 接收方分别收到了发送者的消息、HMac值 -------------------------- */ // ^ 假定消息和HMAC均被黑客截获,黑客进行重放攻击 sendMessgage := message// @ 发送者发送消息 sendHMAC := string(HMAC(sha256.New, secretKey, salt, sendMessgage)) // @ 发送者计算HMAC并与消息一起发给接收者 hackerGetHMAC := sendHMAC// @ 黑客窃听到HMAC hackerGetMessage := sendMessgage// @ 黑客窃听到消息 receiveHMAC := hackerGetHMAC// @ 接收者收到HMAC receiveMessage := hackerGetMessage// @ 接收者收到MESSAGE if string(HMAC(sha256.New, secretKey, salt, receiveMessage)) == receiveHMAC {fmt.Println("\t第1次重放攻击" + message) } receiveHMAC = hackerGetHMAC// @ 接收者收到HMAC receiveMessage = hackerGetMessage // @ 接收者收到MESSAGE if string(HMAC(sha256.New, secretKey, salt, receiveMessage)) == receiveHMAC {fmt.Println("\t第2次重放攻击" + message) } // ^ 假定消息和HMAC都是黑客发送的,但黑客并不知道密钥和盐值 sendMessgage = "今天是KFC V我小能喵喵喵50速速"// @ 发送者发送消息 sendHMAC = string(HMAC(sha256.New, "miyueshishenme", "114514", sendMessgage)) // @ 黑客计算HMAC并与消息一起发给接收者 receiveHMAC = sendHMAC// @ 接收者收到HMAC receiveMessage = sendMessgage// @ 接收者收到MESSAGE if string(HMAC(sha256.New, secretKey, salt, receiveMessage)) != receiveHMAC {fmt.Println("\t消息不一致、认证失败") }}

经验总结扩展阅读