Hash算法

簡介

Hash(哈隙淳停或散列)算法褂微,是密碼學(xué)中非常重要的一類算法果善,用于將任意長度的二進制串確定性地映射到較短的(通常是固定長度的)二進制串(哈希值)上,不同的二進制輸入串很難映射到相同的輸出嫡锌,這種特性使得其又被稱為指紋(fingerprint)或摘要(digest)算法。
一個優(yōu)秀的 Hash 算法琳钉,應(yīng)該具備以下特點:

  • 正向快速:在給定原文后势木,能夠在有限時間和有限資源內(nèi)計算其Hash值;
  • 逆向困難: 在給定Hash值后歌懒,在有限時間和有限資源內(nèi)無法逆推出原文啦桌;
  • 輸入敏感:原文輸入信息的任何輕微改變,都將造成Hash值的巨大變化及皂;
  • 避免碰撞:很難找到兩段內(nèi)容不同的明文甫男,使得它們的Hash值一致(即發(fā)生碰撞);

常見算法

目前常見的 Hash 算法包括國際上的 Message Digest(MD)系列和 Secure Hash Algorithm(SHA)系列算法验烧,以及國內(nèi)的 SM3 算法板驳。

MD 系列主要包括 MD4 和 MD5 兩個算法。MD4(RFC 1320)是 MIT 的 Ronald L. Rivest 在 1990 年設(shè)計的碍拆,其輸出為 128 位若治。MD4 已證明不夠安全。Rivest在1991年對MD4進行改進發(fā)布MD5感混。MD5 比 MD4 更加安全端幼,但過程更加復(fù)雜,計算速度要慢一點浩习。MD5 已于 2004 年被成功碰撞静暂,其安全性已不足應(yīng)用于商業(yè)場景。

SHA 算法由美國國家標(biāo)準(zhǔn)與技術(shù)院(National Institute of Standards and Technology谱秽,NIST)征集制定洽蛀。首個實現(xiàn) SHA-0 算法于 1993 年問世,1998 年即遭破解疟赊。隨后的修訂版本 SHA-1 算法在 1995 年面世郊供,它的輸出為長度 160 位的 Hash 值,安全性更好近哟。SHA-1 已于 2005 年被成功碰撞驮审,意味著無法滿足商用需求。為了提高安全性,NIST 后來制定出更安全的 SHA-224疯淫、SHA-256地来、SHA-384,和 SHA-512 算法(統(tǒng)稱為 SHA-2 算法)熙掺。新一代的 SHA-3 相關(guān)算法也正在研究中未斑。

中國密碼管理局于 2010 年 12 月 17 日發(fā)布了GM/T 0004-2012 《SM3 密碼雜湊算法》,建立了國內(nèi)商用密碼體系中的公開 Hash 算法標(biāo)準(zhǔn)币绩,已經(jīng)被廣泛應(yīng)用在數(shù)字簽名和認(rèn)證等場景中蜡秽。

應(yīng)用

數(shù)字摘要

利用Hash算法的“抗碰撞性”,計算出數(shù)據(jù)的Hash值作為數(shù)字摘要缆镣。當(dāng)原始數(shù)據(jù)被改變時芽突,可以通過計算摘要進行比對從而檢測出數(shù)據(jù)被篡改。

最常見的用例便是文件下載董瞻,有些網(wǎng)站在提供下載文件的同時寞蚌,會提供文件相應(yīng)的數(shù)字摘要,用戶在下載完成后可以在本地計算文件的摘要力细,通過比對來檢測下載的文件是否被篡改睬澡。

數(shù)字簽名

數(shù)字簽名是對數(shù)據(jù)先使用Hash算法計算其數(shù)字摘要,然后使用加密算法對數(shù)字摘要進行加密眠蚂,最后將數(shù)字簽名附到原始數(shù)據(jù)后完成數(shù)據(jù)的簽名工作煞聪。此時該數(shù)據(jù)可以在不安全的信道中傳輸,接收方可以檢驗數(shù)據(jù)的真實性逝慧。接收方檢驗的流程如下:

  1. 從接收的數(shù)據(jù)中分離出數(shù)據(jù)和簽名
  2. 使用約定好的解密算法對簽名進行解密得到數(shù)字摘要
  3. 使用約定好的Hash算法對數(shù)據(jù)計算數(shù)字摘要
  4. 如果2與3中的數(shù)字摘要一致昔脯,就意味著數(shù)據(jù)是真實有效的、未被篡改過的笛臣。

這種真實性源于下面的兩個假設(shè):

  • 加密算法未被破解
  • Hash算法的抗碰撞性

在加密算法未被破解的前提下云稚,Hash算法的抗碰撞性使得在有限的時間和資源內(nèi)難以找出一份篡改后的數(shù)據(jù)使得與原始數(shù)據(jù)摘要相同。加密算法未被破解沈堡,保證了對于偽造的簽名將解密出不一致的摘要静陈。

