PasswordEvidence.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /* Copyright 2010-2016 MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Linq;
  17. using System.Runtime.InteropServices;
  18. using System.Security;
  19. using System.Security.Cryptography;
  20. using System.Text;
  21. using MongoDB.Bson;
  22. using MongoDB.Bson.IO;
  23. using MongoDB.Driver.Core.Misc;
  24. namespace MongoDB.Driver
  25. {
  26. /// <summary>
  27. /// Evidence of a MongoIdentity via a shared secret.
  28. /// </summary>
  29. public sealed class PasswordEvidence : MongoIdentityEvidence
  30. {
  31. // private fields
  32. private readonly SecureString _securePassword;
  33. private readonly string _digest; // used to implement Equals without referring to the SecureString
  34. // constructors
  35. /// <summary>
  36. /// Initializes a new instance of the <see cref="PasswordEvidence" /> class.
  37. /// </summary>
  38. /// <param name="password">The password.</param>
  39. public PasswordEvidence(SecureString password)
  40. {
  41. _securePassword = password.Copy();
  42. _securePassword.MakeReadOnly();
  43. _digest = GenerateDigest(password);
  44. }
  45. /// <summary>
  46. /// Initializes a new instance of the <see cref="PasswordEvidence" /> class.
  47. /// </summary>
  48. /// <param name="password">The password.</param>
  49. public PasswordEvidence(string password)
  50. : this(CreateSecureString(password))
  51. { }
  52. // public properties
  53. /// <summary>
  54. /// Gets the password.
  55. /// </summary>
  56. public SecureString SecurePassword
  57. {
  58. get { return _securePassword; }
  59. }
  60. // public methods
  61. /// <summary>
  62. /// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
  63. /// </summary>
  64. /// <param name="rhs">The <see cref="System.Object" /> to compare with this instance.</param>
  65. /// <returns>
  66. /// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
  67. /// </returns>
  68. public override bool Equals(object rhs)
  69. {
  70. if (object.ReferenceEquals(rhs, null) || GetType() != rhs.GetType()) { return false; }
  71. return _digest == ((PasswordEvidence)rhs)._digest;
  72. }
  73. /// <summary>
  74. /// Returns a hash code for this instance.
  75. /// </summary>
  76. /// <returns>
  77. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
  78. /// </returns>
  79. public override int GetHashCode()
  80. {
  81. return _digest.GetHashCode();
  82. }
  83. // internal methods
  84. /// <summary>
  85. /// Computes the MONGODB-CR password digest.
  86. /// </summary>
  87. /// <param name="username">The username.</param>
  88. /// <returns></returns>
  89. internal string ComputeMongoCRPasswordDigest(string username)
  90. {
  91. using (var md5 = MD5.Create())
  92. {
  93. var encoding = Utf8Encodings.Strict;
  94. var prefixBytes = encoding.GetBytes(username + ":mongo:");
  95. var hash = ComputeHash(md5, prefixBytes, _securePassword);
  96. return BsonUtils.ToHexString(hash);
  97. }
  98. }
  99. // private static methods
  100. private static SecureString CreateSecureString(string str)
  101. {
  102. if (str != null)
  103. {
  104. var secureStr = new SecureString();
  105. foreach (var c in str)
  106. {
  107. secureStr.AppendChar(c);
  108. }
  109. secureStr.MakeReadOnly();
  110. return secureStr;
  111. }
  112. return null;
  113. }
  114. /// <summary>
  115. /// Computes the hash value of the secured string
  116. /// </summary>
  117. private static string GenerateDigest(SecureString password)
  118. {
  119. using (var sha256 = SHA256.Create())
  120. {
  121. var hash = ComputeHash(sha256, new byte[0], password);
  122. return BsonUtils.ToHexString(hash);
  123. }
  124. }
  125. private static byte[] ComputeHash(HashAlgorithm algorithm, byte[] prefixBytes, SecureString password)
  126. {
  127. if (password.Length == 0)
  128. {
  129. return ComputeHash(algorithm, prefixBytes, new byte[0]);
  130. }
  131. else
  132. {
  133. #if NET45
  134. var passwordIntPtr = Marshal.SecureStringToGlobalAllocUnicode(password);
  135. #else
  136. var passwordIntPtr = SecureStringMarshal.SecureStringToGlobalAllocUnicode(password);
  137. #endif
  138. try
  139. {
  140. var passwordChars = new char[password.Length];
  141. var passwordCharsHandle = GCHandle.Alloc(passwordChars, GCHandleType.Pinned);
  142. try
  143. {
  144. Marshal.Copy(passwordIntPtr, passwordChars, 0, password.Length);
  145. return ComputeHash(algorithm, prefixBytes, passwordChars);
  146. }
  147. finally
  148. {
  149. Array.Clear(passwordChars, 0, passwordChars.Length);
  150. passwordCharsHandle.Free();
  151. }
  152. }
  153. finally
  154. {
  155. Marshal.ZeroFreeGlobalAllocUnicode(passwordIntPtr);
  156. }
  157. }
  158. }
  159. private static byte[] ComputeHash(HashAlgorithm algorithm, byte[] prefixBytes, char[] passwordChars)
  160. {
  161. var passwordBytes = new byte[Utf8Encodings.Strict.GetByteCount(passwordChars)];
  162. var passwordBytesHandle = GCHandle.Alloc(passwordBytes, GCHandleType.Pinned);
  163. try
  164. {
  165. Utf8Encodings.Strict.GetBytes(passwordChars, 0, passwordChars.Length, passwordBytes, 0);
  166. return ComputeHash(algorithm, prefixBytes, passwordBytes);
  167. }
  168. finally
  169. {
  170. Array.Clear(passwordBytes, 0, passwordBytes.Length);
  171. passwordBytesHandle.Free();
  172. }
  173. }
  174. private static byte[] ComputeHash(HashAlgorithm algorithm, byte[] prefixBytes, byte[] passwordBytes)
  175. {
  176. var buffer = new byte[prefixBytes.Length + passwordBytes.Length];
  177. var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
  178. try
  179. {
  180. Buffer.BlockCopy(prefixBytes, 0, buffer, 0, prefixBytes.Length);
  181. Buffer.BlockCopy(passwordBytes, 0, buffer, prefixBytes.Length, passwordBytes.Length);
  182. return algorithm.ComputeHash(buffer);
  183. }
  184. finally
  185. {
  186. Array.Clear(buffer, 0, buffer.Length);
  187. bufferHandle.Free();
  188. }
  189. }
  190. }
  191. }