1. 程式人生 > >MD5演算法的C++實現

MD5演算法的C++實現

1. Introduction
MD5演算法是一種訊息摘要演算法(Message Digest Algorithm),此演算法以任意長度的資訊(message)作為輸入進行計算,產生一個128-bit(16-byte)的指紋或報文摘要(fingerprint or message digest)。兩個不同的message產生相同message digest的機率相當小,從一個給定的message digest逆向產生原始message更是困難(不過據說我國的某個教授很善於從message digest構造message),因此MD5演算法適合用在數字簽名應用中。MD5實現簡單,在32位的機器上執行速度也相當快,當然實際應用也不僅僅侷限於數字簽名。

2. MD5 Algorithm Description
假設輸入資訊(input message)的長度為b(bit),我們想要產生它的報文摘要,在此處b為任意的非負整數:b也可能為0,也不一定為8的整數倍,且可能是任意大的長度。設該資訊的位元流表示如下:

          M[0] M[1] M[2] ... M[b-1]

計算此資訊的報文摘要需要如下5步:
2.1 Append Padding Bits
資訊計算前先要進行位補位,設補位後資訊的長度為LEN(bit),則LEN%512 = 448(bit),即資料擴充套件至
K*512+448(bit)。即K*64+56(byte),K為整數。補位操作始終要執行,即使補位前資訊的長度對512求餘的結果是448。具體補位操作:補一個1,然後補0至滿足上述要求。總共最少要補1bit,最多補512bit。

2.2 Append Length

將輸入資訊的原始長度b(bit)表示成一個64-bit的數字,把它新增到上一步的結果後面(在32位的機器上,這64位將用2個字來表示並且低位在前)。當遇到b大於2^64這種極少的情況時,b的高位被截去,僅使用b的低64位。經過上面兩步,資料就被填補成長度為512(bit)的倍數。也就是說,此時的資料長度是16個字(32byte)的整數倍。此時的資料表示為:

          M[0 ... N-1]

其中的N是16的倍數。

2.3 Initialize MD Buffer
用一個四個字的緩衝器(A,B,C,D)來計算報文摘要,A,B,C,D分別是32位的暫存器,初始化使用的是十六進位制表示的數字,注意低位元組在前:

        word A: 01 23 45 67
        word B: 89 ab cd ef
        word C: fe dc ba 98
        word D: 76 54 32 10


2.4 Process Message in 16-Word Blocks
首先定義4個輔助函式,每個函式的輸入是三個32位的字,輸出是一個32位的字:

        F(X,Y,Z) = XY v not(X) Z
        G(X,Y,Z) = XZ v Y not(Z)
        H(X,Y,Z) = X xor Y xor Z
        I(X,Y,Z) = Y xor (X v not(Z))

NOTE:not(X)代表X的按位補運算,X v Y 表示X和Y的按位或運算,X xor Y代表X和Y的按位異或運算,XY代表X和Y的按位與運算。

具體過程如下:
 1 /* Process each 16-word block. */ 2    For i =0 to N/16-1do 3  4 /* Copy block i into X. */ 5      For j =0 to 15do 6        Set X[j] to M[i*16+j].
 7      end /* of loop on j */
 8  9 /* Save A as AA, B as BB, C as CC, and D as DD. */10      AA = A
11      BB =
 B
12      CC =
 C
13      DD =
 D
14 
15 /* Round 1. */16 /* Let [abcd k s i] denote the operation
17           a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
18 /* Do the following 16 operations. */19      [ABCD  071]  [DABC  1122]  [CDAB  2173]  [BCDA  3224]
20      [ABCD  475]  [DABC  5126]  [CDAB  6177]  [BCDA  7228
]
21      [ABCD  879]  [DABC  91210]  [CDAB 101711]  [BCDA 112212
]
22      [ABCD 12713]  [DABC 131214]  [CDAB 141715]  [BCDA 152216
]
23 
24 /* Round 2. */25 /* Let [abcd k s i] denote the operation
26           a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
27 /* Do the following 16 operations. */28      [ABCD  1517]  [DABC  6918]  [CDAB 111419]  [BCDA  02020]
29      [ABCD  5521]  [DABC 10922]  [CDAB 151423]  [BCDA  42024
]
30      [ABCD  9525]  [DABC 14926]  [CDAB  31427]  [BCDA  82028
]
31      [ABCD 13529]  [DABC  2930]  [CDAB  71431]  [BCDA 122032
]
32 
33 /* Round 3. */34 /* Let [abcd k s t] denote the operation
35           a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
36 /* Do the following 16 operations. */37      [ABCD  5433]  [DABC  81134]  [CDAB 111635]  [BCDA 142336]
38      [ABCD  1437]  [DABC  41138]  [CDAB  71639]  [BCDA 102340
]
39      [ABCD 13441]  [DABC  01142]  [CDAB  31643]  [BCDA  62344
]
40      [ABCD  9445]  [DABC 121146]  [CDAB 151647]  [BCDA  22348
]
41 
42 /* Round 4. */43 /* Let [abcd k s t] denote the operation
44           a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
45 /* Do the following 16 operations. */46      [ABCD  0649]  [DABC  71050]  [CDAB 141551]  [BCDA  52152]
47      [ABCD 12653]  [DABC  31054]  [CDAB 101555]  [BCDA  12156
]
48      [ABCD  8657]  [DABC 151058]  [CDAB  61559]  [BCDA 132160
]
49      [ABCD  4661]  [DABC 111062]  [CDAB  21563]  [BCDA  92164
]
50 
51 /* Then perform the following additions. (That is increment each
52 
        of the four registers by the value it had before this block
53         was started.) */
54      A = A + AA
55      B = B +
 BB
