我正在为一个应用程序编写一个注册表单,但仍然遇到了新的c#问题.
我希望加密/哈希密码到md5或sha-256,最好是sha-256.
有什么好例子吗?我希望它能够从"字符串密码"中获取信息; 然后散列它并存储在变量"string hPassword;"中.有任何想法吗?
Luk*_*keH 78
不要使用简单的哈希,甚至是盐渍哈希.使用某种密钥加强技术,如bcrypt(此处使用.NET实现)或PBKDF2(具有内置实现).
这是使用PBKDF2的示例.
从密码生成密钥...
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
}
Run Code Online (Sandbox Code Playgroud)
然后测试密码是否有效......
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!");
}
Run Code Online (Sandbox Code Playgroud)
Don*_*nut 54
您将要使用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);
}
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样调用它(对于MD5):
string hPassword = ComputeHash(password, new MD5CryptoServiceProvider());
Run Code Online (Sandbox Code Playgroud)
或者对于SHA256:
string hPassword = ComputeHash(password, new SHA256CryptoServiceProvider());
Run Code Online (Sandbox Code Playgroud)
编辑:添加Salt支持
正如dtb在注释中指出的那样,如果它包含添加salt的能力,则此代码会更强.如果你不熟悉它,salt是一组随机位,它们被包含作为散列函数的输入,这对于阻止对散列密码的字典攻击(例如,使用彩虹表)有很长的路要走.这ComputeHash是支持salt 的函数的修改版本:
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);
}
Run Code Online (Sandbox Code Playgroud)
希望这对你有所帮助!
在将数据存储在数据库中时,应始终在密码之前对密码进行加密.
推荐的数据库列:
您在网上找到的大多数帖子都会讨论对salt和hash进行ASCII编码,但这不是必需的,只会添加不需要的计算.此外,如果使用SHA-1,则输出将只有20个字节,因此数据库中的哈希字段长度只需要20个字节.我理解你对SHA-256的询问,但除非你有令人信服的理由,否则在大多数商业实践中使用带盐值的SHA-1就足够了.如果坚持使用SHA-256,则数据库中的哈希字段长度必须为32个字节.
下面是一些将生成salt,计算哈希值并根据密码验证哈希值的函数.
下面的salt函数从4个加密创建的随机字节生成加密强盐作为整数.
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]);
}
Run Code Online (Sandbox Code Playgroud)
然后可以使用带有以下功能的salt对密码进行哈希处理.将salt连接到密码,然后计算哈希值.
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);
}
Run Code Online (Sandbox Code Playgroud)
只需计算哈希值,然后将其与预期的哈希值进行比较,即可检查密码.
private bool IsPasswordValid(string passwordToValidate, int salt, byte[] correctPasswordHash)
{
byte[] hashedPassword = ComputePasswordHash(passwordToValidate, salt);
return hashedPassword.SequenceEqual(correctPasswordHash);
}
Run Code Online (Sandbox Code Playgroud)
TL;DR 使用Microsoft.AspNetCore.Cryptography.KeyDerivation,使用 SHA-512 实现 PBKDF2。
开始使用密码散列的好主意是查看OWASP 指南的规定。推荐的算法列表包括 Argon2、PBKDF2、scrypt 和 bcrypt。所有这些算法都可以调整,以调整散列密码所需的时间,以及相应地通过暴力破解密码的时间。所有这些算法都利用盐来防止彩虹表攻击。
这些算法都不是很弱,但是有一些区别:
仅基于算法,我可能会选择 bcrypt,PBKDF2 是最不受欢迎的。
然而,这并不是故事的全部,因为即使是最好的算法也可能因为糟糕的实现而变得不安全。让我们看看 .NET 平台有哪些功能:
Rfc2898DeriveBytes. 这里最大的优势是该实现是由 Microsoft 提供的,虽然我无法正确评估 Microsoft 开发人员与 BCrypt.net 或 libsodium 开发人员的加密勤奋,但信任它是有意义的,因为如果您正在运行 .NET 应用程序,已经严重依赖微软了。如果发现安全问题,我们也可能期望微软发布更新。希望。总结到目前为止的研究,虽然 PBKDF2 可能是四种算法中最不受欢迎的算法,但 Microsoft 提供的实现的可用性胜过这一点,因此合理的决定是使用Microsoft.AspNetCore.Cryptography.KeyDerivation.
目前最新的包面向 .NET Standard 2.0,因此可在 .NET Core 2.0 或 .NET Framework 4.6.1 或更高版本中使用。如果您使用早期的框架版本,则可以使用该包的早期版本1.1.3,它面向 .NET Framework 4.5.1 或 .NET Core 1.0。不幸的是,甚至无法在 .NET 的早期版本中使用它。
文档和工作示例可在learn.microsoft.com上获取。但是,不要按原样复制粘贴,开发人员仍然需要做出决定。
第一个决定是使用什么哈希函数。可用选项包括 SHA-1、SHA-256 和 SHA-512。其中,SHA-1 绝对太快而无法保证安全,SHA-256 还不错,但我会推荐 SHA-512,因为据说它的 64 位操作使用使得更难从基于 GPU 的攻击中受益。
然后,您需要选择密码哈希输出长度和盐长度。输出长于哈希函数输出(例如 SHA-512 的 512 位)是没有意义的,并且完全像这样可能是最安全的。对于盐的长度,意见不一。128 位应该足够了,但无论如何,长度超过哈希输出长度肯定不会带来任何好处。
接下来是迭代计数。它越大,密码哈希就越难破解,但用户登录所需的时间就越长。我建议选择它,这样哈希在典型的生产系统上需要 0.25 - 1 秒,并且在任何情况下,它不应低于10000。
通常,您会得到字节数组作为盐和哈希值。使用 Base64 将它们转换为字符串。您可以选择在数据库中使用两个不同的列,或者使用 Base64 中未遇到的分隔符将盐和密码合并在一列中。
不要忘记设计密码哈希存储,以便将来无缝地转向更好的哈希算法。