逆向是不可能逆向的
在正式介绍MD5“破解”的方法前,先说明一点:我们没办法把MD5码还原对应的原文。道理很简单,任意长度的数据经过MD5处理后,所包含的信息量已经大大减少。要是可以还原的话,那MD5岂不是成为压缩算法??
所谓的“破解”指的是“碰撞”。即找到一个原文,算出来的MD5码和已知的MD5码一样。接下来介绍一些常见的破解方法。
暴力碰撞:穷举法&字典法
小标题上写了两种方法:穷举法和字典法。但是我认为它们的本质是一样的,都是利用计算机的资源尝试碰撞已知的MD5码。这里就放在一起了。
穷举法非常简单,就是不停地尝试各种字符的排列组合,看哪一个组合的MD5码能对上。可惜缺点是太耗费时间了。我们举个栗子,假设我们要破解一个6位大小写字母和数字混合的密码,那么一共有 [公式] 种组合。这个数的大小超过500亿。
既然计算如此费时,能不能考虑把计算结果以映射表的形式存放起来,一个萝卜一个坑,一个原文对应着一个MD5码呢?可以呀!这就是传说中的“字典法”。将已知的MD5码查表,直接反查出原文。字典法体现了算法设计的“以空间换时间”的思想。缺点是比较耗费空间。不过现在硬盘的价格变得白菜价了,空间开销不算什么。
这是一个用字典法破解MD5的网站:https://www.cmd5.com/password.aspx
时间和空间的折中:哈希链表&彩虹表法
如果说穷举法太耗费时间,字典法太耗费存储空间的话,我们能不能考虑在时间消耗和空间消耗之间折中呢?我们可以考虑用链表将一系列有意义的原文和MD5码串起来。
要构造这样的链表,我们需要两个函数:哈希函数H(x)和衰减函数(reduction function)R(x)。哈希函数可以是MD5,也可以是其他的消息摘要算法。H(x)的值域是R(x)的定义域,R(x)的值域是H(x)的定义域。R(x)不是H(x)的反函数。
将一个原文不停地使用H(x)和R(x)交替进行运算k次,再将原文本身和运算结果以链表的形式串接起来,就可以得到结点个数为2k+1的链表。实际存放的时候只存放首端和末端两个原文即可。这种链表叫做“哈希链表”,体现了算法设计的“时空权衡”(Space and Time Tradeoffs)。
举个栗子,假设原文s=abcabc,经过2次交替运算,得到以下的链表:
abcabc->H(x)->3C8B0D7A->R(x)->eopmca->H(x)->7E9F216C->R(x)->rapper
以上数据均为举例编造的,仅为说明原理使用。那我们存放这个链表的时候,只需要记录abcabc和rapper两个原文即可。
假设我们要破解的摘要值(哈希链表的H(x)不一定是MD5算法,这里用更准确的说法代替MD5码)是7E9F216C,经过R(x)运算得到rapper,说明我们要寻找的原文就在以rapper为末端的哈希链表中。从首端开始经过多次运算,我们发现eopmca的摘要值就是7E9F216C。于是就反查出7E9F216C对应的原文是eopmca。
如果在生成哈希链表的时候依次使用多个不一样的R(x),此时的哈希链表就是“彩虹表”。
这里有已经计算好的彩虹表:http://project-rainbowcrack.com/table.htm
差分攻击
上面介绍的穷举法、字典法和彩虹表法都是暴力破解,适用于任何的消息摘要算法。真正意义上MD5算法的破解,是2004年山东大学王小云教授提出的MD5碰撞方法。她所用到的方法正是差分攻击。
这种方法概括起来说是这样的:给定一个1024位的原文M1,加上一个特定的常数得到的新的明文M2。M1和M2的MD5码是一样的。(出处及具体操作见参考文献[1])这个特定的常数到底是怎么找出来的?笔者当时在查阅原始文献的时候也不清楚。因此后来的研究者开始对怎么样差分进行了各种各样的研究。这里就不再赘述。
后记
其实还有一种破解MD5的方法——长度扩展攻击。不过这种方法是在一定条件下(破解加盐之后产生的MD5码)才能用的。这种方法由MD5分块计算的特性而来。
1、算法的公开并不意味着不安全;RSA 的算法也是公开的,AES 也是公开的。现代密码学的安全性从不是靠算法的保密来保证的。
2、目前没有软件能有效地破解 MD5。大多数时候只是把常见字符串的 MD5 存了起来为彩虹表,然后直接反查。
3、再次强调 MD5 只是哈希,而不是加密。MD5 是没有可能解密的,因为一个 MD5 可能对应无数种可能的明文。
4、MD5 目前来说还是可以用的,尤其是考虑到合适的加盐以后可以解决大多数彩虹表带来的危险。当然现在已经很多人提倡用 SHA 系列的哈希算法取代 MD5。
对密码学一窍不通,以上都是我乱编的,实在编不下去了……