ASP.NET Core 2.0中生成1000次迭代SHA256哈希的问题求助
问题排查及修复方案
核心问题点
- 新旧实现逻辑不匹配:旧实现每次调用都会生成随机128位盐,且每轮迭代都会将上一轮哈希结果与盐拼接后再做哈希;新实现完全没有加盐逻辑,输入源存在本质差异,生成结果必然不一致。同时旧实现因为每次盐随机,哪怕输入相同密码、相同迭代次数,每次调用返回的结果也会不同,且未将盐与最终哈希共同存储,后续根本无法完成哈希验证,本身存在逻辑缺陷。
- 迭代计算规则不一致:旧实现每轮迭代的输入是「上一轮哈希字节的Base64编码 + 盐字节的Base64编码」转UTF8字节;新实现每轮迭代的输入只有上一轮哈希结果的Base64编码转UTF8字节,输入内容不同,哈希结果自然不符。
- 加密实例的冗余创建:新实现每次调用
CreateSHA256Hash都会新建SHA256实例,虽然不影响算法结果,但存在不必要的性能开销。
修复方案
方案1:兼容旧逻辑的调整实现
如果需要和旧逻辑生成的哈希对齐,需要保留加盐逻辑,同时将盐与最终哈希共同存储方便后续验证,示例代码如下:
using System.Security.Cryptography; using System.Text; // 输出格式:$"盐Base64|最终哈希Base64",方便后续拆分验证 public static string CreateSHA256HashWithIteration(string password, int iterations = 1000) { try { // 生成128位随机盐 byte[] saltArr = new byte[128 / 8]; using (var rng = RandomNumberGenerator.Create()) { rng.GetNonZeroBytes(saltArr); } using (var sha256 = SHA256.Create()) { byte[] buffer = Encoding.UTF8.GetBytes(password); for (int i = 0; i < iterations; i++) { // 按旧逻辑拼接上一轮哈希Base64 + 盐Base64 string combineStr = Convert.ToBase64String(buffer) + Convert.ToBase64String(saltArr); buffer = Encoding.UTF8.GetBytes(combineStr); buffer = sha256.ComputeHash(buffer); } // 返回盐+哈希,方便后续验证 return $"{Convert.ToBase64String(saltArr)}|{Convert.ToBase64String(buffer)}"; } } catch { return "Exception"; } } // 验证哈希的配套方法 public static bool VerifySHA256Hash(string password, string storedHash, int iterations = 1000) { try { var parts = storedHash.Split('|'); if (parts.Length != 2) return false; byte[] saltArr = Convert.FromBase64String(parts[0]); byte[] storedHashBytes = Convert.FromBase64String(parts[1]); using (var sha256 = SHA256.Create()) { byte[] buffer = Encoding.UTF8.GetBytes(password); for (int i = 0; i < iterations; i++) { string combineStr = Convert.ToBase64String(buffer) + Convert.ToBase64String(saltArr); buffer = Encoding.UTF8.GetBytes(combineStr); buffer = sha256.ComputeHash(buffer); } return buffer.SequenceEqual(storedHashBytes); } } catch { return false; } }
方案2:更安全的官方推荐实现
手动实现迭代哈希容易出现逻辑漏洞,ASP.NET Core 2.0 推荐直接使用内置的Rfc2898DeriveBytes类实现PBKDF2标准的密码哈希,示例如下:
using System.Security.Cryptography; using System.Text; public static string HashPassword(string password, int iterations = 1000) { byte[] salt = new byte[16]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(salt); } var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, iterations, HashAlgorithmName.SHA256, 32); return $"{Convert.ToBase64String(salt)}|{iterations}|{Convert.ToBase64String(hash)}"; } public static bool VerifyPassword(string password, string storedHash) { var parts = storedHash.Split('|'); if (parts.Length != 3) return false; byte[] salt = Convert.FromBase64String(parts[0]); int iterations = int.Parse(parts[1]); byte[] storedHashBytes = Convert.FromBase64String(parts[2]); var computedHash = Rfc2898DeriveBytes.Pbkdf2(password, salt, iterations, HashAlgorithmName.SHA256, 32); return computedHash.SequenceEqual(storedHashBytes); }
内容的提问来源于stack exchange,提问作者Jitendra Pancholi
相关产品推荐
相关产品推荐

