五、单向散列函数

  • 单向散列函数 — 获取消息的指纹
  • 当需要比较两条消息是否一致时,我们不必直接对比消息本身的内容,只要对比它们的“指纹”就可以了。
  • 单向散列函数(one-wayftnction)有一个输人和一个输出,其中输人称为消息(message),输出称为散列值(hashvalue)。单向散列函数可以根据消息的内容计算出散列值,而散列值就可以被用来检查消息的完整性。
  • 这里的消息不一定是人类能够读懂的文字,也可以是图像文件或者声音文件。单向散列函数不需要知道消息实际代表的含义。无论任何消息,单向散列函数都会将它作为单纯的比特序列来处理,即根据比特序列计算出散列值。
  • 散列值的长度和消息的长度无关。无论消息是1比特,还是100MB,甚至是1OOGB,单向散列函数都会计算出固定长度的散列值。以SHA-I单向散列函数为例,它所计算出的散列值的长度永远是160比特(20字节)。
  • 单向散列函数也称为消息摘要函数(message digest function)、哈希函数或者杂凑函数
  • 输人单向散列函数的消息也称为原像(pre-image)。
  • 单向散列函数输出的散列值也称为消息摘要(message digest)或者指纹(fingerprint)。

1.单向散列函数的性质

  • 通过使用单向散列函数,即便是确认几百MB大小的文件的完整性,也只要对比很短的散列值就可以了。
  • 根据任意长度的消息计算出固定长度的散列值
    • 首先,单向散列函数的输人必须能够是任意长度的消息。
    • 其次,无论输人多长的消息,单向散列函数必须都能够生成长度很短的散列值,如果消息越长生成的散列值也越长的话就不好用了。从使用方便的角度来看,散列值的长度最好是短且固定的。
  • 能够快速计算出散列值
    • 计算散列值所花费的时间必须要短。尽管消息越长,计算散列值的时间也会越长,但如果不能在现实的时间内完成计算就没有意义了。
  • 消息不同散列值也不同
    • 为了能够确认完整性,消息中哪怕只有1比特的改变,也必须有很高的概率产生不同的散列值。
    • 如果单向散列函数计算出的散列值没有发生变化,那么消息很容易就会被篡改,这个单向散列函数也就无法被用于完整性的检查。两个不同的消息产生同一个散列值的情况称为碰撞(collision)
    • 难以发现碰撞的性质称为抗碰撞性(collisionresistance)。密码技术中所使用的单向散列函数,都需要具备抗碰撞性。
  • 具备单向性
    • 单向性指的是无法通过散列值反算出消息的性质。根据消息计算散列值可以很容易,但这条单行路是无法反过来走的。
    • 尽管单向散列函数所产生的散列值是和原来的消息完全不同的比特序列,但是单向散列函数并不是一种加密,因此无法通过解密将散列值还原为原来的消息

2.单向散列函数的实际应用

2.1 检测软件是否被篡改
  • 很多软件,尤其是安全相关的软件都会把通过单向散列函数计算出的散列值公布在自己的官方网站上。用户在下载到软件之后,可以自行计算散列值,然后与官方网站上公布的散列值进行对比。通过散列值,用户可以确认自己所下载到的文件与软件作者所提供的文件是否一致。
2.2 消息认证码
  • 消息认证码是将“发送者和接收者之间的共享密钥”和“消息”,进行混合后计算出的散列值。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装。
2.3 数字签名
  • 数字签名是现实社会中的签名(sign)和盖章这样的行为在数字世界中的实现。数字签名的处理过程非常耗时,因此一般不会对整个消息内容直接施加数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值施加数字签名。
2.4 伪随机数生成器
  • 密码技术中所使用的随机数需要具备“事实上不可能根据过去的随机数列预测未来的随机数列”这样的性质。为了保证不可预测性,可以利用单向散列函数的单向性。
2.5 一次性口令
  • 一次性口令经常被用于服务器对客户端的合法性认证。在这种方式中,通过使用单向散列函数可以保证口令只在通信链路上传送一次(one-time),因此即使窃听者窃取了口令,也无法使用。

3.常用的单向散列函数

3.1 MD4、MD5
  • MD4是由Rivest于1990年设计的单向散列函数,能够产生128比特的散列值(RFC1186,修订版RFC1320)。不过,随着Dobbertin提出寻找MD4散列碰撞的方法,因此现在它已经不安全了。
  • MD5是由Rwest于1991年设计的单项散列函数,能够产生128比特的散列值(RFC1321)。
  • MD5的强抗碰撞性已经被攻破,也就是说,现在已经能够产生具备相同散列值的两条不同的消息,因此它也已经不安全了。
  • MD4和MD5中的MD是消息摘要(Message Digest)的缩写。
