简介
MD5(RFC 1321)的全称是Message-Digest Algorithm 5,在90年代初由MIT的计算机科学实验室和RSA Data Security Inc发明,经MD2、MD3和MD4发展而来。它对输入仍以512位分组,其输出是4个32位字的级联,与 MD4 相同。MD5比MD4来得复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好。
原理
Message-Digest泛指字节串(Message)的Hash变换,就是把一个任意长度的字节串变换成一定长的大整数。请注意我使用了“字节串”而不是“字符串”这个词,是因为这种变换只与字节的值有关,与字符集或编码方式无关。
MD5将任意长度的“字节串”变换成一个128bit的大整数,并且它是一个不可逆的字符串变换算法,换句话说就是,即使你看到源程序和算法描述,也无法将一个MD5的值变换回原始的字符串,从数学原理上说,是因为原始的字符串有无穷多个,这有点象不存在反函数的数学函数。
应用
1. 防止被篡改:
1) 比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中 途未被篡改。
2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。
3)SVN在检测文件 是否在CheckOut后被修改过,也是用到了MD5.
2. 防止直接看到明文:
现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码(其实这样是不安全的,后面我会提到)。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)
3. 防止抵赖(数字签名):
这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。
C源代码
http://people.csail.mit.edu/rivest/Md5.c
C API用法
在MD5的C源码里,由一个struct和三个函数协同完成工作:
/* typedef a 32 bit type */typedef unsigned long int UINT4;/* Data structure for MD5 (Message Digest) computation */typedef struct {UINT4 i[2]; /* number of _bits_ handled mod 2^64 */UINT4 buf[4]; /* scratch buffer */unsigned char in[64]; /* 512bit input buffer */ unsigned char digest[16]; /* actual digest after MD5Final call */} MD5_CTX;void MD5Init(MD5_CTX *mdContext);void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);void MD5Final(MD5_CTX *mdContext);像下面这样使用:
void TestMD5() { // 初始化一次 MD5_CTX mdContext; MD5Init(&mdContext); // 获取输入缓冲区,可以重复调用此函数以应对多个缓冲区 // 其内部以MD5Transform迭代数次以对若干512bits分组加密 unsigned char inBuffer[] = "hello, world!"; MD5Update(&mdContext, inBuffer, sizeof(inBuffer)/sizeof(*inBuffer)); // 产生最终128位(16字节)加密结果,保存在mdContext.digest MD5Final(&mdContext); }下面是个简单的计算文件MD5的函数
void TestGUtMD5( const char* fileName ) { MD5Context mdContext; MD5Init( &mdContext ); FILE* filePointer = fopen( fileName, "rb" ); unsigned char buffer[ 1024 ] = { 0 }; while ( true ) { int readByteCount = fread( buffer, 1, 1024, filePointer ); if ( readByteCount <= 0 ) { break; } MD5Update( &mdContext, buffer, readByteCount ); } fclose( filePointer ); MD5Final( &mdContext ); for ( int i = 0 ;i < 16; ++i) { cout << hex << (int)mdContext.digest[i]; }}