当前位置: 首页 > 编程笔记 >

C#使用 Salt + Hash 来为密码加密

龙骏
2023-03-14
本文向大家介绍C#使用 Salt + Hash 来为密码加密,包括了C#使用 Salt + Hash 来为密码加密的使用技巧和注意事项,需要的朋友参考一下

(一) 为什么要用哈希函数来加密密码

如果你需要保存密码(比如网站用户的密码),你要考虑如何保护这些密码数据,象下面那样直接将密码写入数据库中是极不安全的,因为任何可以打开数据库的人,都将可以直接看到这些密码。

解决的办法是将密码加密后再存储进数据库,比较常用的加密方法是使用哈希函数(Hash Function)。哈希函数的具体定义,大家可以在网上或者相关书籍中查阅到,简单地说,它的特性如下:

(1)原始密码经哈希函数计算后得到一个哈希值

(2)改变原始密码,哈希函数计算出的哈希值也会相应改变

(3) 同样的密码,哈希值也是相同的

(4) 哈希函数是单向、不可逆的。也就是说从哈希值,你无法推算出原始的密码是多少

有了哈希函数,我们就可以将密码的哈希值存储进数据库。用户登录网站的时候,我们可以检验用户输入密码的哈希值是否与数据库中的哈希值相同。

由于哈希函数是不可逆的,即使有人打开了数据库,也无法看到用户的密码是多少。

那么存储经过哈希函数加密后的密码是否就是安全的了呢?我们先来看一下几种常见的破解密码的方法。

(二) 几种常见的破解密码的方法

最简单、常见的破解方式当属字典破解(Dictionary Attack)和暴力破解(Brute Force Attack)方式。这两种方法说白了就是猜密码。

字典破解和暴力破解都是效率比较低的破解方式。如果你知道了数据库中密码的哈希值,你就可以采用一种更高效的破解方式,查表法(Lookup Tables)。还有一些方法,比如逆向查表法(Reverse Lookup Tables)、彩虹表(Rainbow Tables)等,都和查表法大同小异。现在我们来看一下查表法的原理。

查表法不像字典破解和暴力破解那样猜密码,它首先将一些比较常用的密码的哈希值算好,然后建立一张表,当然密码越多,这张表就越大。当你知道某个密码的哈希值时,你只需要在你建立好的表中查找该哈希值,如果找到了,你就知道对应的密码了。

(三) 为密码加盐(Salt)

从上面的查表法可以看出,即便是将原始密码加密后的哈希值存储在数据库中依然是不够安全的。那么有什么好的办法来解决这个问题呢?答案是加盐。

盐(Salt)是什么?就是一个随机生成的字符串。我们将盐与原始密码连接(concat)在一起(放在前面或后面都可以),然后将concat后的字符串加密。采用这种方式加密密码,查表法就不灵了(因为盐是随机生成的)。

(四) 在.NET中的实现

在.NET中,生成盐可以使用RNGCryptoServiceProvider类,当然也可以使用GUID。哈希函数的算法我们可以使用SHA(Secure Hash Algorithm)家族算法,当然哈希函数的算法有很多,比如你也可以采用MD5。这里顺便提一下,美国政府以前广泛采用SHA-1算法,在2005年被我国山东大学的王小云教授发现了安全漏洞,所以现在比较常用SHA-1加长的变种,比如SHA-256。在.NET中,可以使用SHA256Managed类。

下面来看一段代码演示如何在.NET中实现给密码加盐加密。加密后的密码保存在MySQL数据库中。

下面的代码演示如何注册一个新帐户。盐的生成可以使用新Guid,也可以使用RNGCryptoServiceProvider 类。将byte[]转换为string,可以使用Base64String,也可以使用下面的ToHexString方法。

protected void ButtonRegister_Click(object sender, EventArgs e) 
{ 
 string username = TextBoxUserName.Text; 
 string password = TextBoxPassword.Text; 
 // random salt 
 string salt = Guid.NewGuid().ToString();
 // random salt 
 // you can also use RNGCryptoServiceProvider class  
 //System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); 
 //byte[] saltBytes = new byte[36]; 
 //rng.GetBytes(saltBytes); 
 //string salt = Convert.ToBase64String(saltBytes); 
 //string salt = ToHexString(saltBytes); 
 byte[] passwordAndSaltBytes = System.Text.Encoding.UTF8.GetBytes(password + salt);  
 byte[] hashBytes = new System.Security.Cryptography.SHA256Managed().ComputeHash(passwordAndSaltBytes);
 string hashString = Convert.ToBase64String(hashBytes);
 // you can also use ToHexString to convert byte[] to string 
 //string hashString = ToHexString(hashBytes); 
 var db = new TestEntities(); 
 usercredential newRecord = usercredential.Createusercredential(username, hashString, salt); 
 db.usercredentials.AddObject(newRecord); 
 db.SaveChanges(); 
}
string ToHexString(byte[] bytes) 
{ 
 var hex = new StringBuilder(); 
 foreach (byte b in bytes) 
 { 
 hex.AppendFormat("{0:x2}", b); 
 } 
 return hex.ToString(); 
} 

