Golang與散列算法
目錄
1、哈希函數(shù)的基本特征
2、SHA-1
3、MD5
3.1 基本使用-直接計算
3.2 大量數(shù)據(jù)-散列計算
4、SHA-1 與 MD5 的比較
5、Hmac
6、哈希函數(shù)的應(yīng)用

散列是信息的提煉,通常其長度要比信息小得多,且為一個固定長度。加密性強的散列一定是不可逆的,這就意味著通過散列結(jié)果,無法推出任何部分的原始信息。任何輸入信息的變化,哪怕僅一位,都將導致散列結(jié)果的明顯變化,這稱之為雪崩效應(yīng)。散列還應(yīng)該是防沖突的,即找不出具有相同散列結(jié)果的兩條信息。具有這些特性的散列結(jié)果就可以用于驗證信息是否被修改。常用于保證數(shù)據(jù)完整性
單向散列函數(shù)一般用于產(chǎn)生消息摘要,密鑰加密等,常見的有
MD5(Message Digest Algorithm 5):是 RSA數(shù)據(jù)安全公司開發(fā)的一種單向散列算法SHA(Secure Hash Algorithm):可以對任意長度的數(shù)據(jù)運算生成一個 160位的數(shù)值
1、哈希函數(shù)的基本特征
哈希函數(shù)不是加密算法,其特征為單向性和唯一性
具體如下
輸入可以是任意長度 輸出是固定長度 根據(jù)輸入很容易計算出輸出 根據(jù)輸出很難計算出輸入(幾乎不可能) 兩個不同的輸入幾乎不可能得到相同的輸出
2、SHA-1
https://golang.google.cn/pkg/crypto/sha1/
在1993年,安全散列算法(SHA)由美國國家標準和技術(shù)協(xié)會(NIST)提出,并作為聯(lián)邦信息處理標準(FIPS PUB 180)公布;1995年又發(fā)布了一個修訂版FIPS PUB 180-1,通常稱之為SHA-1。SHA-1是基于MD4算法的,并且它的設(shè)計在很大程度上是模仿MD4的?,F(xiàn)在已成為公認的最安全的散列算法之一,并被廣泛使用
SHA-1是一種數(shù)據(jù)加密算法,該算法的思想是接收一段明文,然后以一種不可逆的方式將它轉(zhuǎn)換成一段(通常更?。┟芪模部梢院唵蔚睦斫鉃槿∫淮斎氪a(稱為預映射或信息),并把它們轉(zhuǎn)化為長度較短、位數(shù)固定的輸出序列即散列值(也稱為信息摘要或信息認證代碼)的過程
該算法輸入報文的最大長度不超過264位,產(chǎn)生的輸出是一個160位的報文摘要。輸入是按512位的分組進行處理的。SHA-1是不可逆的、防沖突,并具有良好的雪崩效應(yīng)
sha1是SHA家族的五個算法之一(其它四個是SHA-224、SHA-256、SHA-384,和SHA-512)
SHA(Secure Hash Algorithm)安全散列算法,是一系列密碼散列函數(shù),有多個不同安全等級的版本:SHA-1,SHA-224,SHA-256,SHA-384,SHA-512
防偽裝,防竄擾,保證信息的合法性和完整性
算法流程:
填充,使得數(shù)據(jù)長度對
512求余的結(jié)果為448在信息摘要后面附加
64bit,表示原始信息摘要的長度初始化
h0到h4,每個h都是32位h0到h4歷經(jīng)80輪復雜的變換把
h0到h4拼接起來,構(gòu)成160位,返回
常用函數(shù)
New:創(chuàng)建 Hash 對象用于計算字節(jié)/字符 sha1值Sum:計算字節(jié)切片 sha1值
package?main
import?(
?"crypto/sha1"
?"fmt"
)
func?main()?{
?data?:=?[]byte("This?page?intentionally?left?blank.")
?fmt.Printf("%x\n",?sha1.Sum(data))
}
sha256、sha512同理
使用示例
package?main
import?(
?"crypto/sha1"
?"fmt"
?"io"
)
//?sha1散列算法
func?sha1Hash(msg?string)?(hashData?[]byte)?{
?h?:=?sha1.New()
?io.WriteString(h,?msg)
?hashData?=?h.Sum(nil)
?return
}
func?main()?{
?msg?:=?"This?is?the?message?to?hash!"
?//?sha1
?sha1Data?:=?sha1Hash(msg)
?fmt.Printf("SHA1:?%x\n",?sha1Data)
}
3、MD5
https://golang.google.cn/pkg/crypto/md5/
MD5即Message-Digest Algorithm 5(信息-摘要算法 5),用于確保信息傳輸完整一致。是計算機廣泛使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言普遍已有MD5實現(xiàn)。將數(shù)據(jù)(如漢字)運算為另一固定長度值,是雜湊算法的基礎(chǔ)原理,MD5的前身有MD2、MD3和MD4
算法流程跟
SHA-1大體相似MD5的輸出是128位,比SHA-1短了32位MD5相對易受密碼分析的攻擊,運算速度比SHA-1快
常用函數(shù)
New:創(chuàng)建
Hash對象用于計算字節(jié)/字符md5值Sum:計算字節(jié)切片
md5值
import?(
?"crypto/md5"
?"fmt"
)
func?main()?{
????//?最基礎(chǔ)的使用方式:?Sum?返回數(shù)據(jù)的MD5校驗和
?fmt.Printf("%x\n",?md5.Sum([]byte("測試數(shù)據(jù)")))
}
3.1 基本使用-直接計算
package?main
import?(
?"crypto/md5"
?"encoding/hex"
?"fmt"
)
func?main()?{
?//?結(jié)果是byte類型的數(shù)組
?bytes?:=?md5.Sum([]byte("i?am?geek"))
?//?轉(zhuǎn)換為32位小寫
?fmt.Printf("%x\n",?bytes)??//?397f77c74db1e25084653531a8046f21
?//?轉(zhuǎn)換為字符串
?x?:=?fmt.Sprintf("%x\n",?bytes)
?fmt.Println(x)??//?397f77c74db1e25084653531a8046f21
?fmt.Println(hex.EncodeToString(bytes[:]))??//?397f77c74db1e25084653531a8046f21
}
3.2 大量數(shù)據(jù)-散列計算
package?main
import?(
?"crypto/md5"
?"fmt"
)
func?main()?{
?//?較大時,分開批量計算
?m?:=?md5.New()
?m.Write([]byte("i?am"))
?m.Write([]byte("?geek"))
?fmt.Printf("%x\n",?m.Sum(nil))??//?397f77c74db1e25084653531a8046f21
}
4、SHA-1 與 MD5 的比較
因為二者均由MD4導出,SHA-1和MD5彼此很相似。相應(yīng)的,他們的強度和其他特性也是相似,但還有以下幾點不同:
對強行供給的安全性:最顯著和最重要的區(qū)別是 SHA-1摘要比MD5摘要長32位。使用強行技術(shù),產(chǎn)生任何一個報文使其摘要等于給定報摘要的難度對MD5是2128數(shù)量級的操作,而對SHA-1則是2160數(shù)量級的操作。這樣,SHA-1對強行攻擊有更大的強度。對密碼分析的安全性:由于 MD5的設(shè)計,易受密碼分析的攻擊,SHA-1顯得不易受這樣的攻擊。速度:在相同的硬件上, SHA-1的運行速度比MD5慢
5、Hmac
https://golang.google.cn/pkg/crypto/hmac/
Hmac算法也是一種哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac還需要一個密鑰, 只要密鑰發(fā)生了變化,那么同樣的輸入數(shù)據(jù)也會得到不同的簽名,因此,可以把Hmac理解為用隨機數(shù)“增強”的哈希算法
常用函數(shù)
New:創(chuàng)建 Hash對象用于計算字節(jié)/字符hmac值Equal:比較 hmac值是否相等
Hs256實現(xiàn)
package?main
import?(
?"crypto/hmac"
?"crypto/sha256"
?"fmt"
?"io"
)
func?main()??{
?key?:=?[]byte("1234567890abcdefg")
?//?創(chuàng)建hmac?hash對象
?hash?:=?hmac.New(sha256.New,?key)
?//?寫入字符串計算散列
?io.WriteString(hash,?"hi,geek")
?//?計算hmac散列
?fmt.Printf("%x\n",?hash.Sum(nil))??//?89fda53d5e71e8c87adb15f8bf11c2c931af019a5c040321e243b82a3bb45ee5
?hash2?:=?hmac.New(sha256.New,?key)
?hash2.Write([]byte("hi,geek"))
?fmt.Println(hmac.Equal(hash2.Sum(nil),?hash.Sum(nil)))??//?true
}
使用示例
package?main
import?(
?"crypto/hmac"
?"fmt"
?"io"
)
//?使用sha1的Hmac散列算法
func?hmacHash(msg?string,?key?string)?(hashData?[]byte)?{
?k?:=?[]byte(key)
?mac?:=?hmac.New(sha1.New,?k)
?io.WriteString(mac,?msg)
?hashData?=?mac.Sum(nil)
?return
}
func?main()?{
?msg?:=?"This?is?the?message?to?hash!"
?//?hmac
?hmacData?:=?hmacHash(msg,?"The?key?string!")
?fmt.Printf("HMAC:?%x\n",?hmacData)
}
6、哈希函數(shù)的應(yīng)用
用戶密碼的存儲
文件上傳/下載完整性校驗
mysql 大字段的快速對比
數(shù)字簽名(區(qū)塊鏈,比特幣)
示例代碼
package?main
import?(
?"crypto/md5"
?"crypto/sha1"
?"encoding/hex"
?"fmt"
)
func?Sha1(data?string)?string?{
?sha1?:=?sha1.New()
?sha1.Write([]byte(data))
?return?hex.EncodeToString(sha1.Sum(nil))
}
func?Md5(data?string)?string?{
?md5?:=?md5.New()
?md5.Write([]byte(data))
?return?hex.EncodeToString(md5.Sum(nil))
}
func?main()?{
?data?:=?"abcdefg"
?fmt.Printf("SHA-1:?%s\n",?Sha1(data))
?fmt.Printf("MD5:?%s\n",?Md5(data))
}
一個實際的例子,用戶名密碼校驗
密碼校驗則是一個很常見的問題, 當我們設(shè)計用戶中心時,是一個必不可少的功能, 為了安全,我們都不會保存用戶的明文密碼, 最好的方式就是保存為Hash, 這樣即使是數(shù)據(jù)泄露了,也不會導致用戶的明文密碼泄露(hash的過程是不可逆的)
示例需求如下
能校驗密碼
用戶可以修改密碼 修改密碼時,禁止使用最近已經(jīng)使用過的密碼
//?NewHashedPassword?生產(chǎn)hash后的密碼對象
func?NewHashedPassword(password?string)?(*Password,?error)?{
?bytes,?err?:=?bcrypt.GenerateFromPassword([]byte(password),?10)
?if?err?!=?nil?{
??return?nil,?err
?}
?return?&Password{
??Password:?string(bytes),
??CreateAt:?ftime.Now().Timestamp(),
??UpdateAt:?ftime.Now().Timestamp(),
?},?nil
}
type?Password?struct?{
?//?hash過后的密碼
?Password?string
?//?密碼創(chuàng)建時間
?CreateAt?int64
?//?密碼更新時間
?UpdateAt?int64
?//?密碼需要被重置
?NeedReset?bool
?//?需要重置的原因
?ResetReason?string
?//?歷史密碼
?History?[]string
?//?是否過期
?IsExpired?bool
}
//?Update?更新密碼
func?(p?*Password)?Update(new?*Password,?maxHistory?uint,?needReset?bool)?{
?p.rotaryHistory(maxHistory)
?p.Password?=?new.Password
?p.NeedReset?=?needReset
?p.UpdateAt?=?ftime.Now().Timestamp()
?if?!needReset?{
??p.ResetReason?=?""
?}
}
//?IsHistory?檢測是否是歷史密碼
func?(p?*Password)?IsHistory(password?string)?bool?{
?for?_,?pass?:=?range?p.History?{
??err?:=?bcrypt.CompareHashAndPassword([]byte(pass),?[]byte(password))
??if?err?==?nil?{
???return?true
??}
?}
?return?false
}
//?HistoryCount?保存了幾個歷史密碼
func?(p?*Password)?HistoryCount()?int?{
?return?len(p.History)
}
func?(p?*Password)?rotaryHistory(maxHistory?uint)?{
?if?uint(p.HistoryCount())???p.History?=?append(p.History,?p.Password)
?}?else?{
??remainHistry?:=?p.History[:maxHistory]
??p.History?=?[]string{p.Password}
??p.History?=?append(p.History,?remainHistry...)
?}
}
//?CheckPassword?判斷password?是否正確
func?(p?*Password)?CheckPassword(password?string)?error?{
?err?:=?bcrypt.CompareHashAndPassword([]byte(p.Password),?[]byte(password))
?if?err?!=?nil?{
??return?exception.NewUnauthorized("user?or?password?not?connrect")
?}
?return?nil
}
See you ~
歡迎進群一起進行技術(shù)交流
加群方式:公眾號消息私信“加群”或加我好友再加群均可
