探索密碼學的奇妙之旅。介紹HMAC單向散列消息認證碼、加鹽。使用golang crypto包實現HMAC生成與驗證,重放攻擊演示。 ...
HMAC 單向散列消息認證碼
消息認證碼MAC是用於確認完整性併進行認證的技術,消息認證碼的輸入包括任意長度的消息和一個發送者和接收者之間共用的密鑰(可能還需要共用鹽值)。
HMAC是使用單向散列函數來構造消息認證碼的方法,任何高強度單向散列函數都可以被用於HMAC,具體方法如下圖所示。
發送者需要同時把消息和認證碼發送給接收者,接收者接收了兩者,並根據接收到的消息和共用的密鑰生成認證碼進行比較。如果相同則消息未被篡改且認證成功。
MAC不能保證信息的機密性!MAC無法對第三方"C"證明,因為"A","B"兩者都有密鑰,都可以生成消息和MAC。因此第三方不知道是誰生成的,更不知道消息的真實性。MAC無法防止否認。
加鹽
鹽是通過偽隨機數生成器生成的隨機數,會和密鑰一起被輸入單向散列函數。
主要目的是為了防禦字典攻擊。字典攻擊是一種事先進行計算並準備好候選密鑰列表的方法。是一種暴力攻擊破解手段。加了鹽,密鑰便多了n個數量級的可能,加大破解難度。
HMAC利用單向散列函數的單向性和抗碰撞性來保證無法根據MAC值推測出密鑰。
代碼
package main
import (
"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)
}
// 提供散列函數、密鑰、鹽值、消息返回HMAC
func 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消息不一致、認證失敗")
}
}
PS C:\Users\小能喵喵喵\Desktop\Go\Cryptography\HMAC> go run .
消息: A請求B轉賬10000
加鹽: S_XlM8K_dhAvsgch_N3o1w==
HMAC-Sha256: b8dd30d2a418262494f298bcdaf6c12f442c6e8f89a31822dad03561887f3bed
HMAC-Sha256: ba934567837ec98ba89853b09f6652ce56955cfeedd0c4495bd6cba7fc2f8293635fdc59b90180564bd0fdb1d1bffc52644fc2bd8164d6379ae11510e200954c
HMAC-Sha256: 2db84d209e2418f314fc5bb0583cfb50cde90d954d8493d3ed0e3b369fb092d7
HMAC-Sha256: 7b192cedb1d89fd71889189a0094e1df06d26d977bc3bed4f53b16928aa2d58084cb8890d52cb40f665bb9ac62eeb4092495efe7d59292470ed597a3536dea56
第1次重放攻擊A請求B轉賬10000
第2次重放攻擊A請求B轉賬10000
消息不一致、認證失敗
如何防止重放攻擊
- 給消息加序號,收發雙方約定好
- 時間戳,收發雙方必須考慮到通信延遲,還是會存在重放攻擊的空間。
- 接收者發送一次性隨機數,發送者HMAC該隨機數後發送。確保當前消息只能發一次。