PasswordHash.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. // with one slight amendment to ease the transition, we're writing out the bytes in hex
  12. // rather than making them a BASE64 string with stripped padding
  13. private string _id;
  14. private Dictionary<string, string> _parameters = new Dictionary<string, string>();
  15. private string _salt;
  16. private byte[] _saltBytes;
  17. private string _hash;
  18. private byte[] _hashBytes;
  19. public string Id { get => _id; set => _id = value; }
  20. public Dictionary<string, string> Parameters { get => _parameters; set => _parameters = value; }
  21. public string Salt { get => _salt; set => _salt = value; }
  22. public byte[] SaltBytes { get => _saltBytes; set => _saltBytes = value; }
  23. public string Hash { get => _hash; set => _hash = value; }
  24. public byte[] HashBytes { get => _hashBytes; set => _hashBytes = value; }
  25. public PasswordHash(string storageString)
  26. {
  27. string[] splitted = storageString.Split('$');
  28. _id = splitted[1];
  29. if (splitted[2].Contains("="))
  30. {
  31. foreach (string paramset in (splitted[2].Split(',')))
  32. {
  33. if (!string.IsNullOrEmpty(paramset))
  34. {
  35. string[] fields = paramset.Split('=');
  36. if (fields.Length == 2)
  37. {
  38. _parameters.Add(fields[0], fields[1]);
  39. }
  40. else
  41. {
  42. throw new Exception($"Malformed parameter in password hash string {paramset}");
  43. }
  44. }
  45. }
  46. if (splitted.Length == 5)
  47. {
  48. _salt = splitted[3];
  49. _saltBytes = ConvertFromByteString(_salt);
  50. _hash = splitted[4];
  51. _hashBytes = ConvertFromByteString(_hash);
  52. }
  53. else
  54. {
  55. _salt = string.Empty;
  56. _hash = splitted[3];
  57. _hashBytes = ConvertFromByteString(_hash);
  58. }
  59. }
  60. else
  61. {
  62. if (splitted.Length == 4)
  63. {
  64. _salt = splitted[2];
  65. _saltBytes = ConvertFromByteString(_salt);
  66. _hash = splitted[3];
  67. _hashBytes = ConvertFromByteString(_hash);
  68. }
  69. else
  70. {
  71. _salt = string.Empty;
  72. _hash = splitted[2];
  73. _hashBytes = ConvertFromByteString(_hash);
  74. }
  75. }
  76. }
  77. public PasswordHash(ICryptoProvider cryptoProvider)
  78. {
  79. _id = cryptoProvider.DefaultHashMethod;
  80. _saltBytes = cryptoProvider.GenerateSalt();
  81. _salt = ConvertToByteString(SaltBytes);
  82. }
  83. public static byte[] ConvertFromByteString(string byteString)
  84. {
  85. byte[] bytes = new byte[byteString.Length / 2];
  86. for (int i = 0; i < byteString.Length; i += 2)
  87. {
  88. // TODO: NetStandard2.1 switch this to use a span instead of a substring.
  89. bytes[i / 2] = Convert.ToByte(byteString.Substring(i, 2), 16);
  90. }
  91. return bytes;
  92. }
  93. public static string ConvertToByteString(byte[] bytes)
  94. {
  95. return BitConverter.ToString(bytes).Replace("-", "");
  96. }
  97. private string SerializeParameters()
  98. {
  99. string returnString = string.Empty;
  100. foreach (var KVP in _parameters)
  101. {
  102. returnString += $",{KVP.Key}={KVP.Value}";
  103. }
  104. if ((!string.IsNullOrEmpty(returnString)) && returnString[0] == ',')
  105. {
  106. returnString = returnString.Remove(0, 1);
  107. }
  108. return returnString;
  109. }
  110. public override string ToString()
  111. {
  112. string outString = "$" + _id;
  113. string paramstring = SerializeParameters();
  114. if (!string.IsNullOrEmpty(paramstring))
  115. {
  116. outString += $"${paramstring}";
  117. }
  118. if (!string.IsNullOrEmpty(_salt))
  119. {
  120. outString += $"${_salt}";
  121. }
  122. outString += $"${_hash}";
  123. return outString;
  124. }
  125. }
  126. }