C# 中的哈希和盐密码
Posted
技术标签:
【中文标题】C# 中的哈希和盐密码【英文标题】:Hash and salt passwords in C# 【发布时间】:2011-01-09 10:53:16 【问题描述】:我刚刚浏览了 DavidHayden 在Hashing User Passwords 上的一篇文章。
我真的无法得到他想要达到的目标。
这是他的代码:
private static string CreateSalt(int size)
//Generate a cryptographic random number.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
// Return a Base64 string representation of the random number.
return Convert.ToBase64String(buff);
private static string CreatePasswordHash(string pwd, string salt)
string saltAndPwd = String.Concat(pwd, salt);
string hashedPwd =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPwd, "sha1");
return hashedPwd;
还有其他 C# 方法可以对密码进行哈希处理并添加盐吗?
【问题讨论】:
这里是一个使用盐进行哈希处理的库encrypto.codeplex.com 第一种生成盐的方法中的size应该传入什么? 链接已损坏。 @ShaneLeBlanc 您应该至少与 has 函数输出一样多的位。SHA1
不是加密级别的,所以你至少应该使用SHA256
,它输出 256 位或 32 字节。但是,256 位不容易转换为 base 64,因为每个 base64 字符编码 6 位,并且 256 不能完全被 6 整除。所以你需要一个公分母 6(对于 base64)和 8(对于一个字节中的位)超过 256 位,即 264 位或 33 字节。 TLDR:使用 33。
^ 来源:crackstation.net/hashing-security.htm
【参考方案1】:
Salt 用于为哈希增加额外的复杂性,使其更难暴力破解。
来自article on Sitepoint:
黑客仍然可以执行 所谓的字典攻击。 恶意方可能会 通过采取字典攻击,对于 例如,他们有 100,000 个密码 知道人们经常使用(例如城市 姓名、运动队等),对它们进行哈希处理, 然后比较每个条目 字典中的每一行 数据库表。如果黑客发现 比赛,宾果!他们有你的密码。 然而,为了解决这个问题,我们 只需要加盐即可。
要对哈希进行加盐,我们只需提出 一串看起来随机的文本, 将其与密码连接起来 由用户提供,然后对两者进行哈希处理 随机生成的字符串和 密码一起作为一个值。我们 然后保存哈希和盐 作为用户中的单独字段 表。
在这种情况下,不仅 黑客需要猜测密码, 他们还得猜盐。 在明文中添加盐可以改善 安全性:现在,如果黑客尝试 字典攻击,他必须散列他的 100,000 个条目,每个条目的盐 用户行。虽然还是 可能,黑客攻击的机会 成功率急剧下降。
.NET 中没有自动执行此操作的方法,因此您将使用上述解决方案。
【讨论】:
盐用于防御彩虹表之类的东西。为了防御字典攻击,像任何好的 KDF 一样需要一个工作因子(也称为密钥拉伸):en.wikipedia.org/wiki/Key_stretching【参考方案2】:实际上这有点奇怪,使用字符串转换 - 成员资格提供程序将它们放入配置文件中。哈希和盐是二进制 blob,除非要将它们放入文本文件,否则无需将它们转换为字符串。
在我的书中,Beginning ASP.NET Security,(哦,终于有了一个拉皮条的借口)我做了以下事情
static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
HashAlgorithm algorithm = new SHA256Managed();
byte[] plainTextWithSaltBytes =
new byte[plainText.Length + salt.Length];
for (int i = 0; i < plainText.Length; i++)
plainTextWithSaltBytes[i] = plainText[i];
for (int i = 0; i < salt.Length; i++)
plainTextWithSaltBytes[plainText.Length + i] = salt[i];
return algorithm.ComputeHash(plainTextWithSaltBytes);
盐生成是问题中的示例。您可以使用Encoding.UTF8.GetBytes(string)
将文本转换为字节数组。如果您必须将哈希转换为其字符串表示,您可以使用Convert.ToBase64String
和Convert.FromBase64String
将其转换回来。
您应该注意,您不能在字节数组上使用相等运算符,它会检查引用,因此您应该简单地遍历两个数组以检查每个字节
public static bool CompareByteArrays(byte[] array1, byte[] array2)
if (array1.Length != array2.Length)
return false;
for (int i = 0; i < array1.Length; i++)
if (array1[i] != array2[i])
return false;
return true;
始终为每个密码使用新盐。盐不必保密,可以与哈希本身一起存储。
【讨论】:
感谢您的建议 - 真的帮助我入门。我还看到了这个链接 dijksterhuis.org/creating-salted-hash-values-in-c>,我发现这是很好的实用建议,并且反映了这篇文章中所说的大部分内容 用于 CompareByteArrays 的漂亮 LINQ 语句重构return array1.Length == array2.Length && !array1.Where((t, i) => t != array2[i]).Any();
@Brettski 从技术上讲,是的,但是为每个用户提供 唯一 盐会呈现彩虹表(通常被认为是破解散列的最有效方法密码)实际上没用。 This is a quick oveview 对如何安全地存储密码,以及为什么/如何工作进行了深入但不是压倒性的概述。
@hunter :您应该添加一个 .ToList() 以使其恒定时间。例如:返回 array1.Length == array2.Length && !array1.Where((t, i) => t != array2[i]).ToList().Any();否则,LINQ 将在发现一个不相等的字节后立即返回。
-1 用于使用快速散列函数。使用慢速结构,如 PBKDF2、bcrypt 或 scrypt。【参考方案3】:
blowdart 所说的,但代码少了一点。使用 Linq 或 CopyTo
连接数组。
public static byte[] Hash(string value, byte[] salt)
return Hash(Encoding.UTF8.GetBytes(value), salt);
public static byte[] Hash(byte[] value, byte[] salt)
byte[] saltedValue = value.Concat(salt).ToArray();
// Alternatively use CopyTo.
//var saltedValue = new byte[value.Length + salt.Length];
//value.CopyTo(saltedValue, 0);
//salt.CopyTo(saltedValue, value.Length);
return new SHA256Managed().ComputeHash(saltedValue);
Linq 也有一种比较字节数组的简单方法。
public bool ConfirmPassword(string password)
byte[] passwordHash = Hash(password, _passwordSalt);
return _passwordHash.SequenceEqual(passwordHash);
然而,在实施任何这些之前,请查看this post。对于密码散列,您可能需要一种慢速散列算法,而不是快速散列算法。
为此,Rfc2898DeriveBytes
类很慢(并且可以变得更慢),并且可以回答原始问题的第二部分,因为它可以接受密码和盐并返回哈希值。有关更多信息,请参阅this question。注意,Stack Exchange is using Rfc2898DeriveBytes
用于密码哈希(源代码here)。
【讨论】:
@MushinNoShin SHA256 是一个快速哈希。密码散列需要慢速散列,如 PBKDF2、bcrypt 或 scrypt。有关详细信息,请参阅 security.se 上的How to securely hash passwords?。【参考方案4】:我一直在读到像 SHA256 这样的散列函数并不是真正用于存储密码的: https://patrickmn.com/security/storing-passwords-securely/#notpasswordhashes
取而代之的是自适应密钥派生函数,如 PBKDF2、bcrypt 或 scrypt。这是 Microsoft 在其 Microsoft.AspNet.Identity 库中为 PasswordHasher 编写的基于 PBKDF2 的代码:
/* =======================
* HASHED PASSWORD FORMATS
* =======================
*
* Version 3:
* PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
* Format: 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey
* (All UInt32s are stored big-endian.)
*/
public string HashPassword(string password)
var prf = KeyDerivationPrf.HMACSHA256;
var rng = RandomNumberGenerator.Create();
const int iterCount = 10000;
const int saltSize = 128 / 8;
const int numBytesRequested = 256 / 8;
// Produce a version 3 (see comment above) text hash.
var salt = new byte[saltSize];
rng.GetBytes(salt);
var subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);
var outputBytes = new byte[13 + salt.Length + subkey.Length];
outputBytes[0] = 0x01; // format marker
WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
WriteNetworkByteOrder(outputBytes, 5, iterCount);
WriteNetworkByteOrder(outputBytes, 9, saltSize);
Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
return Convert.ToBase64String(outputBytes);
public bool VerifyHashedPassword(string hashedPassword, string providedPassword)
var decodedHashedPassword = Convert.FromBase64String(hashedPassword);
// Wrong version
if (decodedHashedPassword[0] != 0x01)
return false;
// Read header information
var prf = (KeyDerivationPrf)ReadNetworkByteOrder(decodedHashedPassword, 1);
var iterCount = (int)ReadNetworkByteOrder(decodedHashedPassword, 5);
var saltLength = (int)ReadNetworkByteOrder(decodedHashedPassword, 9);
// Read the salt: must be >= 128 bits
if (saltLength < 128 / 8)
return false;
var salt = new byte[saltLength];
Buffer.BlockCopy(decodedHashedPassword, 13, salt, 0, salt.Length);
// Read the subkey (the rest of the payload): must be >= 128 bits
var subkeyLength = decodedHashedPassword.Length - 13 - salt.Length;
if (subkeyLength < 128 / 8)
return false;
var expectedSubkey = new byte[subkeyLength];
Buffer.BlockCopy(decodedHashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);
// Hash the incoming password and verify it
var actualSubkey = KeyDerivation.Pbkdf2(providedPassword, salt, prf, iterCount, subkeyLength);
return actualSubkey.SequenceEqual(expectedSubkey);
private static void WriteNetworkByteOrder(byte[] buffer, int offset, uint value)
buffer[offset + 0] = (byte)(value >> 24);
buffer[offset + 1] = (byte)(value >> 16);
buffer[offset + 2] = (byte)(value >> 8);
buffer[offset + 3] = (byte)(value >> 0);
private static uint ReadNetworkByteOrder(byte[] buffer, int offset)
return ((uint)(buffer[offset + 0]) << 24)
| ((uint)(buffer[offset + 1]) << 16)
| ((uint)(buffer[offset + 2]) << 8)
| ((uint)(buffer[offset + 3]));
请注意,这需要安装 Microsoft.AspNetCore.Cryptography.KeyDerivation nuget 包,该包需要 .NET Standard 2.0(.NET 4.6.1 或更高版本)。对于早期版本的 .NET,请参阅 Microsoft 的 System.Web.Helpers 库中的 Crypto 类。
2015 年 11 月更新 更新了使用来自不同 Microsoft 库的实现的答案,该库使用 PBKDF2-HMAC-SHA256 散列而不是 PBKDF2-HMAC-SHA1(如果 iterCount 足够高,请注意 PBKDF2-HMAC-SHA1 为 still secure)。您可以查看复制简化代码的source,因为它实际上处理了从先前答案实现的验证和升级哈希,如果您将来需要增加 iterCount,这很有用。
【讨论】:
请注意,可能值得将 PBKDF2IterCount 增加到更高的数字,请参阅security.stackexchange.com/q/3959 了解更多信息。 1) 将PBKDF2SubkeyLength
减少到 20 个字节。这就是 f SHA1 的自然大小,并且将其增大到超过该值会减慢防御者的速度,而不会减慢攻击者的速度。 2)我建议增加迭代次数。我推荐 10k 到 100k,具体取决于您的性能预算。 3) 恒定的时间比较也没有什么坏处,但没有太大的实际影响。
KeyDerivationPrf、KeyDerivation 和 BlockCopy 未定义,它们的类是什么?
@mrbengi 您是否安装了提到的 Microsoft.AspNet.Cryptography.KeyDerivation nuget 包?如果这不合适,here 是不需要 nuget 包的版本。 Buffer.BlockCopy 应该存在,它是 System 的一部分。
nuget 包现在是 Microsoft.AspNetCore.Cryptography.KeyDerivation。【参考方案5】:
呸,这样更好! http://sourceforge.net/projects/pwdtknet/ 更好,因为.....它执行 Key Stretching 并使用 HMACSHA512 :)
【讨论】:
【参考方案6】:这就是我的做法。我创建哈希并使用ProtectedData
api 存储它:
public static string GenerateKeyHash(string Password)
if (string.IsNullOrEmpty(Password)) return null;
if (Password.Length < 1) return null;
byte[] salt = new byte[20];
byte[] key = new byte[20];
byte[] ret = new byte[40];
try
using (RNGCryptoServiceProvider randomBytes = new RNGCryptoServiceProvider())
randomBytes.GetBytes(salt);
using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
key = hashBytes.GetBytes(20);
Buffer.BlockCopy(salt, 0, ret, 0, 20);
Buffer.BlockCopy(key, 0, ret, 20, 20);
// returns salt/key pair
return Convert.ToBase64String(ret);
finally
if (salt != null)
Array.Clear(salt, 0, salt.Length);
if (key != null)
Array.Clear(key, 0, key.Length);
if (ret != null)
Array.Clear(ret, 0, ret.Length);
public static bool ComparePasswords(string PasswordHash, string Password)
if (string.IsNullOrEmpty(PasswordHash) || string.IsNullOrEmpty(Password)) return false;
if (PasswordHash.Length < 40 || Password.Length < 1) return false;
byte[] salt = new byte[20];
byte[] key = new byte[20];
byte[] hash = Convert.FromBase64String(PasswordHash);
try
Buffer.BlockCopy(hash, 0, salt, 0, 20);
Buffer.BlockCopy(hash, 20, key, 0, 20);
using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
byte[] newKey = hashBytes.GetBytes(20);
if (newKey != null)
if (newKey.SequenceEqual(key))
return true;
return false;
finally
if (salt != null)
Array.Clear(salt, 0, salt.Length);
if (key != null)
Array.Clear(key, 0, key.Length);
if (hash != null)
Array.Clear(hash, 0, hash.Length);
public static byte[] DecryptData(string Data, byte[] Salt)
if (string.IsNullOrEmpty(Data)) return null;
byte[] btData = Convert.FromBase64String(Data);
try
return ProtectedData.Unprotect(btData, Salt, DataProtectionScope.CurrentUser);
finally
if (btData != null)
Array.Clear(btData, 0, btData.Length);
public static string EncryptData(byte[] Data, byte[] Salt)
if (Data == null) return null;
if (Data.Length < 1) return null;
byte[] buffer = new byte[Data.Length];
try
Buffer.BlockCopy(Data, 0, buffer, 0, Data.Length);
return System.Convert.ToBase64String(ProtectedData.Protect(buffer, Salt, DataProtectionScope.CurrentUser));
finally
if (buffer != null)
Array.Clear(buffer, 0, buffer.Length);
【讨论】:
如何在保存和稍后比较时调用它?【参考方案7】:create proc [dbo].[hash_pass] @family nvarchar(50), @username nvarchar(50), @pass nvarchar(Max),``` @semat nvarchar(50), @tell nvarchar(50)
as insert into tbl_karbar values (@family,@username,(select HASHBYTES('SHA1' ,@pass)),@semat,@tell)
【讨论】:
【参考方案8】:在回答原始问题的这一部分“是否有任何其他 C# 方法用于散列密码”您可以使用 ASP.NET Identity v3.0 https://www.nuget.org/packages/Microsoft.AspNet.Identity.EntityFramework/3.0.0-rc1-final
来实现这一点using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using System.Security.Principal;
namespace HashTest
class Program
static void Main(string[] args)
WindowsIdentity wi = WindowsIdentity.GetCurrent();
var ph = new PasswordHasher<WindowsIdentity>();
Console.WriteLine(ph.HashPassword(wi,"test"));
Console.WriteLine(ph.VerifyHashedPassword(wi,"AQAAAAEAACcQAAAAEA5S5X7dmbx/NzTk6ixCX+bi8zbKqBUjBhID3Dg1teh+TRZMkAy3CZC5yIfbLqwk2A==","test"));
【讨论】:
【参考方案9】:我已经创建了一个库SimpleHashing.Net,以便使用 Microsoft 提供的基本类简化散列过程。普通的 SHA 已经不足以安全存储密码了。
该库使用来自 Bcrypt 的哈希格式的想法,但由于没有官方的 MS 实现,我更喜欢使用框架中可用的东西(即 PBKDF2),但开箱即用有点太难了。
这是一个如何使用该库的简单示例:
ISimpleHash simpleHash = new SimpleHash();
// Creating a user hash, hashedPassword can be stored in a database
// hashedPassword contains the number of iterations and salt inside it similar to bcrypt format
string hashedPassword = simpleHash.Compute("Password123");
// Validating user's password by first loading it from database by username
string storedHash = _repository.GetUserPasswordHash(username);
isPasswordValid = simpleHash.Verify("Password123", storedHash);
【讨论】:
【参考方案10】: protected void m_GenerateSHA256_Button1_Click(objectSender, EventArgs e)
string salt =createSalt(10);
string hashedPassword=GenerateSHA256Hash(m_UserInput_TextBox.Text,Salt);
m_SaltHash_TextBox.Text=Salt;
m_SaltSHA256Hash_TextBox.Text=hashedPassword;
public string createSalt(int size)
var rng= new System.Security.Cyptography.RNGCyptoServiceProvider();
var buff= new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
public string GenerateSHA256Hash(string input,string salt)
byte[]bytes=System.Text.Encoding.UTF8.GetBytes(input+salt);
new System.Security.Cyptography.SHA256Managed();
byte[]hash=sha256hashString.ComputedHash(bytes);
return bytesArrayToHexString(hash);
【讨论】:
其他方法是字符串密码=HashPasswordForStoringInConfigFile(TextBox1.Text,SHA1)【参考方案11】:我阅读了所有答案,我认为这些已经足够了,特别是 @Michael 散列缓慢的文章和 @CodesInChaos 好的 cmets,但我决定分享我的代码 sn-p用于可能有用且不需要 [Microsoft.AspNet.Cryptography.KeyDerivation] 的散列/验证。
private static bool SlowEquals(byte[] a, byte[] b)
uint diff = (uint)a.Length ^ (uint)b.Length;
for (int i = 0; i < a.Length && i < b.Length; i++)
diff |= (uint)(a[i] ^ b[i]);
return diff == 0;
private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
pbkdf2.IterationCount = iterations;
return pbkdf2.GetBytes(outputBytes);
private static string CreateHash(string value, int salt_bytes, int hash_bytes, int pbkdf2_iterations)
// Generate a random salt
RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
byte[] salt = new byte[salt_bytes];
csprng.GetBytes(salt);
// Hash the value and encode the parameters
byte[] hash = PBKDF2(value, salt, pbkdf2_iterations, hash_bytes);
//You need to return the salt value too for the validation process
return Convert.ToBase64String(hash) + ":" +
Convert.ToBase64String(hash);
private static bool ValidateHash(string pureVal, string saltVal, string hashVal, int pbkdf2_iterations)
try
byte[] salt = Convert.FromBase64String(saltVal);
byte[] hash = Convert.FromBase64String(hashVal);
byte[] testHash = PBKDF2(pureVal, salt, pbkdf2_iterations, hash.Length);
return SlowEquals(hash, testHash);
catch (Exception ex)
return false;
请注意非常重要的SlowEquals函数,最后,我希望这对您有所帮助,请不要犹豫,告诉我更好的方法。
【讨论】:
与其创建一个繁忙的循环,不如加入一个人为的非繁忙延迟。例如使用任务延迟。这将延迟蛮力尝试,但不会阻塞活动线程。 @gburton 感谢您的建议。我会检查的。 CreateHash 中有一个错字:您将 Convert.ToBase64String(hash) 连接到自身而不是盐。除此之外,这是一个很好的答案,它几乎解决了 cmets 在其他答案中提出的所有问题。【参考方案12】:我创建了一个具有以下方法的类:
创建盐
哈希输入
验证输入
public class CryptographyProcessor
public string CreateSalt(int size)
//Generate a cryptographic random number.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
public string GenerateHash(string input, string salt)
byte[] bytes = Encoding.UTF8.GetBytes(input + salt);
SHA256Managed sHA256ManagedString = new SHA256Managed();
byte[] hash = sHA256ManagedString.ComputeHash(bytes);
return Convert.ToBase64String(hash);
public bool AreEqual(string plainTextInput, string hashedInput, string salt)
string newHashedPin = GenerateHash(plainTextInput, salt);
return newHashedPin.Equals(hashedInput);
【讨论】:
Alegbe 我试过了,但它会为相同的输入生成两个不同的 has 值。【参考方案13】:使用来自 Microsoft 的 System.Web.Helpers.Crypto
NuGet 包。 它会自动将盐添加到哈希中。
您可以像这样散列密码:var hash = Crypto.HashPassword("foo");
您验证这样的密码:var verified = Crypto.VerifyHashedPassword(hash, "foo");
【讨论】:
【参考方案14】:如果您不使用 asp.net 或 .net core,在 >= .Net Standard 2.0 项目中也有一个简单的方法。
首先您可以设置所需的哈希大小、盐和与哈希生成持续时间相关的迭代次数:
private const int SaltSize = 32;
private const int HashSize = 32;
private const int IterationCount = 10000;
要生成密码哈希和盐,您可以使用以下内容:
public static string GeneratePasswordHash(string password, out string salt)
using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, SaltSize))
rfc2898DeriveBytes.IterationCount = IterationCount;
byte[] hashData = rfc2898DeriveBytes.GetBytes(HashSize);
byte[] saltData = rfc2898DeriveBytes.Salt;
salt = Convert.ToBase64String(saltData);
return Convert.ToBase64String(hashData);
要验证用户输入的密码是否有效,您可以检查数据库中的值:
public static bool VerifyPassword(string password, string passwordHash, string salt)
using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, SaltSize))
rfc2898DeriveBytes.IterationCount = IterationCount;
rfc2898DeriveBytes.Salt = Convert.FromBase64String(salt);
byte[] hashData = rfc2898DeriveBytes.GetBytes(HashSize);
return Convert.ToBase64String(hashData) == passwordHash;
下面的单元测试展示了用法:
string password = "MySecret";
string passwordHash = PasswordHasher.GeneratePasswordHash(password, out string salt);
Assert.True(PasswordHasher.VerifyPassword(password, passwordHash, salt));
Assert.False(PasswordHasher.VerifyPassword(password.ToUpper(), passwordHash, salt));
Microsoft Rfc2898DeriveBytes Source
【讨论】:
【参考方案15】:原来的问题已经有了很好的答案,但我想补充一点,“SequenceEqual”可能会导致定时攻击。
检查(字节)序列的正常方法是相同的,是将顺序中的每个字节与第二个字节进行比较。第一个无序的将停止比较并返回“false”。
byte[] hash1 = ...
byte[] hash2 = ...
// can be exploited with a timing attack
bool equals = hash1.SequenceEqual(hash2);
这样,攻击者需要 256 个字符串以及每个可能的起始字节。他针对该机制运行每个字符串,而获得结果所需时间最长的字符串是具有正确第一个字节的字符串。然后可以在下一个字节上以类似的方式继续攻击......等等。
我发现here 是一种更好的方法,并且有很好的解释。
[MethodImpl(MethodImplOptions.NoOptimization)]
private static bool slowEquals(byte[] a, byte[] b)
int diff = a.Length ^ b.Length;
for (int i = 0; i < a.Length && i < b.Length; i++)
diff |= a[i] ^ b[i];
return diff == 0;
【讨论】:
比较字节所花费的时间是整个操作的极小部分。特别是在所有需要获取密码的网络请求的情况下。即使在非常罕见的情况下,操作如此一致以至于这甚至是可能的,它也需要大量和大量的尝试。很多很多。在这一点上,人们猜测和检查的风险比使用这样的东西要高得多(因此为什么你无论如何都需要限制密码请求。这不是一个真正的问题..【参考方案16】:public static class Salt
public static string GenerateHash(string input)
byte[] salt = new byte[128 / 8];
using (var rngCsp = new RNGCryptoServiceProvider())
rngCsp.GetNonZeroBytes(salt);
byte[] bytes = Encoding.UTF8.GetBytes(input);
SHA256Managed sHA256ManagedString = new SHA256Managed();
byte[] hash = sHA256ManagedString.ComputeHash(bytes);
string baseHashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: input,
salt: hash,
prf: KeyDerivationPrf.HMACSHA256,
iterationCount: 10000,
numBytesRequested: 256 / 8)) ;
return string.Format($"MYHASHED@",baseHashed,salt);
public static bool AreEqual(string plainTextInput, string hashedInput)
string newHashedPin = GenerateHash(plainTextInput);
return newHashedPin.Equals(hashedInput);
【讨论】:
正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。以上是关于C# 中的哈希和盐密码的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Python 中使用 scrypt 生成密码和盐的哈希值