1. 程式人生 > >MD5加密演算法Golang實現

MD5加密演算法Golang實現

什麼是MD5?

MD5訊息摘要演算法(Message-Digest Algorithm),一種被廣泛使用的密碼雜湊函式,可以產生出一個128位(16位元組)的雜湊值,用於確保資訊傳輸完整一致。MD5有MD4、MD3、MD2改進而來,主要增強演算法複雜度和不可逆性。MD5廣泛使用在為檔案傳輸提供一定的可靠性方面。例如:伺服器預先提供一個MD5校驗和,使用者下載完檔案之後,用MD5演算法計算下載檔案的MD5校驗和,然後通過檢查這兩個檢驗和是否一致,就能判斷下載的檔案是否出錯。

MD5演算法介紹

MD5演算法處理不定長的輸入資訊,得到定長的128位輸出。MD5將輸入資訊以512位進行分組,且每一分組又被劃分為16個32位子分組,經過一系列處理之後演算法的輸出得到4個32位的輸出,將此四個輸出拼接起來就可以獲得最終的加密結果。
在這裡插入圖片描述


下面介紹演算法的主要步驟:

  • 第一步:填充(padding)
    • 首先將輸入資訊用二進位制串來表示,其長度設為k bits,如果二進位制串長度k mod 512!=448,則在該二進位制串最後面補充p bits的標識,100…0(一個1和多個0),1≤p≤512,使得填充後的訊息位數滿足: k + p
      448 ( m o d 512 ) k+p ≡ 448(mod 512)
    • 然後,再向上述填充好的訊息尾部附加k值二進位制串表示的低64位(即 k m o d 2 64 k mod 2^{64} ),由於MD5加密演算法採用小端模式(little-endian),所以將此64位二進位制串的最後一個位元組放置在最前方,倒數第二個位元組放置在第二個自接觸,……,第一個位元組放置在最後方,最終處理過後的資訊長度為512的整數倍。
  • 第二步:分塊
    把填充後的訊息結果分割為L個512-bit分組: Y 0 Y_0 Y 1 Y_1 ,…, Y L 1 Y_{L-1} 。然後再將每個分組繼續劃分為16個32-bit長的子分組 X 0 X_0 X 1 X_1 ,…, X 15 X_{15}
  • 第三步:初始化
    初始化一個128-bit的MD緩衝區,記為 C V q CV_q ,表示成4個32-bit暫存器(A,B,C,D);其中, C V 0 = I V CV_0=IV 。迭代在MD緩衝區進行,最後一步的128-bit輸出結果結尾演算法結果。暫存器(A,B,C,D)置16進位制初初始值作為初始向量IV,並採用小端儲存(little-endian)的儲存結構:
	A = 0x67452301
	B = 0xEFCDAB89
	C = 0x98BADCFE
	D = 0x10325476
wordA 01 23 45 67
workB 89 AB CD EF
wordC FE DC BA 09
wordD 76 54 32 10

Little-Endian
將低位位元組排放在記憶體的低地址端,高位位元組排放在記憶體的高地址端,相反Big-Endian將高位位元組排放在記憶體的低地址端,低位位元組排放在記憶體的高地址端。儲存結構與CPU體系結構和語言編譯器有關。PowerPC系列採用Big Endian方式儲存資料,而Intel x86系列則採用Little-Endian方式儲存。

  • 第四步:總控流程
    以512-bit訊息分組為單位,每一份組 Y q ( q = 0 , 1 , . . . L 1 ) Y_q(q=0,1,...L-1) 經過四輪迴圈壓縮演算法,表示為:
    C V 0 = I V CV_0 = IV
    C V i = H M D 5 ( C V i 1 , Y i ) CV_i = H_{MD5}(CV_{i-1}, Y_i)
    輸出結果: M D = C V L MD = CV_L

  • 第五步:MD5壓縮函式 H M D 5 H_{MD5}

    • H M D 5 H_{MD5} C V CV 輸入128位,從訊息分組輸入512位,完成4輪迴圈後,輸出128位,用於下一輪輸入的 C V CV 值。
    • 四輪迴圈中使用的生成函式輪函式是一個32位非線性邏輯函式(F,G,H,I中的一個)
    • 每輪迴圈分別為不同的生成函式(F,G,H,I),結果指定的T表示元素T[]和和訊息分組的不同部分X[]做16次迭代運算,生成下一輪迴圈的輸入。
    • 四輪迴圈總共有64次迭代運算。
  • 說明:

