md5加密(或者说摘要算法)大家都很熟悉了 就不解释了
现在很多数据库设计都喜欢用单向加密的方式保存密码,验证时对提交的密码再次加密之后做密文对比
/// <summary> 使用MD5加密 /// </summary> /// <param name="input">加密字符串</param> /// <remarks>2015.08.26</remarks> public static Guid ToMD5(string input) { using (var md5Provider = new MD5CryptoServiceProvider()) { var bytes = Encoding.UTF8.GetBytes(input); var hash = md5Provider.ComputeHash(bytes); var count = hash.Length; hash[0] = (byte)(hash[3] + (hash[3] = hash[0]) * 0); //交换0,3的值 hash[1] = (byte)(hash[2] + (hash[2] = hash[1]) * 0); //交换1,2的值 hash[5] = (byte)(hash[4] + (hash[4] = hash[5]) * 0); //交换4,5的值 hash[7] = (byte)(hash[6] + (hash[6] = hash[7]) * 0); //交换6,7的值 return new Guid(hash); } }
这种设计最初是防止被暴库之后 黑客可以直接得到用户的密码而设计的,因为是单向加密,所以即便知道加密算法也无法得到用户的实际密码
但是所谓道高一尺魔高一丈, 在彩虹表出现之后, 单纯的md5也不安全了
以下摘自百度百科:
彩虹表是一个用于加密散列函数逆运算的预先计算好的表, 常用于破解加密过的密码散列。一般主流的彩虹表都在100G以上。 查找表常常用于包含有限字符固定长度纯文本密码的加密。这是以空间换时间的典型实践, 在每一次尝试都计算的暴力破解中使用更少的计算能力和更多的储存空间,但却比简单的每个输入一条散列的翻查表使用更少的储存空间和更多的计算性能。使用加盐的KDF函数可以使这种攻击难以实现。
简单的来说就是 攻击者 将简单的密码(123456,111111,888888等)密码事先进行md5加密,得到密文(如123456->e10adc3949ba59abbe56e057f20f883e),然后使用这张表的数据去对比被暴的数据库的密文
这样单纯的md5,很容易就被拿下了
所以后来出现了 2次md5...N次md5, 当然这种也是然并卵......
所以后来出现了加盐加密,
简单来说:比如登录名是blqw ,密码123456 ,则数据库的密文是 md5(123456+blqw),这样可以保证,即使用户的密码是一样的,但密文却不同,这样彩虹表就歇菜了
加盐的方式有很多,一种是 md5(密码+登录名) ,这种方式大部分情况下是可以的
但是登录名修改后,密文也要修改,但是这时候你已经不知道密码的原文是什么了....(虽然一般的登录名是不能修改的,但是产品汪的想法谁知道呢.....)
或者也有人选择多建一个字段用于存放混淆码, 但是依然很麻烦
好了,说了这么多 下面就说说今天的主题,一种比较简单的加盐的方式
public static Guid ToRandomMD5(string input) { using (var md5Provider = new MD5CryptoServiceProvider()) { //获取一个256以内的随机数,用于充当 "盐" var salt = (byte)Math.Abs(new object().GetHashCode() % 256); input += salt; var bytes = Encoding.UTF8.GetBytes(input); var hash = md5Provider.ComputeHash(bytes); hash[0] = salt; return new Guid(hash); } } public static bool EqualsRandomMD5(string input, Guid rmd5) { var arr = rmd5.ToByteArray(); //将盐取出来 var salt = arr[0]; using (var md5Provider = new MD5CryptoServiceProvider()) { input += salt; var bytes = Encoding.UTF8.GetBytes(input); var hash = md5Provider.ComputeHash(bytes); for (int i = 1; i < 16; i++) { if (hash[i] != arr[i]) { return false; } } return true; } }
简单的来说就是把盐放到密文里面 md5 hash完之后得到一个16长度的byte 而byte可以保存0~255的整数 ,所以例子里面,随机的盐就是0~255的数字
然后md5(明文+盐)之后 再将盐保存到 byte[0] 的位置
这样每次hash之后 密文都是不同的 但是依然可以直接密文比较, 这里就是抛砖引玉,觉得255不够的 还可以加一位
当然也可以直接放在1~15的索引上
当然也可以把索引15的byte对15取余后得到0~14然后再放进去....