使用MD5或sha-256 C#散列密码

我正在写一个应用程序的registry单,但仍然遇到新的C#的问题。

我正在寻找encryption/哈希密码为MD5或SHA-256,最好是SHA-256。

任何好的例子? 我希望它能够从“string密码”中获取信息 然后将其散列并存储在variables“string hPassword;”中。 有任何想法吗?

不要使用简单的哈希,甚至不要使用哈希。 使用某种encryption技术,比如bcrypt ( 在这里有一个.NET实现 )或者PBKDF2 (有一个内置的实现 )。

这里是一个使用PB​​KDF2的例子。

从密码生成密钥…

string password = GetPasswordFromUserInput(); // specify that we want to randomly generate a 20-byte salt using (var deriveBytes = new Rfc2898DeriveBytes(password, 20)) { byte[] salt = deriveBytes.Salt; byte[] key = deriveBytes.GetBytes(20); // derive a 20-byte key // save salt and key to database } 

然后testing一个密码是否有效…

 string password = GetPasswordFromUserInput(); byte[] salt, key; // load salt and key from database using (var deriveBytes = new Rfc2898DeriveBytes(password, salt)) { byte[] newKey = deriveBytes.GetBytes(20); // derive a 20-byte key if (!newKey.SequenceEqual(key)) throw new InvalidOperationException("Password is invalid!"); } 

你将要使用System.Security.Cryptography命名空间; 特别是MD5类或SHA256类 。

从这个页面上的代码中抽取一点 ,并且知道这两个类具有相同的基类( HashAlgorithm ),那么可以使用如下的函数:

 public string ComputeHash(string input, HashAlgorithm algorithm) { Byte[] inputBytes = Encoding.UTF8.GetBytes(input); Byte[] hashedBytes = algorithm.ComputeHash(inputBytes); return BitConverter.ToString(hashedBytes); } 

那么你可以这样称呼它(对于MD5):

 string hPassword = ComputeHash(password, new MD5CryptoServiceProvider()); 

或者对于SHA256:

 string hPassword = ComputeHash(password, new SHA256CryptoServiceProvider()); 

编辑:添加盐支持
正如dtb在评论中指出的那样,如果它包含添加盐的能力,这个代码将会更强。 如果你对它不熟悉,salt是一组随机位,它被包含在散列函数中,这个散列函数可以防止字典攻击(例如使用彩虹表 )。 下面是一个支持salt的ComputeHash函数的修改版本:

 public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt) { Byte[] inputBytes = Encoding.UTF8.GetBytes(input); // Combine salt and input bytes Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length]; salt.CopyTo(saltedInput, 0); inputBytes.CopyTo(saltedInput, salt.Length); Byte[] hashedBytes = algorithm.ComputeHash(saltedInput); return BitConverter.ToString(hashedBytes); } 

希望这有帮助!

在将数据存储在数据库中时,您应该始终在哈希前encryption密码。

推荐的数据库列:

  • PasswordSalt:int
  • PasswordHash:二进制(20)

你在网上find的大多数post都会讨论关于salt和hash的ASCII编码,但是这不是必须的,只是增加了不必要的计算。 另外,如果使用SHA-1 ,那么输出将只有20个字节,因此数据库中的散列字段只需要20个字节。 我了解你对SHA-256的询问,但除非你有一个令人信服的理由,否则在大多数商业实践中使用带有盐值的SHA-1就足够了。 如果你坚持使用SHA-256,那么数据库中的哈希字段需要是32个字节的长度。

下面是一些函数,它们将生成salt,计算哈希值并validation密码对应的哈希值。

下面的salt函数从4个密码创build的随机字节中产生一个Integer的encryption强盐。

 private int GenerateSaltForPassword() { RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); byte[] saltBytes = new byte[4]; rng.GetNonZeroBytes(saltBytes); return (((int)saltBytes[0]) << 24) + (((int)saltBytes[1]) << 16) + (((int)saltBytes[2]) << 8) + ((int)saltBytes[3]); } 

然后可以使用下面的函数将密码散列。 盐被连接到密码,然后计算散列。

 private byte[] ComputePasswordHash(string password, int salt) { byte[] saltBytes = new byte[4]; saltBytes[0] = (byte)(salt >> 24); saltBytes[1] = (byte)(salt >> 16); saltBytes[2] = (byte)(salt >> 8); saltBytes[3] = (byte)(salt); byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(password); byte[] preHashed = new byte[saltBytes.Length + passwordBytes.Length]; System.Buffer.BlockCopy(passwordBytes, 0, preHashed, 0, passwordBytes.Length); System.Buffer.BlockCopy(saltBytes, 0, preHashed, passwordBytes.Length, saltBytes.Length); SHA1 sha1 = SHA1.Create(); return sha1.ComputeHash(preHashed); } 

检查密码可以简单地通过计算散列,然后将其与期望的散列进行比较来完成。

 private bool IsPasswordValid(string passwordToValidate, int salt, byte[] correctPasswordHash) { byte[] hashedPassword = ComputePasswordHash(passwordToValidate, salt); return hashedPassword.SequenceEqual(correctPasswordHash); } 