56      C = C +
 CC
57      D = D +
 DD
58 
59    end /* of loop on i */
2.5 Output
報文摘要的產生後的形式為:A,B,C,D。也就是低位位元組A開始,高位位元組D結束。

3. C++ Implementation
有了上面5個步驟的演算法描述,用C++實現起來就很直接了。需要注意的是在具體實現的時候上述5個步驟的順序會有所變動,因為在大多數情況下我們都無法或很難提前計算出輸入資訊的長度b(如輸入資訊來自檔案或網路)。因此在具體實現時Append Padding BitsAppend Length這兩步會放在最後面。

4. Test Suite
由於實現程式碼比較長,在這裡就不貼出來了,在本文後面會提供下載。MD5類的public介面如下:
md5.h
 1 class MD5 {
 2 public
:
 3 
    MD5();
 4     MD5(constvoid*
input, size_t length);
 5     MD5(const string&
str);
 6     MD5(ifstream &
in);
 7 void update(constvoid*
input, size_t length);
 8 void update(const string&
str);
 9 void update(ifstream&
in);
10 constbyte*
 digest();
11 
    string toString();
12 void
 reset();
13 
    ...
14 };

下面簡單介紹一下具體用法:
1.計算字串的MD5值
下面的程式碼計算字串"abc"的MD5值並用cout輸出:
1 MD5 md5;
2 md5.update("abc"
);
3 cout << md5.toString() <<
 endl;
4 //或者更簡單點
5 cout << MD5("abc").toString() << endl;
2.計算檔案的MD5值
下面的程式碼計算文字檔案"D:\test.txt"的MD5值並用cout輸出,如果是二進位制檔案開啟的時候記得要指定ios::binary模式。另外需要注意的是用來計算的檔案必須存在,所以最好在計算前先判斷下ifstream的狀態。
(本來判斷ifstream是否有效不該是客戶的責任,原本想在ifstream無效時用檔名做引數丟擲FileNotFoundException之類的異常,後來卻發現從ifstream中居然無法得到檔名...)
1 MD5 md5;
2 md5.update(ifstream("D:\\test.txt"
));
3 cout << md5.toString() <<
 endl;
4 //或者更簡單點
5 cout << MD5(ifstream("D:\\test.txt")).toString() << endl;
3.最基本的用法
上面的用來計算字串和檔案MD5值的介面都是為了方便才提供的,其實最基本的介面是:
void update(const void *input, size_t length);
update的另外兩個過載都是基於它來實現的,下面的程式碼用上述介面來實現FileDigest函式,該函式用來計算檔案的MD5值:
 1 string FileDigest(const string& file) {
 2 
 3     ifstream in(file.c_str(), ios::binary);
 4 if (!
in)
 5 return""
;
 6 
 7     MD5 md5;
 8 
    std::streamsize length;
 9 char buffer[1024
];
10 while (!
in.eof()) {
11         in.read(buffer, 1024
);
12         length =
 in.gcount();
13 if (length >0
)
14 
            md5.update(buffer, length);
15 
    }
16 
    in.close();
17 return
 md5.toString();
18 }

下面看看測試程式碼:
test.cpp
 1 #include "md5.h" 2 #include <iostream> 3  4 using namespace std;
 5 
 6 void PrintMD5(const string& str, MD5& md5) {
 7     cout <<"MD5(\"" << str << "\") = "<< md5.toString() <<
 endl;
 8 
}
 9 
10 int main() {
11 
12     MD5 md5;
13     md5.update(""
);
14     PrintMD5(""
, md5);
15 
16     md5.update("a");
17     PrintMD5("a"
, md5);
18 
19     md5.update("bc");
20     PrintMD5("abc"
, md5);
21 
22     md5.update("defghijklmnopqrstuvwxyz");
23     PrintMD5("abcdefghijklmnopqrstuvwxyz"
, md5);
24 
25     md5.reset();
26     md5.update("message digest"
);
27     PrintMD5("message digest"
, md5);
28 
29     md5.reset();
30     md5.update(ifstream("D:\\test.txt"
));
31     PrintMD5("D:\\test.txt"
, md5);
32 
33 return0;
34 }

測試結果:
MD5("") = d41d8cd98f00b204e9800998ecf8427e
MD5("a") = 0cc175b9c0f1b6a831c399e269772661
MD5("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5("D:\test.txt") = 7ac66c0f148de9519b8bd264312c4d64


原始碼下載:點選下載
在這裡放上Vrcats修改的Qt版本:點選下載

posted on 2007-09-11 12:20 螞蟻終結者 閱讀(51481) 評論(121)  編輯 收藏 引用 所屬分類: Encrypt