You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PasswordHash.cs 3.1KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. using System;
  2. using System.Security.Cryptography;
  3. namespace iiie.Authentication.Business.JWT
  4. {
  5. /// <summary>
  6. /// Hash generator for passwords
  7. /// </summary>
  8. public static class PasswordHash
  9. {
  10. private const int ITERATION_INDEX = 0;
  11. private const int SALT_INDEX = 1;
  12. private const int PBKDF2_INDEX = 2;
  13. private const int SALT_BYTE_SIZE = 24;
  14. private const int HASH_BYTE_SIZE = 42;
  15. private const int PBKDF2_ITERATIONS = 2048;
  16. /// <summary>
  17. /// Crée un hash à partir du password
  18. /// </summary>
  19. /// <param name="password">le password à hasher</param>
  20. /// <returns>le hash du password</returns>
  21. public static string CreateHash(string password)
  22. {
  23. // génaration du SALT aléatoire
  24. RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
  25. byte[] salt = new byte[SALT_BYTE_SIZE];
  26. csprng.GetBytes(salt);
  27. // hash le password et création de la chaine avec les paramêtres
  28. byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
  29. return PBKDF2_ITERATIONS + ":" +
  30. Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);
  31. }
  32. /// <summary>
  33. /// Valide le password en adequation avec le hash
  34. /// </summary>
  35. /// <param name="password">le password à vérifier</param>
  36. /// <param name="correctHash">le hash du password stocké en base</param>
  37. /// <returns>True si c'est bon sinon false</returns>
  38. public static bool ValidatePassword(string password, string correctHash)
  39. {
  40. // Extraction des paramêtres du hash
  41. char[] delimiter = { ':' };
  42. string[] split = correctHash.Split(delimiter);
  43. int iterations = Int32.Parse(split[ITERATION_INDEX]);
  44. byte[] salt = Convert.FromBase64String(split[SALT_INDEX]);
  45. byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]);
  46. byte[] testHash = PBKDF2(password, salt, iterations, hash.Length);
  47. return SlowEquals(hash, testHash);
  48. }
  49. private static bool SlowEquals(byte[] a, byte[] b)
  50. {
  51. uint diff = (uint)a.Length ^ (uint)b.Length;
  52. for (int i = 0; i < a.Length && i < b.Length; i++)
  53. diff |= (uint)(a[i] ^ b[i]);
  54. return diff == 0;
  55. }
  56. /// <summary>
  57. /// Calcul le PBKDF2-SHA1 hash du password.
  58. /// </summary>
  59. /// <param name="password">The password à hasher</param>
  60. /// <param name="salt">le salt</param>
  61. /// <param name="iterations">le nombre d'itération</param>
  62. /// <param name="outputBytes">la longueur du hash à générer</param>
  63. /// <returns>le hash du password.</returns>
  64. private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
  65. {
  66. Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
  67. pbkdf2.IterationCount = iterations;
  68. return pbkdf2.GetBytes(outputBytes);
  69. }
  70. }
  71. }