數(shù)據(jù)保護

在一些場景下不保存數(shù)據(jù)的原始信息,而保存其Hash值來減輕數(shù)據(jù)庫泄露造成的危害诞丽。例如鲸拥,后臺數(shù)據(jù)庫保存登錄口令的Hash值,每次通過Hash值比對即可判斷輸入口令是否正確僧免,此時即便數(shù)據(jù)庫發(fā)生泄露刑赶,攻擊者也無法知曉用戶的登錄口令。

字典攻擊就是攻擊者掌握了大量常見的登錄密碼懂衩,如password撞叨,123等等金踪,通過對其計算Hash值,編成 口令-Hash 字典牵敷,然后通過Hash值來快速反查找到原始口令胡岔。

對這類攻擊的防范可以使用加鹽(Salt)的方法。保存的不是原文的直接 Hash 值劣领,而是原文再加上一段隨機字符串(即“鹽”)之后的 Hash 值姐军。Hash 結(jié)果和“鹽”分別存放在不同的地方,這樣只要不是兩者同時泄露尖淘,攻擊者很難進行破解。這種思想的高級變種包括二次驗證技術(shù)著觉。

基于內(nèi)容的編址

由于Hash算法的特性村生,可以將任意內(nèi)容映射到一個固定長度的字符串,而且不同內(nèi)容映射到相同串的概率很低饼丘。因此趁桃,這就構(gòu)成了一個很好的“內(nèi)容 -> 索引”的生成關(guān)系。

假設(shè)肄鸽,給定一個數(shù)據(jù)和存儲系統(tǒng)卫病。將這個數(shù)據(jù)放到存儲系統(tǒng)中的什么位置,可以在查詢時快速確定該內(nèi)容是否存在于存儲系統(tǒng)中典徘?通過對內(nèi)容進行Hash計算蟀苛,然后將該哈希值作為在存儲系統(tǒng)中地址,這樣就可以實現(xiàn)高效的查找逮诲。

然而在物理世界中沒有無限長的紙帶帜平,當(dāng)存儲系統(tǒng)越小,Hash值映射的范圍越小梅鹦,Hash碰撞的概率就會越大裆甩。為了提供空間利用率, Burton Howard Bloom提出使用Bloom過濾器進行Hash編址齐唆。其基本思想是:在數(shù)據(jù)上應(yīng)用多個Hash函數(shù)計算多個Hash地址嗤栓,然后將這些Hash地址拼接起來形成最終的地址。這樣箍邮,不同數(shù)據(jù)發(fā)生地址碰撞當(dāng)且僅當(dāng)所有的Hash函數(shù)都碰撞茉帅,因此降低了地址碰撞的幾率。

使用

下面的示例以SHA-256為例媒殉,輸出的均為Hash值的十六進制字符串

bash

# 計算字符串的SHA-256值
$ echo "hello world!" |shasum -a 256
ecf701f727d9e2d77c4aa49ac6fbbcc997278aca010bddeeb961c10cf54d435a -

# 計算一個文件的的SHA-256數(shù)字摘要
$ echo  "hello world!" > hello.txt && shasum -a 256 hello.txt
ecf701f727d9e2d77c4aa49ac6fbbcc997278aca010bddeeb961c10cf54d435a  hello.txt

Go

package main

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

func sha256_file(path string) (string, error) {
    f, err := os.Open(path)
    if err != nil{
        return "", err
    }
    defer f.Close()

    h := sha256.New()
    if _, err = io.Copy(h, f); err != nil{
        return "", err
    }
    byte_hash := h.Sum(nil)
    hex_hash := hex.EncodeToString(byte_hash)
    return hex_hash, nil
}

func main(){
    s := "hello world!"

    // 從 `sha256.New()` 創(chuàng)建一個新的hash對象開始
    h := sha256.New()

    // `Write` 需要字節(jié)輸入担敌。所以要使用 `[]byte(s)` 將字符串 `s` 轉(zhuǎn)成字節(jié)切片
    // 可以調(diào)用多次 `Write` 來寫入數(shù)據(jù)
    h.Write([]byte(s))

    // 獲得當(dāng)前已有數(shù)據(jù)的Hash值
    // `Sum` 接受一個參數(shù)可以將獲得的Hash值附到一個存在的字節(jié)切片后:通常不是必需的
    byte_hash := h.Sum(nil)

    // 以十六進制字符串的格式進行編碼
    hex_hash := hex.EncodeToString(byte_hash)
    fmt.Printf("%s  %s\n", hex_hash, s)

    // 計算文件的SHA256值
    path := os.Args[1]
    file_hash, err := sha256_file(path)
    
    if err != nil {
        fmt.Println("this file not exists")
    } else {
        fmt.Printf("%s  %s\n", file_hash, path)
    }
}

python

# -*- coding: utf-8 -*-
import hashlib
import sys

