定义
MD5信息摘要算法(Message-Digest Algorithm 5)是一种哈希算法,可以将任意长的数据转化成一个128位(即16字节)的散列值。
历史
MD5算法是美国密码学家罗纳德.李维斯特(Roland Linn Reivest)设计,于1992年公开,用于替代MD4,这套算法的程序在RFC 1321中被加以规范。
1996年后,被真实存在弱点,可以被加以破解。对于需要高度安全性的数据,专家建议改用更高安全性的算法,如SHA-2。
2004年,王小云教授证明MD5数字签名算法可以产生碰撞。
2007年,Marc Stevens,Arjen K. Lenstra和Benne de Weger进一步指出通过伪造软件签名,可重复性攻击MD5算法。研究者使用前缀碰撞法(chosen-prefix collision),使程序前端包含恶意程序,利用后面的空间添上垃圾代码凑出同样的MD5 Hash值。荷兰埃因霍芬技术大学科学家成功把2个可执行文件进行了MD5碰撞,使得这两个运行结果不同的程序被计算出同一个MD5。2008年12月科研人员通过MD5碰撞成功生成了伪造的SSL证书,这使得在https协议中服务器可以伪造一些根CA的签名。
2008年,MD5被攻破后,在Crypto2008上, Rivest提出了MD6算法,该算法的Block size为512 bytes(MD5的Block Size是512 bits), Chaining value长度为1024 bits, 算法增加了并行 机制,适合于多核CPU。 在安全性上,Rivest宣称该算法能够抵抗截至目前已知的所有的 攻击(包括差分攻击)。
2009年,中国科学院的谢涛和冯登国仅用了2^20.96的碰撞算法复杂度,破解了MD5的碰撞抵抗,该攻击在普通计算机上运行只需要数秒钟。
特征
和其它哈希算法(md4、sha-1、sha-2…)的特征一样,md5具有以下特征:
1.产生散列值跟数据长度无关,不过数据长度如何,md5产生的数据都为128位。
2.相同的输入,必定由相同的输出。
3.即使输入数据十分相似,但输出值存在非常大的差异。
4.完全不同的数据,会有极低概率产生相同的散列值,这种情况称为”碰撞”。
5.不能通过散列值获得原数据。
用途
MD5的常用用途:
1.防篡改
通常网站在提供下载软件的同时,还提供该软件的MD5值。当用户下载该软件后,可以去计算下载后的软件的MD5值,然后与网站提供的MD5值做对比,如果不一致,那么下载的软件很有可能被篡改过。
2.登录密码
现在很多网站都不会明文存储注册用户的登录帐号,而是通过对用户名和密码加盐,之后再使用MD5处理才存储到数据库中。当用户进行登录操作时,网站系统将帐号和密码进行相同的处理再跟数据库存储的加密字符串进行对比。(ps:这样有一个方法可以知道网站是否存储的明文,点击网站的”找回密码”,假如网站提示让你重新输入一个新的密码,而不是直接把原密码发给你,则表示该网站存储不是明文。)
3.文件去重
比如一些去重软件,扫描主机上面的各种文件并计算它们的MD5值,然后通过查找相同MD5值,从而找到重复的文件。
还有各种网盘,通过相同原理找到并移除用户之间的重复文件,从而大大减少存储开支成本。
与此同时还有一种秒传功能,当用户向网盘上传文件时,网盘先计算该文件的MD5值,然后使用该MD5值去网盘数据查找是否已经有相同的记录,如果存在,则表明网盘线上已经存在相同的文件,这个时候只需要将线上文件做一个链接到用户目录中,而不用再上传本地文件,从而达到秒传的目的。
算法
MD5将可变长度消息处理为128位的固定长度输出。输入消息被分解为512位块(16个32位字)的块;填充消息,使其长度可被512整除。填充的工作方式如下:首先将一个位1附加到消息的末尾。接下来是使消息的长度比512的倍数小64位所需的零。其余的位用64位填充,表示原始消息的长度,模264。
主MD5算法在128位状态下运行,分为4个32位字,表示为A,B,C和D.这些字被初始化为某些固定常数。然后,主算法依次使用每个512位消息块来修改状态。消息块的处理由四个类似的阶段组成,称为轮次;每轮由基于非线性函数F,模数加法和左旋转的16个类似操作组成。图1说明了一轮内的一个操作。有四种可能的功能;每轮使用不同的一个:
F(B,C,D)=(B&C)|((~B)&D)
G(B,C,D)=(B&D)|(C&(~D))
H(B,C,D)=B^C^D
I(B,C,D)=C^(B|(~D))

