using System.Security.Cryptography; using System.Text; namespace FutureMailAPI.Helpers { public interface IPasswordHelper { string HashPassword(string password); bool VerifyPassword(string password, string hash); bool VerifyPassword(string password, string hash, string salt); string HashPassword(string password, string salt); string GenerateSalt(); } public class PasswordHelper : IPasswordHelper { public string HashPassword(string password) { // 生成随机盐值 byte[] salt; new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]); // 使用PBKDF2算法生成哈希 var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000); byte[] hash = pbkdf2.GetBytes(20); // 组合盐值和哈希值 byte[] hashBytes = new byte[36]; Array.Copy(salt, 0, hashBytes, 0, 16); Array.Copy(hash, 0, hashBytes, 16, 20); // 转换为Base64字符串 return Convert.ToBase64String(hashBytes); } public string GenerateSalt() { // 生成随机盐值 byte[] salt; new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]); // 转换为Base64字符串 return Convert.ToBase64String(salt); } public string HashPassword(string password, string salt) { // 将Base64盐值转换为字节数组 byte[] saltBytes = Convert.FromBase64String(salt); // 使用PBKDF2算法生成哈希 var pbkdf2 = new Rfc2898DeriveBytes(password, saltBytes, 10000); byte[] hash = pbkdf2.GetBytes(20); // 转换为Base64字符串 return Convert.ToBase64String(hash); } public bool VerifyPassword(string password, string hash) { try { // 检查哈希长度,判断是完整哈希(36字节)还是纯哈希(20字节) byte[] hashBytes = Convert.FromBase64String(hash); if (hashBytes.Length == 36) { // 完整哈希格式:盐值(16字节) + 哈希值(20字节) byte[] salt = new byte[16]; Array.Copy(hashBytes, 0, salt, 0, 16); // 使用相同的盐值计算密码的哈希 var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000); byte[] computedHash = pbkdf2.GetBytes(20); // 比较两个哈希值 for (int i = 0; i < 20; i++) { if (hashBytes[i + 16] != computedHash[i]) return false; } return true; } else if (hashBytes.Length == 20) { // 纯哈希格式:只有哈希值(20字节) // 这种情况下,我们需要从数据库中获取盐值 // 但由于VerifyPassword方法没有盐值参数,我们无法验证这种格式 // 返回false表示验证失败 return false; } else { // 未知的哈希格式 return false; } } catch { return false; } } public bool VerifyPassword(string password, string hash, string salt) { try { // 使用提供的盐值计算密码的哈希 var computedHash = HashPassword(password, salt); return hash == computedHash; } catch { return false; } } } }