1. 程式人生 > ><Golang>MD5、SHA256等雜湊演算法介紹、應用場景及具體實現

<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更安全的原因之一)


單向雜湊函式運用場景很多,諸如上文所提幾種用法,將在我的部落格中詳細介紹。

歡迎進入我的部落格一起交流學習:)