3.2 Go中使用MD5
package main

import (
	"crypto/md5"
	"fmt"
	"encoding/hex"
)

//计算Md5的方式1
func getMD5_1(str []byte) string {
	// 1. 计算数据的md5
	result := md5.Sum(str)
	fmt.Println(result)
	fmt.Printf("%x\n", result)
	// 2. 数据格式化为16进制格式字符串
	res := fmt.Sprintf("%x", result)
	fmt.Println(res)
	// --- 这是另外一种格式化切片的方式
	res = hex.EncodeToString(result[:])
	fmt.Println("res: ", res)
	return res
}

//计算Md5的方式2
func getMD5_2(str []byte) string {
	// 1. 创建一个使用MD5校验的Hash对象`
	myHash := md5.New()
	// 2. 通过io操作将数据写入hash对象中
	//io.WriteString(myHash, string(str))
	myHash.Write(str)
	// 3. 计算结果
	result := myHash.Sum(nil)
	fmt.Println(result)
	// 4. 将结果转换为16进制格式字符串
	res := fmt.Sprintf("%x", result)
	fmt.Println(res)
	// --- 这是另外一种格式化切片的方式
	res = hex.EncodeToString(result)
	fmt.Println(res)

	return res
}

func main() {
	str := "事后诸葛亮"
	getMD5_1([]byte(str))
	fmt.Println("--------------------------")
	getMD5_2([]byte(str))
}
  • 输出结果:
[69 31 184 192 42 4 128 175 88 70 8 56 1 140 151 62]
451fb8c02a0480af58460838018c973e
451fb8c02a0480af58460838018c973e
res:  451fb8c02a0480af58460838018c973e
--------------------------
[69 31 184 192 42 4 128 175 88 70 8 56 1 140 151 62]
451fb8c02a0480af58460838018c973e
451fb8c02a0480af58460838018c973e
3.3 SHA-1、SHA-224、SHA-256、SHA-384、SHA-512
  • SHA-1是由NIST(NationalInstituteOfStandardsandTechnology,美国国家标准技术研究所)设计的一种能够产生160比特的散列值的单向散列函数。1993年被作为美国联邦信息处理标准规格(FIPS PUB 180)发布的是SHA,1995年发布的修订版FIPS PUB 180-1称为SHA-1。
  • SHA-1的消息长度存在上限,但这个值接近于264比特,是个非常巨大的数值,因此在实际应用中没有问题。
  • SHA-224、SHA-256、SHA-384和SHA-512都是由NIST设计的单向散列函数,它们的散列值长度分别为224比特、256比特、384比特和512比特。这些单向散列函数合起来统称SHA-2,它们的消息长度也存在上限(SHA-224 和 SHA-256的上限接近于 264 比特,SHA-384 和 SHA-512的上限接近于 2128 比特)。SHA-1 的强抗碰撞性已于2005年被攻破, 也就是说,现在已经能够产生具备相同散列值的两条不同的消息。不过,SHA-2还尚未被攻破。
hash函数比特数字节数
MD4128bit16byte
MD5128bit16byte
SHA-1160bit20byte
SHA-224224bit28byte
SHA-256256bit32byte
SHA-384384bit48byte
SHA-512512bit64byte
3.4 Go中对SHA-1、SHA-2的使用
package main

import (
	"crypto/sha1"
	"encoding/hex"
	"crypto/sha256"
	"crypto/sha512"
	"fmt"
)

func Sha(typ int, str []byte) string {
	myHash := sha1.New()
	if typ == 256 {
		myHash = sha256.New()
	}
	if typ == 512 {
		myHash = sha512.New()
	}

	myHash.Write(str)
	tmp1 := myHash.Sum(nil)
	result := hex.EncodeToString(tmp1)
	return result
}

func main() {
	b := []byte("事后诸葛亮")
	fmt.Println("sha1: ", Sha(0, b))
	fmt.Println("sha256: ", Sha(256, b))
	fmt.Println("sha512: ", Sha(512, b))
}
  • 输出结果
sha1:  542ddfd33bbfbcb0e3627bf9f50116c1593e7d41
sha256:  920453953d64d84a199b38591a89b65f630acbdd7efe9adf34f5603185bc72a3
sha512:  af654c7a047ab41bad6f17cb77f3a4cb770017ada7634f04c7444a512172bf53587716677e0c9247189fc2e4b7193784e919c52283cebdb2f7dcf22c055ed8c9
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值