Go-哈希函数与消息认证详解(含代码)

简介: Go-哈希函数与消息认证详解(含代码)

哈希函数

简介

哈希函数也称散列函数、杂凑函数等,是一种单向密码体制,即它是一个从明文到密文的不可逆映射,即只有“加密”过程,不存在“解密”过程。同时,Hash函数可以将“任意”长度的输入经过变换以后得到固定长度的输出。Hash函数的这种单向特征和输出数据长度固定的特征使得它可以生成消息或数据块的“数据指纹”(也称消息摘要、哈希值或散列值),因此,哈希函数在数据完整性和数字签名等领域有广泛的应用。

使用公式表示为:h=H(m)

  • M:任意长度的消息
  • H:哈希(Hash)函数或杂凑函数或散列函数
  • h:固定长度的哈希值

历史

Hash的概念起源于1956年,Dumey用它来解决symbol table question(符号表问题), 使得数据表的插入、删除、查询操作可以在更短的时间内完成。

散列算法MD族是在上个世纪90年代初由Ron●Rivest设计的,MD代表消息摘要(message-digest), MD2(1989)、MD4(1990)和MD5(1991)都产生一个128位的信息摘要。

SHA系列算法是美国国家标准与技术研究院(NIST)根据Rivest设计的MD4和MD5而开发的算法,国家安全当局发布SHA作为美国政府标准,SHA(Secure Hash Algorithm)表示安全散列算法。

特性

  • 输入:消息是任意有限长度。
  • 输出:哈希值是固定长度。
  • 容易计算:对于任意给定的消息,容易计算其哈希值(正向容易)。
  • 单向性:对于给定的哈希值h,要找到M使得H(M)=h在计算上是不可行的。(逆向不可行)

安全性

抗弱碰撞性:对于给定的消息M,要发现另一个消息M2,满足 H ( M 1 ) = H ( M 2 ) H(M_1)=H(M_2) H(M1 )=H(M2 )在计算上是不可行的。

抗强碰撞性:找任意一对不同的消息M, M2,使 H ( M 1 ) = H ( M 2 ) H(M_1)=H(M_2) H(M1 )=H(M 2)在计算上是不可行的。

随机性:当一个输入位发生变化时,输出位将发生很大变化。(雪崩效应)

MD族

md4

MD4 由Ron Rivest设计 1990年

具有3轮16步,输出位长度为128位。

特点:对任意长度的输入,产生128位输出;其安全性不依赖任何假设,适合高速实现

MD4公布不久,一些密码学家发现,如果去掉MD4算法的第一轮和最后一轮,则算法是不安全的,但他们并没有证明整个算法是不安全的。

md5

MD5 由Ron Rivest设计 1991年 对MD4的改进

具有4轮16步,输出位长128位。

输入分组 512bit

输出 128bit

SHA系列

SHA系列包括多个散列算法标准,其中, SHA-1是数字签名标准中要求使用的算法。

SHA-0:正式地称作SHA,这个版本在发行后不久被指出存在弱点。

SHA-1:NIST于1994年发布的,它与MD4和MD5散列算法非常相似,被认为是MD4和MD5的后继者。(160位)

SHA-2:实际上分为SHA-224、 SHA-256、SHA-384和SHA512算法。

SHA-1

SHA-1接受任何有限长度的输入消息,并产生长度为160比特的Hash值(MD5仅仅生成128位的摘要),因此抗穷举攻击的能力更强。SHA-1设计原理.与MD4相同,它有5个参与运算的32位寄存器字,消息分组和填充方式与MD5相同,主循环也同样是4轮,但每轮进行20次操作,非线性运算、移位和加法运算也与MD5类似,但非线性函数、加法常数和循环左移操作的设计有一些区别。

SHA-2

  • SHA-256:具有64轮单步,输出位长度为256位。
  • SHA-384:实际上与SHA-512相同,除了输出被截断为383位。
  • SHA-512:具有80个单步的轮数和512位的输出位长度。

消息认证

网络系统安全一般要考虑两个方面:

一方面,加密保护传送的信息,使其可以抵抗被动攻击;

另一方面,就是要能防止对手对系统进行主动攻击,如伪造、篡改信息等。认证是对抗主动攻击的主要手段,它对于开放的网络中的各种信息系统的安全性有重要作用。认证分为实体认证和消息认证。


在一个开放通信网络的环境中,信息面临的攻击包括窃听、伪造、修改、插入、删除、否认等。因此,需要提供用来验证消息完整性的一种机制或服务–消息认证。这种服务的主要功能包括:

  • 确保收到的消息确实和发送的一样;
  • 确保消息的来源真实有效;

注:对称密码体制和公钥密码体制都可提供这种服务,但用于消息认证的最常见的密码技术是基于哈希函数的消息认证码

消息认证的目的

验证信息的来源是真实的,而不是冒充的,此为消息源认证。

验证消息的完整性,即验证信息在传送或存储过程中是否被修改。

消息认证。

消息认证码(MAC,Messages Authentication Codes),是与密钥相关的的单向散列函数,也称为消息鉴别码或是消息校验和。此时需要通信双方A和B共享一密钥K。

设A欲发送给B的消息是M,A首先计算 M A C = C k ( M ) MAC=C_k(M) MAC=Ck(M),其中 C k ( ● ) C_k(●) Ck(●)是密钥控制的公开函数(如哈希函数),然后向B发送M||MAC,B收到后做与A相同的计算,求得一新MAC,并与收到的MAC做比较。

HMAC由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一种基于Hash函数和密钥进行消息认证的方法,并于1997年作为RFC2104被公布,并在IPSec和其他网络协议(如SSL)中得以广泛应用,现在已经成为事实上的Internet安全标准。

