哈希(Hash)与加密(Encrypt)的基本原理、区别及工程应用
[2] 哈希(Hash)与加密(Encrypt)的基本原理、区别及工程应用
3.3、对简单哈希(Hash)的攻击
下面我们讨论上述的数据保护方法是否安全。
对于哈希的攻击,主要有寻找碰撞法和穷举法。
先来说说寻找碰撞法。从哈希本身的定义和上面的数据保护原理图可以看出,如果想非法登录系统,不一定非要得到注册时的输入口令,只要能得到一个注册口令的碰撞即可。因此,如果能从杂凑串中分析出一个口令的碰撞,则大功告成。
不过我的意见是,对这种攻击大可不必担心,因为目前对于MD5和SHA1并不存在有效地寻找碰撞方法。虽然我国杰出的数学家王小云教授曾经在国际密码学会议上发布了对于MD5和SHA1的碰撞寻找改进算法,但这种方法和很多人口中所说的“破解”相去甚远,其理论目前仅具有数学上的意义,她将破解MD5的预期步骤数从2^80降到了2^69,虽然从数学上降低了好几个数量级,但2^69对于实际应用来说仍然是一个天文数字,就好比以前需要一亿年,现在需要一万年一样。
不过这并不意味着使用MD5或SHA1后就万事大吉了,因为还有一种对于哈希的攻击方法——穷举法。通俗来说,就是在一个范围内,如从000000到999999,将其中所有值一个一个用相同的哈希算法哈希,然后将结果和杂凑串比较,如果相同,则这个值就一定是源字串或源字串的一个碰撞,于是就可以用这个值非法登录了。
例如,下文是对MD5的穷举攻击的代码(设攻击范围为000000到999999):
using System; using System.Web.Security; namespace HashAndEncrypt { /// <summary> /// MD5攻击工具类 /// </summary> public sealed class MD5AttackHelper { /// <summary> /// 对MD5进行穷举攻击 /// </summary> /// <param name="hashString">杂凑串</param> /// <returns>杂凑串的源串或源串碰撞(攻击失败则返回null)</returns> public static string AttackMD5(string hashString) { for (int i = 0; i <= 999999; i++) { string testString = i.ToString(); while (testString.Length < 6) testString = "0" + testString; if (FormsAuthentication.HashPasswordForStoringInConfigFile(testString, "MD5") == hashString) return testString; } return null; } } }
这种看似笨拙的方法,在现实中爆发的能量却是惊人的,目前几乎所有的MD5破解机或MD5在线破解都是用这种穷举法,但就是这种“笨”方法,却成功破解出很多哈希串。纠其缘由,就是相当一部分口令是非常简单的,如“123456”或“000000”这种口令还有很多人在用,可以看出,穷举法是否能成功很大程度上取决于口令的复杂性。因为穷举法扫描的区间往往是单字符集、规则的区间,或者由字典数据进行组合,因此,如果使用复杂的口令,例如“ASDF#$%uiop.8930”这种变态级口令,穷举法就很难奏效了。
3.4、对一次哈希(Hash)的改进——多重混合哈希(Hash)
上面说过,如果口令过于简单,则使用穷举法可以很有效地破解出一次哈希后的杂凑串。如果不想这样,只有让用户使用复杂口令,但是,很多时候我们并不能强迫用户,因此,我们需要想一种办法,即使用户使用诸如“000000”这种简单密码,也令穷举法难奏效。其中一种办法就是使用多重哈希,所谓多重哈希就是使用不同的哈希函数配合自定义的Key对口令进行多次哈希,如果Key很复杂,那么穷举法将变得异常艰难。
例如,如果使用下面的混合公式进行哈希:
如果将Key设为一个极为复杂的字符串,那么在攻击者不知道Key的情况下,几乎无法通过穷举法破解。因为即使S很简单,但是Key的MD5值几乎是无法在合理时间内穷举完的。下面是这种多重混合哈希的代码实现:
using System; using System.Web.Security; namespace HashAndEncrypt { /// <summary> /// MD5攻击工具类 /// </summary> public sealed class MD5AttackHelper { /// <summary> /// 对MD5进行穷举攻击 /// </summary> /// <param name="hashString">杂凑串</param> /// <returns>杂凑串的源串或源串碰撞(攻击失败则返回null)</returns> public static string AttackMD5(string hashString) { for (int i = 0; i <= 999999; i++) { string testString = i.ToString(); while (testString.Length < 6) testString = "0" + testString; if (FormsAuthentication.HashPasswordForStoringInConfigFile(testString, "MD5") == hashString) return testString; } return null; } } }
3.5、使用加密(Encrypt)方法进行数据保护
加密方法如果用于口令保护的话,与上述哈希方法的流程基本一致,只是在需要时,可以使用解密方法得到明文。关于加密本身是一个非常庞大的系统,而对于加密算法的攻击更是可以写好几本书了,所以这里从略。下面只给出使用C#进行DES加密和解密的代码。
using System; using System.Web.Security; namespace HashAndEncrypt { /// <summary> /// MD5攻击工具类 /// </summary> public sealed class MD5AttackHelper { /// <summary> /// 对MD5进行穷举攻击 /// </summary> /// <param name="hashString">杂凑串</param> /// <returns>杂凑串的源串或源串碰撞(攻击失败则返回null)</returns> public static string AttackMD5(string hashString) { for (int i = 0; i <= 999999; i++) { string testString = i.ToString(); while (testString.Length < 6) testString = "0" + testString; if (FormsAuthentication.HashPasswordForStoringInConfigFile(testString, "MD5") == hashString) return testString; } return null; } } }
4、总结
密码学本身是一个非常深奥的数学分支,对于普通开发者,不需要了解过于深入的密码学知识。本文仅仅讲述哈希与加密的基础内容,并对两者做了比较,帮助读者明晰概念,另外,对一些实际应用情况进行了简单的讨论。希望本文对大家有所帮助。