using System.Security.Cryptography; using System.Text; namespace Service.Implement { /// /// 密码哈希工具(PBKDF2-SHA256,格式 salt$hash,盐随机 16 字节) /// public static class PasswordHelper { private const int Iterations = 10_000; private const int SaltSize = 16; private const int HashSize = 32; /// 生成密码哈希(salt$hash) public static string Hash(string password) { var salt = RandomNumberGenerator.GetBytes(SaltSize); var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, HashAlgorithmName.SHA256, HashSize); return $"{Convert.ToHexString(salt)}${Convert.ToHexString(hash)}"; } /// 校验密码是否匹配 public static bool Verify(string password, string? stored) { if (string.IsNullOrWhiteSpace(stored)) return false; var parts = stored.Split('$'); if (parts.Length != 2) return false; var salt = Convert.FromHexString(parts[0]); var expected = Convert.FromHexString(parts[1]); var actual = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, HashAlgorithmName.SHA256, expected.Length); return CryptographicOperations.FixedTimeEquals(actual, expected); } /// 生成随机盐字符串(供外部使用) public static string NewSalt() { var salt = RandomNumberGenerator.GetBytes(SaltSize); return Convert.ToHexString(salt).ToLowerInvariant(); } /// SHA256(供 RefreshToken 等场景) public static string Sha256(string input) { var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(input)); return Convert.ToHexString(bytes).ToLowerInvariant(); } } }