认证码与检错码

现代密码学中的消息认证码与通信学的消息检错码有密切的联系,并由其演变而来,但其根源和目的是不同的,采用的技术手段有本质的差别

  • 检错码是检测由于通信的缺陷而导致消息发生错误的方法。(客观环境造成的)
  • 认证码是用来检查由于恶意或有目的等方式修改消息的技术。(人为原因造成的)

HMAC的Go实现

crypto/hmac包

func New(h func() hash.Hash, key []byte) hash.Hash

New函数返回一个采用hash.Hash作为底层hash接口、key作为密钥的HMAC算法的hash接口。

func Equal(mac1, mac2 []byte) bool

比较两个MAC是否相同,而不会泄露对比时间信息。(以规避时间侧信道攻击:指通过计算比较时花费的时间的长短来获取密码的信息,用于密码破解)

hash包

io.Writer

通过嵌入的匿名io.Writer接口的Write方法向hash中添加更多数据,永远不返回错误

Sum(b []byte) []byte

crypto/sha1包

func New() hash.Hash

返回一个新的使用SHA1校验的hash.Hash接口。

代码实现

生成MAC

1.使用hmac.New生成hash.Hash(这里使用的sha1,之后的数字签名文章会使用sha2)

2.使用Hash接口的Write添加原消息

3.使用hash.Sum获取MAC

func GenerateMAC(plainText,key []byte) []byte {
  hash := hmac.New(sha1.New,key)
  hash.Write(plainText)
  hashText := hash.Sum(nil)
  return hashText
}

全部代码

package main
import (
  "crypto/hmac"
  "crypto/sha1"
  "fmt"
)
// 生成消息认证码
// plainText 明文
// key 密钥
// 返回 消息认证码
func GenerateMAC(plainText,key []byte) []byte {
  hash := hmac.New(sha1.New,key)
  hash.Write(plainText)
  hashText := hash.Sum(nil)
  return hashText
}
// 消息认证
// plainText 明文
// key 密钥
// hashText 消息认证码
// 返回 是否是原消息
func VerifyMAC(plainText,key,hashText []byte) bool {
  hash := hmac.New(sha1.New,key)
  hash.Write(plainText)
  return hmac.Equal(hashText,hash.Sum(nil))
}
func main()  {
  plainText := []byte("消息")
  key := []byte("私钥")
  hashText := GenerateMAC(plainText,key)
  ok := VerifyMAC(plainText,key,hashText)
  if ok{
    fmt.Printf("%s 是原消息\n",plainText)
  }
  fakeText := []byte("假消息")
  ok = VerifyMAC(plainText,key,fakeText)
  if !ok{
    fmt.Printf("%s 是假消息\n",fakeText)
  }
}

截图

2020062310470442.png已更新到:gocrypto

参考

《现代密码学教程 谷利泽 杨义先等》

Go标准库-crypto/hmac

Go标准库-hash

Go-标准库-crypto/sha1


相关文章
|
1天前
|
Go 索引
掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
|
1天前
|
Go 数据处理
Go杂记1-切片Slice作为函数参数那点事儿
Go杂记1-切片Slice作为函数参数那点事儿
7 0
|
1天前
|
Go 微服务
4. 参考 go 代码——服务注册与发现
4. 参考 go 代码——服务注册与发现
|
1天前
|
存储 Go 开发者
【Go语言专栏】函数在Go语言中的使用与实现
【4月更文挑战第30天】本文介绍了Go语言中函数的使用和实现,包括函数定义、参数传递、返回值、匿名函数、变长参数、函数类型、闭包和错误处理。通过示例展示了如何定义和调用函数,以及如何利用闭包和递归解决问题。此外,还提到了Go函数作为一等公民的特性,允许存储和传递。进一步学习可参考官方文档和相关书籍。
|
1天前
|
Go
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
【4月更文挑战第21天】Go语言函数是代码组织的基本单元,用于封装可重用逻辑。本文介绍了函数定义(包括基本形式、命名、参数列表和多返回值)、调用以及匿名函数与闭包。在函数定义时,注意参数命名和注释,避免参数顺序混淆。在调用时,要检查并处理多返回值中的错误。理解闭包原理,小心处理外部变量引用,以提升代码质量和可维护性。通过实践和示例,能更好地掌握Go语言函数。
31 1
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
|
1天前
|
程序员 Go API
【Go语言快速上手(二)】 分支与循环&函数讲解
【Go语言快速上手(二)】 分支与循环&函数讲解
|
1天前
|
存储 Go 开发者
Golang深入浅出之-Go语言字符串操作:常见函数与面试示例
【4月更文挑战第20天】Go语言字符串是不可变的字节序列,采用UTF-8编码。本文介绍了字符串基础,如拼接(`+`或`fmt.Sprintf()`)、长度与索引、切片、查找与替换(`strings`包)以及转换与修剪。常见问题包括字符串不可变性、UTF-8编码处理、切片与容量以及查找与替换的边界条件。通过理解和实践这些函数及注意事项,能提升Go语言编程能力。
28 0
|
1天前
|
自然语言处理 数据挖掘 程序员
《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)(下)
《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)(上)
27 1
|
1天前
|
数据采集 搜索推荐 Go
《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)(上)
《Go 简易速速上手小册》第2章:控制结构与函数(2024 最新版)
26 1
|
1天前
|
供应链 算法 安全
掌握Go语言:函数精髓,定义、参数、多返回值与应用(14)
掌握Go语言:函数精髓,定义、参数、多返回值与应用(14)

热门文章

最新文章