<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更安全的原因之一)
單向雜湊函式運用場景很多,諸如上文所提幾種用法,將在我的部落格中詳細介紹。