1. 單向散列函數(shù)的含義
單向散列函數(shù)(one-wayfunction)有一個(gè)輸入和一個(gè)輸出,其中輸入稱為消息(message),輸出稱為散列值 (hashvalue)。單向散列函數(shù)可以根據(jù)消息的內(nèi)容計(jì)算出散列值,而散列值就可以被用來(lái)檢查消息的完整性。
這里的消息不一定是人類能夠讀懂的文字,也可以是圖像文件或者聲音文件。單向散列函數(shù)不需要知道消息實(shí)
際代表的含義。無(wú)論任何消息,單向散列函數(shù)都會(huì)將它作為單純的比特序列來(lái)處理,即根據(jù)比特序列計(jì)算出散
列值。
散列值的長(zhǎng)度和消息的長(zhǎng)度無(wú)關(guān)。無(wú)論消息是1比特,還是100MB,甚至是IOOGB,單向散列函數(shù)都會(huì)計(jì)算出固 定長(zhǎng)度的散列值。以SHA-I單向散列函數(shù)為例,它所計(jì)算出的散列值的長(zhǎng)度永遠(yuǎn)是160比特(20字節(jié))。
單向散列函數(shù)的相關(guān)術(shù)語(yǔ)有很多變體,不同參考資料中所使用的術(shù)語(yǔ)也不同,下面我們就介紹其中的兒個(gè)。 單向散列函數(shù)也稱為消息摘要函數(shù)(message digest function)、哈希函數(shù)或者雜湊函數(shù)。 輸入單向散列函數(shù)的消息也稱為 原像 (pre-image)。
單向散列函數(shù)輸出的散列值也稱為消息摘要(message digest)或者指紋(fingerprint)。 完整性 也稱為一致性。
2. 單向散列函數(shù)的性質(zhì)
- 根據(jù)任意長(zhǎng)度的消息計(jì)算出固定長(zhǎng)度的散列值
- 能夠快速計(jì)算出散列值
- 消息不同散列值也不同
- 難以發(fā)現(xiàn)碰撞,稱為抗碰撞性
- 備單向性,無(wú)法通過散列值反算出消息
3. 單向散列函數(shù)的實(shí)際應(yīng)用
檢測(cè)軟件是否被篡改
消息認(rèn)證碼
消息認(rèn)證碼是將“發(fā)送者和接收者之間的共享密鑰”和“消息,進(jìn)行混合后計(jì)算出的散列值。使用消息認(rèn)證碼可以 檢測(cè)并防止通信過程中的錯(cuò)誤、篡改以及偽裝。
消息認(rèn)證碼在SSL/TLS中也得到了運(yùn)用。數(shù)字簽名
數(shù)字簽名是現(xiàn)實(shí)社會(huì)中的簽名(sign)和蓋章這樣的行為在數(shù)字世界中的實(shí)現(xiàn)。數(shù)字簽名的處理過程非常耗時(shí), 因此一般不會(huì)對(duì)整個(gè)消息內(nèi)容直接施加數(shù)字簽名,而是先通過單向散列函數(shù)計(jì)算出消息的散列值,然后再對(duì)這 個(gè)散列值施加數(shù)字簽名。偽隨機(jī)數(shù)生成器
密碼技術(shù)中所使用的隨機(jī)數(shù)需要具備“事實(shí)上不可能根據(jù)過去的隨機(jī)數(shù)列預(yù)測(cè)未來(lái)的隨機(jī)數(shù)列”這樣的性質(zhì)。為 了保證不可預(yù)測(cè)性,可以利用單向散列函數(shù)的單向性。一次性口令
一次性口令經(jīng)常被用于服務(wù)器對(duì)客戶端的合法性認(rèn)證。在這種方式中,通過使用單向散列函數(shù)可以保證口令只 在通信鏈路上傳送一次(one-time),因此即使竊聽者竊取了口令,也無(wú)法使用。
4. 常用的單向散列函數(shù)
4.1. MD4、MD5
MD4是由Rivest于1990年設(shè)計(jì)的單向散列函數(shù),能夠產(chǎn)生128比特的散列值(RFC1186,修訂版RFC1320)。不 過,隨著Dobbertin提出尋找MD4散列碰撞的方法,因此現(xiàn)在它已經(jīng)不安全了。
MD5是由Rwest于1991年設(shè)計(jì)的單項(xiàng)散列函數(shù),能夠產(chǎn)生128比特的散列值(RFC1321)。
MD5的強(qiáng)抗碰撞性已經(jīng)被攻破,也就是說(shuō),現(xiàn)在已經(jīng)能夠產(chǎn)生具備相同散列值的兩條不同的消息,因此它也已
經(jīng)不安全了。
MD4和MD5中的MD是消息摘要(Message Digest)的縮寫。
4.1.1 Go中使用MD5
func GetMD5ToHexStr(src []byte) string {
result := md5.Sum(src)
return hex.EncodeToString(result[:])
}
4.2. SHA-1、SHA-224、SHA-256、SHA-384、SHA-512
SHA-1是由NIST(NationalInstituteOfStandardsandTechnology,美國(guó)國(guó)家標(biāo)準(zhǔn)技術(shù)研究所)設(shè)計(jì)的一種能夠產(chǎn)生 160比特的散列值的單向散列函數(shù)。1993年被作為美國(guó)聯(lián)邦信息處理標(biāo)準(zhǔn)規(guī)格(FIPS PUB 180)發(fā)布的是 SHA,1995年發(fā)布的修訂版FIPS PUB 180-1稱為SHA-1。
SHA-1的消息長(zhǎng)度存在上限,但這個(gè)值接近于2^64比特,是個(gè)非常巨大的數(shù)值,因此在實(shí)際應(yīng)用中沒有問題。
SHA-256、SHA-384和SHA-512都是由NIST設(shè)計(jì)的單向散列函數(shù),它們的散列值長(zhǎng)度分別為256比特、384比特和
512比特。這些單向散列函數(shù)合起來(lái)統(tǒng)稱SHA-2,它們的消息長(zhǎng)度也存在上限(SHA-256的上限接近于 2^64 比特,
SHA-384 和 SHA-512的上限接近于 2^128 比特)。這些單向散列函數(shù)是于2002年和 SHA-1 一起作為 FIPS PUB 180-2 發(fā)布的 SHA-1 的強(qiáng)抗碰撞性已于2005年被攻破, 也就是說(shuō),現(xiàn)在已經(jīng)能夠產(chǎn)生具備相同散列值的兩條不同的消 息。不過,SHA-2還尚未被攻破。
4.2.1 Go中對(duì)SHA-1、SHA-2的使用
func GetHashFromBytes(src []byte, kHashType KHashType) (string, error) {
result := []byte("")
switch kHashType {
case KHashTypeMd5:
temp := md5.Sum(src)
result = temp[:]
case KHashTypeSha1:
temp := sha1.Sum(src)
result = temp[:]
case KHashTypeSha256:
temp := sha256.Sum256(src)
result = temp[:]
case KHashTypeSha512:
temp := sha512.Sum512(src)
result = temp[:]
}
if result == nil{
return "", errors.New("ERROR:WRONG kHashType")
}
return hex.EncodeToString(result),nil
}
func GetHashFromFile(filename string, kHashType KHashType) (string, error) {
// 打開文件
file, err := os.Open(filename)
if err != nil {
return "", err
}
defer file.Close()
hash := func() hash.Hash {
switch kHashType {
case KHashTypeMd5:
return md5.New()
case KHashTypeSha1:
return sha1.New()
case KHashTypeSha256:
return sha256.New()
case KHashTypeSha512:
return sha512.New()
default:
return nil
}
}()
if hash == nil {
return "", errors.New("ERROR:WRONG kHashType")
}
// 將文件數(shù)據(jù)拷貝給哈希對(duì)象
num, err := io.Copy(hash, file)
if err != nil {
return "", err
}
fmt.Println(num)
return hex.EncodeToString(hash.Sum(nil)), nil
}