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.7KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. using System;
  2. using System.Security.Cryptography;
  3. using System.Text;
  4. namespace iiie.Authentication.Business.JWT
  5. {
  6. /// <summary>
  7. /// Hash generator for passwords
  8. /// </summary>
  9. public static class PasswordHash
  10. {
  11. /// <summary>
  12. /// Compute a random string
  13. /// </summary>
  14. /// <param name="size">The length of the string</param>
  15. /// <returns>A random stirng</returns>
  16. public static string RandomString(int size)
  17. {
  18. var random = new Random((int)DateTime.Now.Ticks);
  19. StringBuilder builder = new StringBuilder();
  20. for (int i = 0; i < size; i++)
  21. builder.Append(Convert.ToChar(Convert.ToInt32(Math.Floor(90 * random.NextDouble() + 33))));
  22. return builder.ToString();
  23. }
  24. /// <summary>
  25. /// Get a random salt
  26. /// </summary>
  27. /// <returns>A salt</returns>
  28. public static string GetSalt()
  29. {
  30. return RandomString(10);
  31. }
  32. /// <summary>
  33. /// Crée un hash à partir du password
  34. /// </summary>
  35. /// <param name="password">le password à hasher</param>
  36. /// <returns>le hash du password</returns>
  37. public static string CreateHash(string password)
  38. {
  39. // génaration du SALT aléatoire
  40. RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
  41. byte[] salt = new byte[AuthProvider.GetPasswordSaltByteSize()];
  42. csprng.GetBytes(salt);
  43. // hash le password et création de la chaine avec les paramêtres
  44. byte[] hash = PBKDF2(password, salt, AuthProvider.GetPasswordIterations(),
  45. AuthProvider.GetPasswordSaltByteSize());
  46. return AuthProvider.GetPasswordIterations() + ":" +
  47. Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);
  48. }
  49. /// <summary>
  50. /// Valide le password en adequation avec le hash
  51. /// </summary>
  52. /// <param name="password">le password à vérifier</param>
  53. /// <param name="correctHash">le hash du password stocké en base</param>
  54. /// <returns>True si c'est bon sinon false</returns>
  55. public static bool ValidatePassword(string password, string correctHash)
  56. {
  57. // Extraction des paramêtres du hash
  58. char[] delimiter = { ':' };
  59. string[] split = correctHash.Split(delimiter);
  60. int iterations = Int32.Parse(split[0]);
  61. byte[] salt = Convert.FromBase64String(split[1]);
  62. byte[] hash = Convert.FromBase64String(split[2]);
  63. byte[] testHash = PBKDF2(password, salt, iterations, hash.Length);
  64. return SlowEquals(hash, testHash);
  65. }
  66. private static bool SlowEquals(byte[] a, byte[] b)
  67. {
  68. uint diff = (uint)a.Length ^ (uint)b.Length;
  69. for (int i = 0; i < a.Length && i < b.Length; i++)
  70. diff |= (uint)(a[i] ^ b[i]);
  71. return diff == 0;
  72. }
  73. /// <summary>
  74. /// Calcul le PBKDF2-SHA1 hash du password.
  75. /// </summary>
  76. /// <param name="password">The password à hasher</param>
  77. /// <param name="salt">le salt</param>
  78. /// <param name="iterations">le nombre d'itération</param>
  79. /// <param name="outputBytes">la longueur du hash à générer</param>
  80. /// <returns>le hash du password.</returns>
  81. private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
  82. {
  83. Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
  84. pbkdf2.IterationCount = iterations;
  85. return pbkdf2.GetBytes(outputBytes);
  86. }
  87. }
  88. }