PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/referencesource/System.IdentityModel/System/IdentityModel/Selectors/X509CertificateStore.cs

https://github.com/pruiz/mono
C# | 381 lines | 328 code | 44 blank | 9 comment | 63 complexity | 856a852aea7775f692646ae16c4141fe MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.IdentityModel.Selectors
  5. {
  6. using Microsoft.Win32.SafeHandles;
  7. using System.ComponentModel;
  8. using System.Diagnostics;
  9. using System.Runtime;
  10. using System.Runtime.InteropServices;
  11. using System.Runtime.CompilerServices;
  12. using System.Runtime.ConstrainedExecution;
  13. using System.Security;
  14. using System.Security.Cryptography;
  15. using System.Security.Cryptography.X509Certificates;
  16. using System.Security.Permissions;
  17. class X509CertificateStore
  18. {
  19. SafeCertStoreHandle certStoreHandle = SafeCertStoreHandle.InvalidHandle;
  20. string storeName;
  21. StoreLocation storeLocation;
  22. [Fx.Tag.SecurityNote(Critical = "Uses critical type SafeCertStoreHandle.",
  23. Safe = "Performs a Demand for full trust.")]
  24. [SecuritySafeCritical]
  25. [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
  26. public X509CertificateStore(StoreName storeName, StoreLocation storeLocation)
  27. {
  28. switch (storeName)
  29. {
  30. case StoreName.AddressBook:
  31. this.storeName = "AddressBook";
  32. break;
  33. case StoreName.AuthRoot:
  34. this.storeName = "AuthRoot";
  35. break;
  36. case StoreName.CertificateAuthority:
  37. this.storeName = "CA";
  38. break;
  39. case StoreName.Disallowed:
  40. this.storeName = "Disallowed";
  41. break;
  42. case StoreName.My:
  43. this.storeName = "My";
  44. break;
  45. case StoreName.Root:
  46. this.storeName = "Root";
  47. break;
  48. case StoreName.TrustedPeople:
  49. this.storeName = "TrustedPeople";
  50. break;
  51. case StoreName.TrustedPublisher:
  52. this.storeName = "TrustedPublisher";
  53. break;
  54. default:
  55. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidEnumArgumentException("storeName", (int)storeName,
  56. typeof(StoreName)));
  57. }
  58. if (storeLocation != StoreLocation.CurrentUser && storeLocation != StoreLocation.LocalMachine)
  59. {
  60. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("storeLocation", SR.GetString(SR.X509CertStoreLocationNotValid)));
  61. }
  62. this.storeLocation = storeLocation;
  63. }
  64. [Fx.Tag.SecurityNote(Critical = "Uses critical type SafeCertStoreHandle.",
  65. Safe = "Performs a Demand for full trust.")]
  66. [SecuritySafeCritical]
  67. [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
  68. public void Close()
  69. {
  70. // Accessing via IDisposable to avoid Security check (functionally the same)
  71. ((IDisposable)this.certStoreHandle).Dispose();
  72. }
  73. [Fx.Tag.SecurityNote(Critical = "Uses critical type SafeCertStoreHandle.",
  74. Safe = "Performs a Demand for full trust.")]
  75. [SecuritySafeCritical]
  76. [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
  77. public void Open(OpenFlags openFlags)
  78. {
  79. DiagnosticUtility.DebugAssert(this.certStoreHandle.IsInvalid, "");
  80. uint dwOpenFlags = MapX509StoreFlags(this.storeLocation, openFlags);
  81. SafeCertStoreHandle certStoreHandle = CAPI.CertOpenStore(new IntPtr(CAPI.CERT_STORE_PROV_SYSTEM),
  82. CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
  83. IntPtr.Zero,
  84. dwOpenFlags,
  85. this.storeName);
  86. if (certStoreHandle == null || certStoreHandle.IsInvalid)
  87. {
  88. int error = Marshal.GetLastWin32Error();
  89. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(error));
  90. }
  91. this.certStoreHandle = certStoreHandle;
  92. }
  93. [Fx.Tag.SecurityNote(Critical = "Uses critical types SafeCertContextHandle, SafeCertStoreHandle, SafeHGlobalHandle.",
  94. Safe = "Performs a Demand for full trust.")]
  95. [SecuritySafeCritical]
  96. [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
  97. public X509Certificate2Collection Find(X509FindType findType, object findValue, bool validOnly)
  98. {
  99. DiagnosticUtility.DebugAssert(!this.certStoreHandle.IsInvalid, "");
  100. uint dwFindType;
  101. SafeHGlobalHandle pvFindPara = SafeHGlobalHandle.InvalidHandle;
  102. SafeCertContextHandle pCertContext = SafeCertContextHandle.InvalidHandle;
  103. X509Certificate2Collection result = new X509Certificate2Collection();
  104. SafeHGlobalHandle pvTemp = SafeHGlobalHandle.InvalidHandle;
  105. string strFindValue;
  106. byte[] bytes;
  107. try
  108. {
  109. switch (findType)
  110. {
  111. case X509FindType.FindBySubjectName:
  112. strFindValue = findValue as string;
  113. if (strFindValue == null)
  114. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.X509FindValueMismatch, findType, typeof(string), findValue.GetType())));
  115. dwFindType = CAPI.CERT_FIND_SUBJECT_STR;
  116. pvFindPara = SafeHGlobalHandle.AllocHGlobal(strFindValue);
  117. break;
  118. case X509FindType.FindByThumbprint:
  119. bytes = findValue as byte[];
  120. if (bytes == null)
  121. {
  122. strFindValue = findValue as string;
  123. if (strFindValue == null)
  124. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.X509FindValueMismatchMulti, findType, typeof(string), typeof(byte[]), findValue.GetType())));
  125. bytes = SecurityUtils.DecodeHexString(strFindValue);
  126. }
  127. CAPI.CRYPTOAPI_BLOB blob = new CAPI.CRYPTOAPI_BLOB();
  128. pvTemp = SafeHGlobalHandle.AllocHGlobal(bytes);
  129. blob.pbData = pvTemp.DangerousGetHandle();
  130. blob.cbData = (uint)bytes.Length;
  131. dwFindType = CAPI.CERT_FIND_HASH;
  132. pvFindPara = SafeHGlobalHandle.AllocHGlobal(CAPI.CRYPTOAPI_BLOB.Size);
  133. Marshal.StructureToPtr(blob, pvFindPara.DangerousGetHandle(), false);
  134. break;
  135. case X509FindType.FindBySubjectDistinguishedName:
  136. if (!(findValue is string))
  137. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.X509FindValueMismatch, findType, typeof(string), findValue.GetType())));
  138. dwFindType = CAPI.CERT_FIND_ANY;
  139. break;
  140. case X509FindType.FindByIssuerName:
  141. strFindValue = findValue as string;
  142. if (strFindValue == null)
  143. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.X509FindValueMismatch, findType, typeof(string), findValue.GetType())));
  144. dwFindType = CAPI.CERT_FIND_ISSUER_STR;
  145. pvFindPara = SafeHGlobalHandle.AllocHGlobal(strFindValue);
  146. break;
  147. case X509FindType.FindByIssuerDistinguishedName:
  148. if (!(findValue is string))
  149. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.X509FindValueMismatch, findType, typeof(string), findValue.GetType())));
  150. dwFindType = CAPI.CERT_FIND_ANY;
  151. break;
  152. case X509FindType.FindBySerialNumber:
  153. bytes = findValue as byte[];
  154. if (bytes == null)
  155. {
  156. strFindValue = findValue as string;
  157. if (strFindValue == null)
  158. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.X509FindValueMismatchMulti, findType, typeof(string), typeof(byte[]), findValue.GetType())));
  159. bytes = SecurityUtils.DecodeHexString(strFindValue);
  160. // reverse bits
  161. int len = bytes.Length;
  162. for (int i = 0, j = len - 1; i < bytes.Length / 2; ++i, --j)
  163. {
  164. byte tmp = bytes[i];
  165. bytes[i] = bytes[j];
  166. bytes[j] = tmp;
  167. }
  168. }
  169. findValue = bytes;
  170. dwFindType = CAPI.CERT_FIND_ANY;
  171. break;
  172. case X509FindType.FindBySubjectKeyIdentifier:
  173. bytes = findValue as byte[];
  174. if (bytes == null)
  175. {
  176. strFindValue = findValue as string;
  177. if (strFindValue == null)
  178. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.X509FindValueMismatchMulti, findType, typeof(string), typeof(byte[]), findValue.GetType())));
  179. bytes = SecurityUtils.DecodeHexString(strFindValue);
  180. }
  181. findValue = bytes;
  182. dwFindType = CAPI.CERT_FIND_ANY;
  183. break;
  184. default:
  185. // Fallback to CLR implementation
  186. X509Store store = new X509Store(this.certStoreHandle.DangerousGetHandle());
  187. try
  188. {
  189. return store.Certificates.Find(findType, findValue, validOnly);
  190. }
  191. finally
  192. {
  193. store.Close();
  194. }
  195. }
  196. #pragma warning suppress 56523 // We are not interested in CRYPT_E_NOT_FOUND error, it return null anyway.
  197. pCertContext = CAPI.CertFindCertificateInStore(this.certStoreHandle,
  198. CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
  199. 0,
  200. dwFindType,
  201. pvFindPara,
  202. pCertContext);
  203. while (pCertContext != null && !pCertContext.IsInvalid)
  204. {
  205. X509Certificate2 cert;
  206. if (TryGetMatchingX509Certificate(pCertContext.DangerousGetHandle(), findType,
  207. dwFindType, findValue, validOnly, out cert))
  208. {
  209. result.Add(cert);
  210. }
  211. // CER
  212. RuntimeHelpers.PrepareConstrainedRegions();
  213. try { }
  214. finally
  215. {
  216. // Suppress the finalizer
  217. #pragma warning suppress 56508 // CertFindCertificateInStore will release the prev one.
  218. GC.SuppressFinalize(pCertContext);
  219. #pragma warning suppress 56523 // We are not interested in CRYPT_E_NOT_FOUND error, it return null anyway.
  220. pCertContext = CAPI.CertFindCertificateInStore(this.certStoreHandle,
  221. CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
  222. 0,
  223. dwFindType,
  224. pvFindPara,
  225. pCertContext);
  226. }
  227. }
  228. }
  229. finally
  230. {
  231. if (pCertContext != null)
  232. {
  233. pCertContext.Close();
  234. }
  235. pvFindPara.Close();
  236. pvTemp.Close();
  237. }
  238. return result;
  239. }
  240. bool TryGetMatchingX509Certificate(IntPtr certContext, X509FindType findType,
  241. uint dwFindType, object findValue, bool validOnly, out X509Certificate2 cert)
  242. {
  243. cert = new X509Certificate2(certContext);
  244. if (dwFindType == CAPI.CERT_FIND_ANY)
  245. {
  246. switch (findType)
  247. {
  248. case X509FindType.FindBySubjectDistinguishedName:
  249. if (0 != String.Compare((string)findValue, cert.SubjectName.Name, StringComparison.OrdinalIgnoreCase))
  250. {
  251. cert.Reset();
  252. cert = null;
  253. return false;
  254. }
  255. break;
  256. case X509FindType.FindByIssuerDistinguishedName:
  257. if (0 != String.Compare((string)findValue, cert.IssuerName.Name, StringComparison.OrdinalIgnoreCase))
  258. {
  259. cert.Reset();
  260. cert = null;
  261. return false;
  262. }
  263. break;
  264. case X509FindType.FindBySerialNumber:
  265. if (!BinaryMatches((byte[])findValue, cert.GetSerialNumber()))
  266. {
  267. cert.Reset();
  268. cert = null;
  269. return false;
  270. }
  271. break;
  272. case X509FindType.FindBySubjectKeyIdentifier:
  273. X509SubjectKeyIdentifierExtension skiExtension =
  274. cert.Extensions[CAPI.SubjectKeyIdentifierOid] as X509SubjectKeyIdentifierExtension;
  275. if (skiExtension == null || !BinaryMatches((byte[])findValue, skiExtension.RawData))
  276. {
  277. cert.Reset();
  278. cert = null;
  279. return false;
  280. }
  281. break;
  282. default:
  283. DiagnosticUtility.DebugAssert(findType + " is not supported!");
  284. break;
  285. }
  286. }
  287. if (validOnly)
  288. {
  289. X509Chain chain = new X509Chain(false);
  290. chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
  291. chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
  292. if (!chain.Build(cert))
  293. {
  294. cert.Reset();
  295. cert = null;
  296. return false;
  297. }
  298. }
  299. return cert != null;
  300. }
  301. bool BinaryMatches(byte[] src, byte[] dst)
  302. {
  303. if (src.Length != dst.Length)
  304. return false;
  305. for (int i = 0; i < src.Length; ++i)
  306. {
  307. if (src[i] != dst[i])
  308. return false;
  309. }
  310. return true;
  311. }
  312. // this method maps X509Store OpenFlags to a combination of crypto API flags
  313. uint MapX509StoreFlags(StoreLocation storeLocation, OpenFlags flags)
  314. {
  315. uint dwFlags = 0;
  316. uint openMode = ((uint)flags) & 0x3;
  317. switch (openMode)
  318. {
  319. case (uint)OpenFlags.ReadOnly:
  320. dwFlags |= CAPI.CERT_STORE_READONLY_FLAG;
  321. break;
  322. case (uint)OpenFlags.MaxAllowed:
  323. dwFlags |= CAPI.CERT_STORE_MAXIMUM_ALLOWED_FLAG;
  324. break;
  325. }
  326. if ((flags & OpenFlags.OpenExistingOnly) == OpenFlags.OpenExistingOnly)
  327. dwFlags |= CAPI.CERT_STORE_OPEN_EXISTING_FLAG;
  328. if ((flags & OpenFlags.IncludeArchived) == OpenFlags.IncludeArchived)
  329. dwFlags |= CAPI.CERT_STORE_ENUM_ARCHIVED_FLAG;
  330. if (storeLocation == StoreLocation.LocalMachine)
  331. dwFlags |= CAPI.CERT_SYSTEM_STORE_LOCAL_MACHINE;
  332. else if (storeLocation == StoreLocation.CurrentUser)
  333. dwFlags |= CAPI.CERT_SYSTEM_STORE_CURRENT_USER;
  334. return dwFlags;
  335. }
  336. }
  337. }