using System; using System.Security.Cryptography; namespace iiie.Authentication.Business.JWT { /// /// Hash generator for passwords /// public static class PasswordHash { private const int ITERATION_INDEX = 0; private const int SALT_INDEX = 1; private const int PBKDF2_INDEX = 2; private const int SALT_BYTE_SIZE = 24; private const int HASH_BYTE_SIZE = 42; private const int PBKDF2_ITERATIONS = 2048; /// /// 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[SALT_BYTE_SIZE]; csprng.GetBytes(salt); // hash le password et création de la chaine avec les paramêtres byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE); return PBKDF2_ITERATIONS + ":" + 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[ITERATION_INDEX]); byte[] salt = Convert.FromBase64String(split[SALT_INDEX]); byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]); 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); } } }