這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Golang計算MD5
1.單向加密
什麼是單向密碼編譯演算法。簡而言之就是不可解密的加密方法,也叫作非可逆加密。這些學術 解釋都不用管它,反正就是一群數學家老爺爺通過各種證明得到的一種方法,用這種方法加密 過的東西,地球上現有的人類在有限的時間內是無法解密的,包括加密者自己。既然加密者自己 都無法解密,那這乖僻的密碼編譯演算法有個毛用阿?哪些情境需要這種有去無回的加密方式呢?
這裡先瞭解下單向加密的特徵: 首先,不可解密性在上面已經提到了,目的就是掩藏明文。其次單向密碼編譯演算法的另外兩個特徵 使得它更接地氣,第一是任意兩段明文資料,加密以後的密文不能是相同的; 第二是任意一段明文資料,經過加密以後,其結果必須永遠是不變的。這樣當我們需要表徵一段 唯一的內容,但是有不想別人知道該段內容時或者僅僅是為一個內容打上一個唯一的ID時,就可以 嘗試下單向密碼編譯演算法。
比如,如果想對一段內容做快取作業,為了更快的找到其內容,使用K-V的儲存方式。那麼該如何 構建Key呢?當然有很多方法,比如時間。通過上面“"不同明文加密結果不同"特徵,我們可以 用對該內容作單向加密,然後用加密結果做為key。又比如在使用者登陸的情境中,害怕別人通過劫 包的方式擷取使用者秘密,我們可以將該秘密進行單向加密存放到資料庫中,之後的驗證都對使用者 輸入的秘密進行單向加密,確保其在傳輸過程中“"不可見"。
那麼常用的單向密碼編譯演算法有哪些呢? 主要有:BASE64、MD5、SHA、HMAC,其中最為常見的就有MD5,BASE64。這裡我們主要介紹最常 使用的MD5演算法。該演算法可以用來得到一個128bit的值,既可以作為K-V中的key,也可以對密碼 進行加密從而確保其在傳輸中"不可見"。
2.經典的MD5API
在C系列中計算MD5的API一般有如下幾個API構成。
- init :獲得一個md5上下文
- append/update : 添加明文,可以多次添加
- final: 執行計算操作
- digest/hex_digest: 獲得加密結果,結果為128bit記憶體值或者其32個16進位標記法。
Python的標準庫中也是提供了這幾個API,除此之外還提供了一個直接從檔案中獲得明文計算MD5的 方法
3.Golang的"crypto/md5"
Golang的加密庫都放在crypto目錄下,其中MD5庫在crypto/md5包中,該包主要提供了New和Sum函數。 乍一看和以往的使用不太一樣。但是看完例子後發現,其宗旨還是一樣的,都是先建立一個MD5對象, 然後更新明文,接著加密輸出,最後打掃戰場。
先看個樣本:
package mainimport ( "crypto/md5" "fmt" "encoding/hex")func main(){ md5Ctx := md5.New() md5Ctx.Write([]byte("test md5 encrypto")) cipherStr := md5Ctx.Sum(nil) fmt.Print(cipherStr) fmt.Print("\n") fmt.Print(hex.EncodeToString(cipherStr))}
這裡直接對一串字串計算MD5。其中通過md5.New()初始化一個MD5對象,其實它是一個hash.Hash對象。 函數原型為 func New() hash.Hash
。該對象實現了hash.Hash的Sum介面:計算出校正和。其函數原型 為 func Sum(data []byte) [Size]byte
這裡的官方Manual對其的描述我感覺有點問題。其官方描述為: " Sum returns the MD5 checksum of the data. "
通過翻閱源碼可以看到他並不是對data進行校正計算,而是對hash.Hash對象內部儲存的內容進行校正和 計算然後將其追加到data的後面形成一個新的byte切片。因此通常的使用方法就是將data置為nil
該方法返回一個Size大小的byte數組,對於MD5來說就是一個128bit的16位元組byte數組。這裡通過 []byte() 將一個字串轉換成byte數組,還可以通過byte的其他介面進行轉換,Write方法在下面進行 說明。 hash.Hash對象內的儲存內容進行校正和計算。那這個儲存內容是放在哪呢?通過Golang標準庫的代碼 md5.go,可以找到
type digest struct { s [4]uint32 x [chunk]byte nx int len uint64}
這樣的類型。其內容就存在64位元組的byte數組中。
4.明文介面
hash.Hash繼承了io.Write。因此可以將其當成一個輸入資料流進行內容的更新。上面已經看到了Write方法, 這裡對其進行詳細介紹。該方法是從io.Write繼承而來,確切的說是繼承了一個介面,然後去實現它。 函數原型為: Write(p []byte) (n int, err error)
,將p中的內容讀入並進行計算後存入到上面 digest的byte記憶體x中去。最後通過內建函式checkSum計算出其校正和。
因此我們可以將Write方法看成是C系中的Update方法。Sum看成是Final方法。從上我們看到其結構為 128bit的記憶體標記法。那如何得到其32位元組的16進位標記法呢?這裡我們可以使用encoding/hex包中的 EncodeToString方法對其進行編碼.函數原型為 func EncodeToString(src []byte) string
直接 將Sum的返回結果給到src即可。這樣我們就可以進行簡單的對應來快速熟悉Golang的MD5介面操作了:
init() -- vs -- md5.New()update() -- vs -- md5Ctx.Write()final() -- vs -- md5Ctx.Sum(nil)hex_digest() -- vs -- hex.EncodeToString()
有了這幾個介面,就可以很方便的自己封裝一個對檔案計算md5值的介面了。