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


    HMAC 单向散列消息认证码

    消息认证码MAC是用于确认完整性并进行认证的技术,消息认证码的输入包括任意长度的消息和一个发送者和接收者之间共享的密钥(可能还需要共享盐值)。

    HMAC是使用单向散列函数来构造消息认证码的方法,任何高强度单向散列函数都可以被用于HMAC,具体方法如下图所示。

    发送者需要同时把消息和认证码发送给接收者,接收者接收了两者,并根据接收到的消息和共享的密钥生成认证码进行比较。如果相同则消息未被篡改且认证成功。

    MAC不能保证信息的机密性!MAC无法对第三方"C"证明,因为"A","B"两者都有密钥,都可以生成消息和MAC。因此第三方不知道是谁生成的,更不知道消息的真实性。MAC无法防止否认。

    image-20220927223400754


    加盐

    盐是通过伪随机数生成器生成的随机数,会和密钥一起被输入单向散列函数。

    主要目的是为了防御字典攻击。字典攻击是一种事先进行计算并准备好候选密钥列表的方法。是一种暴力攻击破解手段。加了盐,密钥便多了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转账100002次重放攻击A请求B转账10000
        消息不一致、认证失败
    

    如何防止重放攻击

    • 给消息加序号,收发双方约定好
    • 时间戳,收发双方必须考虑到通信延迟,还是会存在重放攻击的空间。
    • 接收者发送一次性随机数,发送者HMAC该随机数后发送。确保当前消息只能发一次。

    __EOF__

  • 本文作者: 小能正在往前冲
  • 本文链接: https://www.cnblogs.com/linxiaoxu/p/16736463.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    Vue中如何进行表单验证码与滑动验证
    观察者模式:包工头的救赎
    微信小程序实战,基于vue2实现瀑布流
    Lint-staged自动修复格式错误及小结
    games101 作业2
    猿创征文|C# DataSet类的简单学习
    【实用技巧】Latex写算法伪代码(格式篇)
    React 全栈体系(十九)
    RunnerGo 支持UI自动化的测试平台
    ModStartBlog 现代化个人博客系统 v5.2.0 主题开发增强,新增联系方式
  • 原文地址:https://www.cnblogs.com/linxiaoxu/p/16736463.html