MD5算法的C++实现

来源:互联网 发布:大码女装淘宝店 编辑:程序博客网 时间:2024/05/17 04:30
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 to15 do
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 0 7 1]  [DABC 112 2]  [CDAB 2 17 3]  [BCDA 3 22 4]
20      [ABCD 4 7 5]  [DABC 5 12 6]  [CDAB 6 17 7]  [BCDA 7 22 8
]
21      [ABCD 8 7 9]  [DABC 9 1210]  [CDAB10 1711]  [BCDA11 2212
]
22      [ABCD12 7 13]  [DABC131214]  [CDAB14 1715]  [BCDA15 2216
]
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 1 517]  [DABC 6 918]  [CDAB11 1419]  [BCDA 02020]
29      [ABCD 5 521]  [DABC10  922]  [CDAB15 1423]  [BCDA 42024
]
30      [ABCD 9 525]  [DABC14  926]  [CDAB 31427]  [BCDA 82028
]
31      [ABCD13 5 29]  [DABC 2 930]  [CDAB 71431]  [BCDA12 2032
]
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 5 433]  [DABC 81134]  [CDAB11 1635]  [BCDA14 2336]
38      [ABCD 1 437]  [DABC 41138]  [CDAB 71639]  [BCDA10 2340
]
39      [ABCD13 4 41]  [DABC 01142]  [CDAB 31643]  [BCDA 62344
]
40      [ABCD 9 445]  [DABC12 1146]  [CDAB15 1647]  [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 0 649]  [DABC 71050]  [CDAB14 1551]  [BCDA 52152]
47      [ABCD12 6 53]  [DABC 31054]  [CDAB10 1555]  [BCDA 12156
]
48      [ABCD 8 657]  [DABC15 1058]  [CDAB 61559]  [BCDA13 2160
]
49      [ABCD 4 661]  [DABC11 1062]  [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

10int 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

 

MD5权威文档:http://www.ietf.org/rfc/rfc1321.txt

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 桌面太低座位太高写字不舒服怎么办 一个人如果欠下网贷无法偿还怎么办 c语言的编译和运行不见了怎么办 8k纸的国庆节手抄报怎么办 合同员工签字了老板一直未签怎么办 下体长毛了很多肉芽很痒怎么办 对方拟把假离婚协议弄假成真怎么办 领主之塔改版后旧材料怎么办 汽车遥控钥匙打不开车门怎么办 苹果手机访问限制密码忘了怎么办 苹果的访问限制密码忘了怎么办 手机下雨天进水声音出了问题怎么办 胸太小了该怎么办经常揉胸会变大吗 鼻冲洗器冲鼻子耳朵进水怎么办 手表进水了然后里面有水珠怎么办 玩游戏后头晕恶心想吐怎么办 吃完美林3小时后又发烧怎么办 颜值和身材都是负分怎么办 妈妈离婚又有一个孩子我该怎么办 孩子妈妈天天给孩子吃药我该怎么办 被像蚂蚁有翅膀的虫子咬了怎么办 1周半宝宝扭脚丫肿了怎么办 离婚后前夫带孩子走了找不到怎么办 宜昌全视之眼逃出卢浮宫怎么办 qq号被盗了密保手机也被改怎么办 乐视盒子控播平台认证怎么办 购买冲气娃娃被发现了怎么办 买了充气娃娃太美舍不得扔怎么办 一品官老爷账号密码忘了怎么办 苹果手机加声音显示出耳机怎么办 被删除的照片恢复后效果变差怎么办 w10系统玩刺激战场声音小怎么办 宝宝不咳嗽但是喉咙有痰怎么办 深圳限行如果车堵在路上怎么办 奔跑吧qq中奖我填写资料怎么办 微信之前绑定的手机号丢了怎么办 龙之谷手游换装备洗炼材料怎么办 小学生在班上碰到流氓同学怎么办 问道手游仓库密码忘了怎么办 问道手游安全码忘了怎么办 问道手游账号密码忘了怎么办