11.消息摘要算法之MD5

转载自:https://github.com/cokepluscarbon/

任何消息通过散列函数处理后,都会获得唯一的散列值。这一个过程称之为“消息摘要”,其散列值称为“数字指纹”,散列函数算法称为“消息摘要算法”

MD5是目前运用最为广泛的消息摘要算法之一。经过MD5处理的消息后会产生一个128位的二进制摘要信息,转换换为十六进制后,可以得到一个32位长度的字符串(包含字母和数字)。

例如,cokepluscarbon经过MD5散列处理的结果为:b2ae5ff940a47bf9a5a06695089d414c

11.1 MD5不属于加密算法

MD5实际上不属于加密算法,因为经过MD5散列处理后产生的数字指纹是不可能逆向还原为消息的。

以一个1G的文件为例,经过MD5散列处理后为32位十六进制的${hashkey},那么我们是不可能通过这32位十六进制的${hashkey}还原1G的文件的!如果可以,那压缩效率真这是屌爆了!!!

11.2 MD5的散列值范围

我们知道MD5散列产生的散列值是128位的二进制,那么MD5最多只能散列出2^128个不同的字符串。也就是说,当我们对2^128+1个消息进行散列时,至少会有两条消息的散列值是一模一样的!,这也侧证了不能从散列值逆向还原消息这个事实。

  • 同一个消息的散列值是一定是唯一的;
  • 散列值在0至2^128这个区间均匀分布;
  • 不同的消息会产生同一个散列值,但是概率极低(这种情况称之为哈希碰撞);

11.3 彩虹表

虽然说MD5不可以逆向破解,但是可以事先根据大量密码字典生成大量的散列值,然后再根据散列值逆向查询密码。这个字典与散列值对应的数据库我们称之为“彩虹表”。这是以空间换取时间的典型案例。

彩虹表

password md5hash
key1 ${hashkey1}
key2 ${hashkey2}

例如我们知道了某个散列值${hashkey1}那么我们就可以通过遍历数据库,得到密码key1。

彩虹表大都上百G上T,实际上,如果建立使用合适的分库分表和索引等技术,查询效率是非常高的。

MD5散列与逆向查询:http://md5online.org/

11.4 MD5在密码处理上的应用

使用明文存储密码是非常不好的做法,一般我们都会使用MD5对密码进行散列处理然后再进行存储。

为了更好地使用MD5对密码进行处理,我们看一下密码在数据库中存储的历史变迁过程。

11.4.1 明文密码

最原始的做法:存储密码的明文形式。

username password
admin admin
test test

这种做法很直接,但是缺陷也非常明显,数据库一旦泄露,那么所有用户名和密码都会泄露,后果非常严重。

11.4.2 简单MD5散列的密码

username password
admin 21232f297a57a5a743894a0e4a801fc3
test 098f6bcd4621d373cade4e832627b4f6

经过简单MD5散列之后,admin散列为21232f297a57a5a743894a0e4a801fc3,但是缺陷依然明显。像admin这样的密码很容易通过彩虹表查询得到明文密码,即使密码设置得很复杂,也可以通过哈希碰撞绕开实际密码从而获取用户权限。

11.4.3 固定盐

为了解决上述问题,我们可以在密码进行散列之前加入一点”佐料“,例如,所有的密码后面都加入”iloveu1314”然后再进行散列。

这个“佐料”,我们称之为“盐”。

String password = md5(password + "iloveu1314");
username password
admin 1a5c828bbfd8ac20ccef64d59e34af7f
test 006a7f976aacd9ada0966cbf954b7900

那么我们就无形之中加强了密码的复杂度,加大了彩虹表反向查询的难度。然而我们这里使用的固定盐,那么当我们的操作系统被入侵导致盐和用户表被泄露后,攻击者也可以根据盐重新生成彩虹表从而破解用户密码。

11.4.4 随机盐

为了解决固定盐的缺陷,我们可以这样改善:为不同的用户生成随机盐,然后进行散列。

username password salt
admin ce3374fe8d444dc1b0b9f4046ceff5f9 698d51a19d8a121ce581499d7b701668
test b559c3cf86d38a35842fe6b397b53668 bcbe3365e6ac95ea2c0343a2395834dd

那么即使用户密码的散列值和盐都别泄露,攻击者想破解密码的难度也级数提高。因为想破击一个用户的密码,那么就要生成一次彩虹表。假如密码字典大小为200W,用户信息泄露为10W,那么要生成的彩虹表就需要200W * 10W = 2000WW条记录。这实际上根据现在的计算机性能和存储空间,破解这些密码是得不偿失的。

如果再想增加破解的难度,可以将密码根据一定算法插入到不同盐的位置,然后进行散列。当然,这实际上对一般系统来说已经没多大必要了,绕开随机盐需要付出的代价已经非常惨痛了。

11.5 Commons Codec

Apache Commons Codec是Apache基金合作组织一个Java库,它提供了常用的编码、解码和加密的实现。

@Test
public void t01_base64Encode() {
String plainText = "cokepluscarbon";
String ciphertext = Base64.encodeBase64String(plainText.getBytes());
Assert.assertEquals("Y29rZXBsdXNjYXJib24=", ciphertext);
}
@Test
public void t02_base64Decode() {
String ciphertext = "Y29rZXBsdXNjYXJib24=";
String plainText = new String(Base64.decodeBase64(ciphertext));
Assert.assertEquals("cokepluscarbon", plainText);
}
@Test
public void t03_md5Hash() {
String plainText = "cokepluscarbon";
String ciphertext = DigestUtils.md5Hex(plainText);
Assert.assertEquals("b2ae5ff940a47bf9a5a06695089d414c", ciphertext);
}
@Test
public void t04_md5HashWithSalt() {
String plainText = "cokepluscarbon";
String salt = "IAMSALT";
String ciphertext = DigestUtils.md5Hex(plainText + salt);
Assert.assertEquals("20f23dbff3f91aa59f25b29a93d996e0", ciphertext);
}

11.6 参考资料

posted @ 2014-05-06 18:01  cokepluscarbon  阅读(759)  评论(0)    收藏  举报