迭代運算如下:

dtemp := d
d = c
c = b
b = b + ((a+輪函式(b,c,d)+X[k]+T[i] <<< s))
a = dtemp

輪函式定義如下:

輪次 Function g g(b,c,d)
1 F(b,c,d) (b & c) | (~b & d)
2 G(b,c,d) (b & d) | (c & ~d)
3 H[b,c,d) (b ^ c ^ d)
4 I(b,c,d) c ^ (b | ~d)

a,b,c,d:MD緩衝區(A,B,C,D)的當前值;
X[k]:當前處理訊息分組的16個子分組;
T[i]:T表的第i個元素,32位字;T表總共有64個元素,也稱為加法常數
<<<s:將32位輸入迴圈左移s位
+ : 模 2 32 2^{32} 加法。

其中X[k]具體如下:
4輪迴圈中,第i次迭代(i=0…63)使用的X[k]的確定;

  • 第一輪迴圈:k = i (0~15)
    順序使用X[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15]
  • 第二輪迴圈:k=(1+5i) mod 16 (16~31)
    順序使用X[1, 6,11, 0, 5,10,15, 4, 9,14, 3, 8,13, 2, 7,12]
  • 第三輪迴圈:k=(5+3i) mod 16(32~47)
    順序使用X[5, 8,11,14, 1, 4, 7,10,13, 0, 3, 6, 9,12,15, 2]
  • 第四輪迴圈:k=7i mod 16(48~63)
    順序使用X[0, 7,14, 5,12, 3,10, 1, 8,15, 6,13, 4,11, 2, 9]

T表定義如下:
T [ i ] = i n t ( 2 32 s i n ( i ) ) T[i] = int(2^{32}*|sin(i)|)
int 取整函式,sin 正弦函式,以 i 作為弧度輸入。
各次迭代運算採用的T值:

var T = []uint32{
	0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
	0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8,
	0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,
	0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51,
	0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
	0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905,
	0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681,
	0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60,
	0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
	0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244,
	0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
	0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314,
	0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
}

各次迭代運算採用的做迴圈移位s的值:

// 向左位移數
var s = []uint32{
	7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7,
	12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
	4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10,
	15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
}

演算法實現

// 下方函式為MD5加密演算法的核心,迭代加密步驟
func trans(text string) {
	// 將512-bit訊息劃分為16組
	X := div_group(text)
	var f, k uint32
	// result儲存最初的A,B,C,D
	a := result[0]
	b := result[1]
	c := result[2]
	d := result[3]
	for i := uint32(0); i < uint32(64); i++ {
		if i < 16 {
			f = F(b, c, d)
			k = i
		} else if i < 32 {
			f = G(b, c, d)
			k = (5*i + 1) % 16
		} else if i < 48 {
			f = H(b, c, d)
			k = (3*i + 5) % 16
		} else {
			f = I(b, c, d)
			k = (7 * i) % 16
		}
		dtemp := d
		d = c
		c = b
		b = b + (shift(a+f+T[i]+X[k], s[i]))
		a = dtemp
	}
	result[0] = result[0] + a
	result[1] = result[1] + b
	result[2] = result[2] + c
	result[3] = result[3] + d
}

加密結果如下:

// 下方三組資料來自wiki
The quick brown fox jumps over the lazy dog:  9e107d9d372bb6826bd81d3542a419d6
The quick brown fox jumps over the lazy cog:  1055d3e698d289f2af8663725127bd4b
:  d41d8cd98f00b204e9800998ecf8427e //空字串加密結果

完整程式碼詳見Github