MD5由64个这样的操作组成,分为四轮,每组16个操作。F是非线性函数; 每轮使用一个函数。M i表示消息输入的32位块,并且K i表示32位常数,对于每个操作是不同的。小号指由左位旋转小号的地方; 小号变化为每个操作。表示加法模2^32。
//Note: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
var int[64] s, K
var int i
//s specifies the per-round shift amounts
s[ 0..15] := { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22 }
s[16..31] := { 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20 }
s[32..47] := { 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23 }
s[48..63] := { 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 }
//Use binary integer part of the sines of integers (Radians) as constants:
for i from 0 to 63
K[i] := floor(232 × abs(sin(i + 1)))
end for
//(Or just use the following precomputed table):
K[ 0.. 3] := { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee }
K[ 4.. 7] := { 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501 }
K[ 8..11] := { 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be }
K[12..15] := { 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 }
K[16..19] := { 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa }
K[20..23] := { 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8 }
K[24..27] := { 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed }
K[28..31] := { 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a }
K[32..35] := { 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c }
K[36..39] := { 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70 }
K[40..43] := { 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05 }
K[44..47] := { 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 }
K[48..51] := { 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039 }
K[52..55] := { 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1 }
K[56..59] := { 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1 }
K[60..63] := { 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }
//Initialize variables:
var int a0 := 0x67452301 //A
var int b0 := 0xefcdab89 //B
var int c0 := 0x98badcfe //C
var int d0 := 0x10325476 //D
//Pre-processing: adding a single 1 bit
append "1" bit to message
// Notice: the input bytes are considered as bits strings,
// where the first bit is the most significant bit of the byte.[48]
//Pre-processing: padding with zeros
append "0" bit until message length in bits ≡ 448 (mod 512)
append original length in bits mod 264 to message
//Process the message in successive 512-bit chunks:
for each 512-bit chunk of padded message
break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
//Initialize hash value for this chunk:
var int A := a0
var int B := b0
var int C := c0
var int D := d0
//Main loop:
for i from 0 to 63
var int F, g
if 0 ≤ i ≤ 15 then
F := (B and C) or ((not B) and D)
g := i
else if 16 ≤ i ≤ 31 then
F := (D and B) or ((not D) and C)
g := (5×i + 1) mod 16
else if 32 ≤ i ≤ 47 then
F := B xor C xor D
g := (3×i + 5) mod 16
else if 48 ≤ i ≤ 63 then
F := C xor (B or (not D))
g := (7×i) mod 16
//Be wary of the below definitions of a,b,c,d
F := F + A + K[i] + M[g]
A := D
D := C
C := B
B := B + leftrotate(F, s[i])
end for
//Add this chunk's hash to result so far:
a0 := a0 + A
b0 := b0 + B
c0 := c0 + C
d0 := d0 + D
end for
var char digest[16] := a0 append b0 append c0 append d0 //(Output is in little-endian)
//leftrotate function definition
leftrotate (x, c)
return (x << c) binary or (x >> (32-c));
各种编程语言
1.python
在python3的标准库中,已经移除了md5,而关于hash加密算法都放在hashlib这个标准库中,如SHA1、SHA224、SHA256、SHA384、SHA512和MD5算法等。
import hashlib
hash = hashlib.md5()
hash.update('original'.encode('utf-8'))
hash.hexdigest();
2.javascript
<script src="http://cdn.bootcss.com/blueimp-md5/1.1.0/js/md5.js"></script>
var hash = md5("original");
3.objective-c
在MyAdditions.h头文件中:
@interface NSString (MyAdditions)
- (NSString *)md5;
@end
@interface NSData (MyAdditions)
- (NSString*)md5;
@end
在MyAdditions.m文件中:
#import "MyAdditions.h"
#import <CommonCrypto/CommonDigest.h> // Need to import for CC_MD5 access
@implementation NSString (MyAdditions)
- (NSString *)md5
{
const char *cStr = [self UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5( cStr, (int)strlen(cStr), result ); // This is the md5 call
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
@end
@implementation NSData (MyAdditions)
- (NSString*)md5
{
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5( self.bytes, (int)self.length, result ); // This is the md5 call
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
@end