<Golang>MD5、SHA256等哈希算法介绍、应用场景及具体实现

版权声明:本文为作者原创,如需转载,请注明出处https://blog.csdn.net/weixin_42940826

前言

MD5和SHA256是非常常用的两种单向散列函数,虽然MD5在2005年已经被中国密码学家王小云攻破,但是曾经也是叱咤风云的被大规模使用,现在比较常用的是SHA256算法,两种算法在go语言中已经被封装好,调用方法相同。

称谓: 单向散列函数, 哈希函数, 杂凑函数, 消息摘要函数

输入: 原像

输出: 散列值, 哈希值, 指纹, 摘要


三大特性:

1. 将任意长度的数据转换成固定长度的数据
(无论输入的是一个比特还是一亿个比特,输出的结果长度固定,具体长度根据不同算法决定,比如MD5输出的长度就是128位)
2. 很强的抗碰撞性
(输出的结果唯一,也就是很难找到H(x1)=H(x2))
3. 不可逆
(目前没有任何方法可以通过散列值找到对应的输入值,也就是不能通过H(X)的值找到对应的X)


常见应用场景:

1、数据库中保存用户密码
比如前段时间频频爆出一些公司的数据库被爆,用户的账户密码以明文存在,大量资料流出这样的做法是极其不负责和危险的。
最好的方法就是将用户的密码通过单向散列函数输出到数据库,每次登录时对比散列值即可。由于单向散列函数的不可逆性,就算数据库被盗取,也没有办法得到用户的信息。
(某些自称破解单向散列函数的网站使用的方法其实是低级的穷举法,保存大量常用明文的散列值,这样做是很愚蠢的。有很多方法可以应对,比如一种叫做“加盐”的常用方法,将用户的信息后面统一加上诸如$%*^&这样的字符,然后计算散列值存入数据库中,或者可以计算散列值的散列值,这样可以保证绝对的安全性。)

2、防止文件篡改
目前大部分提供下载服务的网站都有提供文件的SHA256值,这是因为单向散列函数具备防篡改的效果,若是下载的文件的SHA256和网站提供的值不符,则可能此文件已经遭到了修改,可能含有病毒或者是盗版等等。。。接下来的代码中我们也会来实现计算文件的SHA256值。

3、 数字签名
在进行数字签名时也会使用单向散列函数。
数字签名是现实社会中的签名和盖章这样行为在数字世界中的实现。数字签名的处理过程非常耗时,因此一般不会对整个消息内容直接使用数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值使用私钥加密,得到的就是数字签名。

4、伪随机数生成器
使用单向散列函数可以构造伪随机数生成器。
密码技术中所使用的随机数需要具备“事实上不可能根据过去的随机数列预测未来的随机数列”这样的性质。为了保证不可预测性,可以利用单向散列函数的单向性。

5、秒传
很多诸如某云盘,某网盘这样的公司利用单向散列函数的特性实现秒传的效果。
单向散列函数就像文件的指纹一样,当用户上传文件时,首先计算一下此文件的单向散列值,将此值在数据库中进行查找,若存在相同值,证明此用户上传的文件已经存在相同的,所以无需上传,共享即可。如此可大幅降低服务器负载,大幅缩减存储空间,实现去重的效果。


golang实现:

先来看一下golang中提供的有关函数接口

// 第一种方式, 直接调用sum
// 适用于数据量比较小的情况
func Sum(data []byte) [Size]byte

// 第二种方式
// 1. 创建哈希接口对象
func New() hash.Hash
type Hash interface {
    // 通过嵌入的匿名io.Writer接口的Write方法向hash中添加更多数据,永远不返回错误
    io.Writer
    // 返回添加b到当前的hash值后的新切片,不会改变底层的hash状态
    Sum(b []byte) []byte
    // 重设hash为无数据输入的状态
    Reset()
    // 返回Sum会返回的切片的长度
    Size() int
    // 返回hash底层的块大小;Write方法可以接受任何大小的数据,
    // 但提供的数据是块大小的倍数时效率更高
    BlockSize() int
}
type Writer interface {
    Write(p []byte) (n int, err error)
}
// 2. 往创建出的哈希对象中添加数据
hash.Hash.Write([]byte("添加的数据..."))
hash.Hash.Write([]byte("添加的数据..."))
hash.Hash.Write([]byte("添加的数据..."))
hash.Hash.Write([]byte("添加的数据..."))
// 3. 计算结果, md5就是散列值
md5 := hash.Sum(nil);
// 散列值一般是一个二进制的字符串, 有些字符不可见, 需要格式化
// 格式化为16进制的数字串 - 0-9, a-f
func EncodeToString(src []byte) string
// 数据转换完成之后, 长度是原来的2倍

首先先来试一下第一种方法
注意:这种方法只针对数据量较小,一次性使用的情况

package main

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

func main() {
	//定义一个明文,go是世界上最好的语言:)
	plainText:=[]byte("goisthebest:)")
	//由于数据量比较少,可以直接调用
	result:=md5.Sum(plainText)
	//将不可见的二进制字符转化为可见的十六进制,并输出到屏幕上
	fmt.Println(hex.EncodeToString(result[:]))
}
//输出结果:b113781f09fa259f75c9113ca4902d0a

下面我们实现第二种方法,用第二种方法检验文件是否被篡改

package main

import (
	"encoding/hex"
	"fmt"
	"crypto/sha256"
	"os"
)

func main() {
	GetfileHash("文件路径")
}

func GetfileHash(path string)  {
	//创建一个可操作的sha256对象
	hash:=sha256.New()
	//打开所需校验的文件
	fp,_:=os.Open(path)
	defer fp.Close()
	创建一个用来读取的缓存
	buf:=make([]byte,1024)
	//持续读取文件内容
	for  {
		n,_:=fp.Read(buf)
		if n==0 {
			break
		}
		//将每次读取到的数据都添加到hash中去
		hash.Write(buf[:n])
	}
	//最后来一次大汇总
	result:=hash.Sum(nil)
	//转化为十六进制后输出到屏幕
	fmt.Println(hex.EncodeToString(result))
}

//输出:67035c0a45d2db1b6bac545adbb71ee7f907d449f6c84567c9301004d6af2ccb
(位数比MD5得到的位数更多,这也是为什么SHA256更安全的原因之一)


单向散列函数运用场景很多,诸如上文所提几种用法,将在我的博客中详细介绍。

欢迎进入我的博客一起交流学习:)

猜你喜欢

转载自blog.csdn.net/weixin_42940826/article/details/83662174