using System; using System.Security.Cryptography; using System.Text; namespace iiie.Authentication.Business.JWT { /// /// Hash generator for passwords /// public static class PasswordHash { /// /// Compute a random string /// /// The length of the string /// A random stirng public static string RandomString(int size) { var random = new Random((int)DateTime.Now.Ticks); StringBuilder builder = new StringBuilder(); for (int i = 0; i < size; i++) builder.Append(Convert.ToChar(Convert.ToInt32(Math.Floor(90 * random.NextDouble() + 33)))); return builder.ToString(); } /// /// Get a random salt /// /// A salt public static string GetSalt() { return RandomString(10); } /// /// Crée un hash à partir du password /// /// le password à hasher /// le hash du password public static string CreateHash(string password) { // génaration du SALT aléatoire RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider(); byte[] salt = new byte[AuthProvider.GetPasswordSaltByteSize()]; csprng.GetBytes(salt); // hash le password et création de la chaine avec les paramêtres byte[] hash = PBKDF2(password, salt, AuthProvider.GetPasswordIterations(), AuthProvider.GetPasswordSaltByteSize()); return AuthProvider.GetPasswordIterations() + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash); } /// /// Valide le password en adequation avec le hash /// /// le password à vérifier /// le hash du password stocké en base /// True si c'est bon sinon false public static bool ValidatePassword(string password, string correctHash) { // Extraction des paramêtres du hash char[] delimiter = { ':' }; string[] split = correctHash.Split(delimiter); int iterations = Int32.Parse(split[0]); byte[] salt = Convert.FromBase64String(split[1]); byte[] hash = Convert.FromBase64String(split[2]); byte[] testHash = PBKDF2(password, salt, iterations, hash.Length); return SlowEquals(hash, testHash); } 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; } /// /// Calcul le PBKDF2-SHA1 hash du password. /// /// The password à hasher /// le salt /// le nombre d'itération /// la longueur du hash à générer /// le hash du password. 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); } } }