如果您要存储哈希密码,请使用bcrypt而不是SHA-256。 问题在于SHA-256的速度是最优化的,如果有人能够访问你的数据库,就会更容易对密码进行暴力攻击。

阅读这篇文章: 足够的彩虹表:你需要知道安全密码scheme和这个答案以前的SO问题。

文章中的一些引用:

问题是MD5很快。 它的现代竞争者也是如此,比如SHA1和SHA256。 速度是现代安全散列的devise目标,因为散列几乎是每个密码系统的构build块,并且通常以每个数据包或每个消息为基础进行需求执行。

速度正是你不想在密码哈希函数。


最后,我们了解到,如果我们想要安全地存储密码,我们有三个合理的选项:PHK的MD5scheme,Provos-Maziere的Bcryptscheme和SRP。 我们了解到正确的select是Bcrypt。

PBKDF2正在使用HMACSHA1 …….如果你想要更现代的HMACSHA256或HMACSHA512的实现,并仍然需要密钥扩展,使algorithm更慢我build议这个API: https ://sourceforge.net/projects/pwdtknet/

这是持久性未知的SecuredPassword类的完整实现

 using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; public class SecuredPassword { private const int saltSize = 256; private readonly byte[] hash; private readonly byte[] salt; public byte[] Hash { get { return hash; } } public byte[] Salt { get { return salt; } } public SecuredPassword(string plainPassword) { if (string.IsNullOrWhiteSpace(plainPassword)) return; using (var deriveBytes = new Rfc2898DeriveBytes(plainPassword, saltSize)) { salt = deriveBytes.Salt; hash = deriveBytes.GetBytes(saltSize); } } public SecuredPassword(byte[] hash, byte[] salt) { this.hash = hash; this.salt = salt; } public bool Verify(string password) { if (string.IsNullOrWhiteSpace(password)) return false; using (var deriveBytes = new Rfc2898DeriveBytes(password, salt)) { byte[] newKey = deriveBytes.GetBytes(saltSize); return newKey.SequenceEqual(hash); } } } 

并testing:

  public class SecuredPasswordTests { [Test] public void IsHashed_AsExpected() { var securedPassword = new SecuredPassword("password"); Assert.That(securedPassword.Hash, Is.Not.EqualTo("password")); Assert.That(securedPassword.Hash.Length, Is.EqualTo(256)); } [Test] public void Generates_Unique_Salt() { var securedPassword = new SecuredPassword("password"); var securedPassword2 = new SecuredPassword("password"); Assert.That(securedPassword.Salt, Is.Not.Null); Assert.That(securedPassword2.Salt, Is.Not.Null); Assert.That(securedPassword.Salt, Is.Not.EqualTo(securedPassword2.Salt)); } [Test] public void Generates_Unique_Hash() { var securedPassword = new SecuredPassword("password"); var securedPassword2 = new SecuredPassword("password"); Assert.That(securedPassword.Hash, Is.Not.Null); Assert.That(securedPassword2.Hash, Is.Not.Null); Assert.That(securedPassword.Hash, Is.Not.EqualTo(securedPassword2.Hash)); } [Test] public void Verify_WhenMatching_ReturnsTrue() { var securedPassword = new SecuredPassword("password"); var result = securedPassword.Verify("password"); Assert.That(result, Is.True); } [Test] public void Verify_WhenDifferent_ReturnsFalse() { var securedPassword = new SecuredPassword("password"); var result = securedPassword.Verify("Password"); Assert.That(result, Is.False); } [Test] public void Verify_WhenRehydrated_AndMatching_ReturnsTrue() { var securedPassword = new SecuredPassword("password123"); var rehydrated = new SecuredPassword(securedPassword.Hash, securedPassword.Salt); var result = rehydrated.Verify("password123"); Assert.That(result, Is.True); } [Test] public void Constructor_Handles_Null_Password() { Assert.DoesNotThrow(() => new SecuredPassword(null)); } [Test] public void Constructor_Handles_Empty_Password() { Assert.DoesNotThrow(() => new SecuredPassword(string.Empty)); } [Test] public void Verify_Handles_Null_Password() { Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(null)); } [Test] public void Verify_Handles_Empty_Password() { Assert.DoesNotThrow(() => new SecuredPassword("password").Verify(string.Empty)); } [Test] public void Verify_When_Null_Password_ReturnsFalse() { Assert.That(new SecuredPassword("password").Verify(null), Is.False); } } 

System.Security.Cryptography.SHA256类应该做的伎俩:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha256.aspx

请使用这个,因为我以前有同样的问题,但可以解决它将利特尔代码片段

  public static string ComputeHash(string input, HashAlgorithm algorithm, Byte[] salt) { Byte[] inputBytes = Encoding.UTF8.GetBytes(input); // Combine salt and input bytes Byte[] saltedInput = new Byte[salt.Length + inputBytes.Length]; salt.CopyTo(saltedInput, 0); inputBytes.CopyTo(saltedInput, salt.Length); Byte[] hashedBytes = algorithm.ComputeHash(saltedInput); StringBuilder hex = new StringBuilder(hashedBytes.Length * 2); foreach (byte b in hashedBytes) hex.AppendFormat("{0:X2}", b); return hex.ToString(); }