PasswordHash.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace MediaBrowser.Model.Cryptography
  5. {
  6. public class PasswordHash
  7. {
  8. //Defined from this hash storage spec
  9. //https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md
  10. //$<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
  11. private string id;
  12. private Dictionary<string, string> parameters = new Dictionary<string, string>();
  13. private string salt;
  14. private byte[] saltBytes;
  15. private string hash;
  16. private byte[] hashBytes;
  17. public string Id { get => id; set => id = value; }
  18. public Dictionary<string, string> Parameters { get => parameters; set => parameters = value; }
  19. public string Salt { get => salt; set => salt = value; }
  20. public byte[] SaltBytes { get => saltBytes; set => saltBytes = value; }
  21. public string Hash { get => hash; set => hash = value; }
  22. public byte[] HashBytes { get => hashBytes; set => hashBytes = value; }
  23. public PasswordHash(string storageString)
  24. {
  25. string[] splitted = storageString.Split('$');
  26. id = splitted[1];
  27. if (splitted[2].Contains("="))
  28. {
  29. foreach (string paramset in (splitted[2].Split(',')))
  30. {
  31. if (!string.IsNullOrEmpty(paramset))
  32. {
  33. string[] fields = paramset.Split('=');
  34. if (fields.Length == 2)
  35. {
  36. parameters.Add(fields[0], fields[1]);
  37. }
  38. else
  39. {
  40. throw new Exception($"Malformed parameter in password hash string {paramset}");
  41. }
  42. }
  43. }
  44. if (splitted.Length == 5)
  45. {
  46. salt = splitted[3];
  47. saltBytes = ConvertFromByteString(salt);
  48. hash = splitted[4];
  49. hashBytes = ConvertFromByteString(hash);
  50. }
  51. else
  52. {
  53. salt = string.Empty;
  54. hash = splitted[3];
  55. hashBytes = ConvertFromByteString(hash);
  56. }
  57. }
  58. else
  59. {
  60. if (splitted.Length == 4)
  61. {
  62. salt = splitted[2];
  63. saltBytes = ConvertFromByteString(salt);
  64. hash = splitted[3];
  65. hashBytes = ConvertFromByteString(hash);
  66. }
  67. else
  68. {
  69. salt = string.Empty;
  70. hash = splitted[2];
  71. hashBytes = ConvertFromByteString(hash);
  72. }
  73. }
  74. }
  75. public PasswordHash(ICryptoProvider cryptoProvider)
  76. {
  77. id = cryptoProvider.DefaultHashMethod;
  78. saltBytes = cryptoProvider.GenerateSalt();
  79. salt = ConvertToByteString(SaltBytes);
  80. }
  81. public static byte[] ConvertFromByteString(string byteString)
  82. {
  83. List<byte> Bytes = new List<byte>();
  84. for (int i = 0; i < byteString.Length; i += 2)
  85. {
  86. Bytes.Add(Convert.ToByte(byteString.Substring(i, 2),16));
  87. }
  88. return Bytes.ToArray();
  89. }
  90. public static string ConvertToByteString(byte[] bytes)
  91. {
  92. return BitConverter.ToString(bytes).Replace("-", "");
  93. }
  94. private string SerializeParameters()
  95. {
  96. string ReturnString = string.Empty;
  97. foreach (var KVP in parameters)
  98. {
  99. ReturnString += $",{KVP.Key}={KVP.Value}";
  100. }
  101. if ((!string.IsNullOrEmpty(ReturnString)) && ReturnString[0] == ',')
  102. {
  103. ReturnString = ReturnString.Remove(0, 1);
  104. }
  105. return ReturnString;
  106. }
  107. public override string ToString()
  108. {
  109. string outString = "$" +id;
  110. string paramstring = SerializeParameters();
  111. if (!string.IsNullOrEmpty(paramstring))
  112. {
  113. outString += $"${paramstring}";
  114. }
  115. if (!string.IsNullOrEmpty(salt))
  116. {
  117. outString += $"${salt}";
  118. }
  119. outString += $"${hash}";
  120. return outString;
  121. }
  122. }
  123. }