下面的代码演示了如何检验登录用户的密码是否正确。首先检验用户名是否存在,如果存在,获得该用户的盐,然后用该盐和用户输入的密码来计算哈希值,并和数据库中的哈希值进行比较。

protected void ButtonSignIn_Click(object sender, EventArgs e) 
{ 
 string username = TextBoxUserName.Text; 
 string password = TextBoxPassword.Text;
 var db = new TestEntities(); 
 usercredential record = db.usercredentials.Where(x => string.Compare(x.UserName, username, true) == 0).FirstOrDefault(); 
 if (record == default(usercredential)) 
 { 
 throw new ApplicationException("invalid user name and password"); 
 }
 string salt = record.Salt; 
 byte[] passwordAndSaltBytes = System.Text.Encoding.UTF8.GetBytes(password + salt); 
 byte[] hashBytes = new System.Security.Cryptography.SHA256Managed().ComputeHash(passwordAndSaltBytes); 
 string hashString = Convert.ToBase64String(hashBytes);

 if (hashString == record.PasswordHash) 
 { 
 // user login successfully 
 } 
 else 
 { 
 throw new ApplicationException("invalid user name and password"); 
 } 
}

总结:单单使用哈希函数来为密码加密是不够的,需要为密码加盐来提高安全性,盐的长度不能过短,并且盐的产生应该是随机的。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持小牛知识库!

 类似资料:
  • 问题内容: 我试图弄清楚如何使用crypto模块对nodejs中的密码进行加盐和哈希处理。我可以这样做来创建哈希密码: 但是我对以后如何验证密码感到困惑。 问题答案: 在您使用的任何持久性机制(数据库)中,您都将在哈希值和迭代次数之间存储结果哈希,这两者都是纯文本。如果每个密码使用不同的盐(您应该这样做),则还必须保存该信息。 然后,您将比较新的纯文本密码,使用相同的盐(和迭代次数)对其进行哈希处

  • 问题内容: 目前,据说MD5部分不安全。考虑到这一点,我想知道使用哪种机制进行密码保护。 这个问题,“双重哈希”密码是否比仅hash一次密码安全?建议多次散列可能是一个好主意,而如何对单个文件实施密码保护建议使用盐。 我正在使用PHP。我想要一个安全,快速的密码加密系统。将密码hash一百万次可能更安全,但也更慢。如何在速度和安全性之间取得良好的平衡?另外,我希望结果具有恒定数量的字符。 hash

  • 我想使用 scrypt 为用户的密码和盐创建哈希。我找到了两个参考资料,但有些地方我不明白。 他们使用scrypt加密和解密功能。一个加密随机字符串,另一个加密盐(这看起来是错误的,因为只有密码而不是盐用于解密)。看起来解密功能用于验证密码/盐作为解密的副作用。 根据我对的了解,我想要的是一个密钥派生函数(KDF)而不是加密/解密,并且KDF很可能是由scrypt生成并使用的加密/解密。实际的KD

  • 我正在使用以下代码创建哈希密码和salt: 我正在数据库中存储HashedPassword和Salt。 现在我要验证用户登录时的密码: 这不起作用,我得到了一个完全不同的哈希密码,而不是存储在数据库中的密码。据我所知,您应该在用户登录时输入的密码之前预置salt,然后运行相同的哈希密码函数。上面不是等价于那个吗?

  • 我想得到的是 使用libnail加密密码salt 我有一个列表的盐,我想用它来加密/解密我的密码。当我加密密码时,我得到一个哈希返回,这样一个似乎可以工作,但在解密时,我总是得到假返回值。 我是否使用了错误的方法对libnaude进行加密/解密,还是完全朝着错误的方向行驶? 我的加密/解密源: 我感谢任何帮助! 多姆

  • 我使用以下方法从NodeJS中的crypto lib创建加盐和散列密码: 对于randomBytes调用(创建SALT)我应该使用多大的大小?我听说过128位的盐,可能最多256位。看起来这个函数使用的是字节大小,那么我可以假设32(256位)的大小就足够了吗? 对于pbkdf2调用,什么是好的迭代次数,什么是好的密钥长度(keylen)?