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然后再放進去....