using System.Security.Cryptography; using System.Text; namespace PasswordMigrationUtil { public class PasswordHasherService { private const int SaltSize = 16; // 128 bit private const int HashSize = 32; // 256 bit private const int Iterations = 10000; // Número de iteraciones (ajustable) // Genera un hash y una salt para una contraseña dada public (string hash, string salt) HashPassword(string password) { // Generar una salt aleatoria byte[] saltBytes = RandomNumberGenerator.GetBytes(SaltSize); // Crear el hash usando PBKDF2 var pbkdf2 = new Rfc2898DeriveBytes(password, saltBytes, Iterations, HashAlgorithmName.SHA256); byte[] hashBytes = pbkdf2.GetBytes(HashSize); // Convertir bytes a strings Base64 para almacenamiento string saltString = Convert.ToBase64String(saltBytes); string hashString = Convert.ToBase64String(hashBytes); return (hashString, saltString); } // Verifica si una contraseña coincide con un hash y salt almacenados public bool VerifyPassword(string password, string storedHash, string storedSalt) { try { // Convertir strings Base64 de vuelta a bytes byte[] saltBytes = Convert.FromBase64String(storedSalt); byte[] storedHashBytes = Convert.FromBase64String(storedHash); // Crear el hash de la contraseña ingresada usando la misma salt e iteraciones var pbkdf2 = new Rfc2898DeriveBytes(password, saltBytes, Iterations, HashAlgorithmName.SHA256); byte[] testHashBytes = pbkdf2.GetBytes(HashSize); // Comparar los hashes de forma segura (evita timing attacks) return CryptographicOperations.FixedTimeEquals(storedHashBytes, testHashBytes); } catch (FormatException) { // Manejar el caso donde las strings almacenadas no son Base64 válidas return false; } catch (Exception) { // Loggear la excepción si es necesario return false; } } } }