def sha256_file(path):
    buf_size = 64*1024
    sha256 = hashlib.sha256()
    try:
        with open(path, "rb") as f:
            while True:
                data = f.read(buf_size)
                if not data:
                    break
                sha256.update(data)
            return sha256.hexdigest()
    except:
        return None

if __name__ == "__main__":
    s = "hello world!"
    # 創(chuàng)建SHA256對象
    sha256 = hashlib.sha256()
    # 寫入要計算摘要的數(shù)據(jù)
    sha256.update(s.encode())
    # 計算當(dāng)前數(shù)據(jù)的摘要
    hex_hash = sha256.hexdigest()
    print("{0}  {1}".format(hex_hash, s))

    path = sys.argv[1]
    hex_hash = sha256_file(path)
    if hex_hash == None:
        print("this file not exists")
    else:
        print("{0}  {1}".format(hex_hash, path))

注意

各個編程語言的哈希算法對于相同的二進制數(shù)據(jù)會計算出相同的Hash摘要,但有時會出現(xiàn)意想不到的不一致廷蓉。這有可能是因為沒有考慮編碼的原因全封。例如马昙,python中的字符串在內(nèi)存中默認(rèn)以utf-8編碼,而bash根據(jù)每個人機器的不同配置會有不同的編碼刹悴,這樣表達意思相同的“你好”行楞,在兩者中將以不一致的二進制串出現(xiàn),從而導(dǎo)致對“你好”出現(xiàn)不同的摘要信息土匀。

$ python
> s = "你好"    // s以utf-8標(biāo)準(zhǔn)對“你好”進行編碼

$ echo "你好"    // 根據(jù)機器配置對“你好”進行編碼子房,不一定是utf-8
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市就轧,隨后出現(xiàn)的幾起案子证杭,更是在濱河造成了極大的恐慌,老刑警劉巖妒御,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件解愤,死亡現(xiàn)場離奇詭異,居然都是意外死亡乎莉,警方通過查閱死者的電腦和手機送讲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惋啃,“玉大人哼鬓,你說我怎么就攤上這事”呙穑” “怎么了异希?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長存筏。 經(jīng)常有香客問我宠互,道長,這世上最難降的妖魔是什么椭坚? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任予跌,我火速辦了婚禮,結(jié)果婚禮上善茎,老公的妹妹穿的比我還像新娘券册。我一直安慰自己,他們只是感情好垂涯,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布烁焙。 她就那樣靜靜地躺著,像睡著了一般耕赘。 火紅的嫁衣襯著肌膚如雪骄蝇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天操骡,我揣著相機與錄音九火,去河邊找鬼赚窃。 笑死,一個胖子當(dāng)著我的面吹牛勒极,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虑鼎,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼辱匿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炫彩?” 一聲冷哼從身側(cè)響起匾七,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎江兢,沒想到半個月后乐尊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡划址,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了限府。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夺颤。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胁勺,靈堂內(nèi)的尸體忽然破棺而出世澜,到底是詐尸還是另有隱情,我是刑警寧澤署穗,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布寥裂,位于F島的核電站,受9級特大地震影響案疲,放射性物質(zhì)發(fā)生泄漏封恰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一褐啡、第九天 我趴在偏房一處隱蔽的房頂上張望诺舔。 院中可真熱鬧,春花似錦备畦、人聲如沸低飒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褥赊。三九已至,卻和暖如春莉恼,著一層夾襖步出監(jiān)牢的瞬間拌喉,已是汗流浹背速那。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留司光,地道東北人琅坡。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像残家,于是被迫代替她去往敵國和親榆俺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 散列表,它是基于高速存取的角度設(shè)計的坞淮,也是一種典型的“空間換時間”的做法茴晋。顧名思義,該數(shù)據(jù)結(jié)構(gòu)能夠理解為一個線性表...
    江江JJ閱讀 1,906評論 0 6
  • 散列表,它是基于快速存取的角度設(shè)計的回窘,也是一種典型的“空間換時間”的做法诺擅。顧名思義,該數(shù)據(jù)結(jié)構(gòu)可以理解為一個線性表...
    yeying12321閱讀 3,691評論 0 6
  • 轉(zhuǎn)載:http://blog.csdn.net/tanggao1314/article/details/51457...
    SinX竟然被占用了閱讀 1,682評論 0 0
  • 今天是周末撮执,懶在床上,打手游看視頻刷朋友圈是多數(shù)人的常態(tài)了舷丹。一天過去看手機自動記錄的時間使用統(tǒng)計抒钱,在...
    漂若浮塵閱讀 459評論 0 0
  • 昨天去了國博,兩會期間颜凯,天安門東的安檢長隊已經(jīng)排起來了谋币,排了有50分鐘。 記得一年前是跟大學(xué)同學(xué)一起的症概,見到了許多...
    思索行進閱讀 260評論 0 0