PageRenderTime 39ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/referencesource/System/security/system/security/cryptography/x509/x509chain.cs

https://github.com/pruiz/mono
C# | 450 lines | 366 code | 61 blank | 23 comment | 58 complexity | fabf16bd06197f214c2520400b905a46 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. //
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. //
  5. // ==--==
  6. //
  7. // X509Chain.cs
  8. //
  9. namespace System.Security.Cryptography.X509Certificates {
  10. using System.Collections;
  11. using System.Diagnostics;
  12. using System.Net;
  13. using System.Runtime.InteropServices;
  14. using System.Runtime.InteropServices.ComTypes;
  15. using System.Security.Cryptography;
  16. using System.Security.Permissions;
  17. using System.Text;
  18. using Microsoft.Win32.SafeHandles;
  19. using _FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
  20. [Flags]
  21. public enum X509ChainStatusFlags {
  22. NoError = 0x00000000,
  23. NotTimeValid = 0x00000001,
  24. NotTimeNested = 0x00000002,
  25. Revoked = 0x00000004,
  26. NotSignatureValid = 0x00000008,
  27. NotValidForUsage = 0x00000010,
  28. UntrustedRoot = 0x00000020,
  29. RevocationStatusUnknown = 0x00000040,
  30. Cyclic = 0x00000080,
  31. InvalidExtension = 0x00000100,
  32. InvalidPolicyConstraints = 0x00000200,
  33. InvalidBasicConstraints = 0x00000400,
  34. InvalidNameConstraints = 0x00000800,
  35. HasNotSupportedNameConstraint = 0x00001000,
  36. HasNotDefinedNameConstraint = 0x00002000,
  37. HasNotPermittedNameConstraint = 0x00004000,
  38. HasExcludedNameConstraint = 0x00008000,
  39. PartialChain = 0x00010000,
  40. CtlNotTimeValid = 0x00020000,
  41. CtlNotSignatureValid = 0x00040000,
  42. CtlNotValidForUsage = 0x00080000,
  43. OfflineRevocation = 0x01000000,
  44. NoIssuanceChainPolicy = 0x02000000,
  45. ExplicitDistrust = 0x04000000,
  46. HasNotSupportedCriticalExtension = 0x08000000,
  47. HasWeakSignature = 0x00100000,
  48. }
  49. public struct X509ChainStatus {
  50. private X509ChainStatusFlags m_status;
  51. private string m_statusInformation;
  52. public X509ChainStatusFlags Status {
  53. get {
  54. return m_status;
  55. }
  56. set {
  57. m_status = value;
  58. }
  59. }
  60. public string StatusInformation {
  61. get {
  62. if (m_statusInformation == null)
  63. return String.Empty;
  64. return m_statusInformation;
  65. }
  66. set {
  67. m_statusInformation = value;
  68. }
  69. }
  70. }
  71. public class X509Chain
  72. : IDisposable
  73. {
  74. private uint m_status;
  75. private X509ChainPolicy m_chainPolicy;
  76. private X509ChainStatus[] m_chainStatus;
  77. private X509ChainElementCollection m_chainElementCollection;
  78. [SecurityCritical]
  79. private SafeX509ChainHandle m_safeCertChainHandle;
  80. private bool m_useMachineContext;
  81. private readonly object m_syncRoot = new object();
  82. public static X509Chain Create() {
  83. return (X509Chain) CryptoConfig.CreateFromName("X509Chain");
  84. }
  85. [SecurityCritical]
  86. public X509Chain () : this (false) {}
  87. [SecurityCritical]
  88. public X509Chain (bool useMachineContext) {
  89. m_status = 0;
  90. m_chainPolicy = null;
  91. m_chainStatus = null;
  92. m_chainElementCollection = new X509ChainElementCollection();
  93. m_safeCertChainHandle = SafeX509ChainHandle.InvalidHandle;
  94. m_useMachineContext = useMachineContext;
  95. }
  96. // Package protected constructor for creating a chain from a PCCERT_CHAIN_CONTEXT
  97. [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
  98. [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
  99. public X509Chain (IntPtr chainContext) {
  100. if (chainContext == IntPtr.Zero)
  101. throw new ArgumentNullException("chainContext");
  102. m_safeCertChainHandle = CAPI.CertDuplicateCertificateChain(chainContext);
  103. if (m_safeCertChainHandle == null || m_safeCertChainHandle == SafeX509ChainHandle.InvalidHandle)
  104. throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidContextHandle), "chainContext");
  105. Init();
  106. }
  107. public IntPtr ChainContext {
  108. [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
  109. [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
  110. get {
  111. return m_safeCertChainHandle.DangerousGetHandle();
  112. }
  113. }
  114. public SafeX509ChainHandle SafeHandle {
  115. [SecurityCritical]
  116. [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  117. [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  118. get
  119. {
  120. return m_safeCertChainHandle;
  121. }
  122. }
  123. public X509ChainPolicy ChainPolicy {
  124. get {
  125. if (m_chainPolicy == null)
  126. m_chainPolicy = new X509ChainPolicy();
  127. return m_chainPolicy;
  128. }
  129. set {
  130. if (value == null)
  131. throw new ArgumentNullException("value");
  132. m_chainPolicy = value;
  133. }
  134. }
  135. public X509ChainStatus[] ChainStatus {
  136. get {
  137. // We give the user a reference to the array since we'll never access it.
  138. if (m_chainStatus == null) {
  139. if (m_status == 0) {
  140. m_chainStatus = new X509ChainStatus[0]; // empty array
  141. } else {
  142. m_chainStatus = GetChainStatusInformation(m_status);
  143. }
  144. }
  145. return m_chainStatus;
  146. }
  147. }
  148. public X509ChainElementCollection ChainElements {
  149. get {
  150. return m_chainElementCollection;
  151. }
  152. }
  153. #if FEATURE_CORESYSTEM
  154. [SecuritySafeCritical]
  155. #endif
  156. #if !FEATURE_CORESYSTEM
  157. [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
  158. [PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted=true)]
  159. #endif
  160. public bool Build (X509Certificate2 certificate) {
  161. lock (m_syncRoot) {
  162. if (certificate == null || certificate.CertContext.IsInvalid)
  163. throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidContextHandle), "certificate");
  164. #if !FEATURE_CORESYSTEM
  165. // Chain building opens and enumerates the root store to see if the root of the chain is trusted.
  166. StorePermission sp = new StorePermission(StorePermissionFlags.OpenStore | StorePermissionFlags.EnumerateCertificates);
  167. sp.Demand();
  168. #endif
  169. X509ChainPolicy chainPolicy = this.ChainPolicy;
  170. #if !FEATURE_CORESYSTEM
  171. if (chainPolicy.RevocationMode == X509RevocationMode.Online) {
  172. if (certificate.Extensions[CAPI.szOID_CRL_DIST_POINTS] != null ||
  173. certificate.Extensions[CAPI.szOID_AUTHORITY_INFO_ACCESS] != null) {
  174. // If there is a CDP or AIA extension, we demand unrestricted network access and store add permission
  175. // since CAPI can download certificates into the CA store from the network.
  176. PermissionSet ps = new PermissionSet(PermissionState.None);
  177. ps.AddPermission(new WebPermission(PermissionState.Unrestricted));
  178. ps.AddPermission(new StorePermission(StorePermissionFlags.AddToStore));
  179. ps.Demand();
  180. }
  181. }
  182. #endif
  183. Reset();
  184. int hr = BuildChain(m_useMachineContext ? new IntPtr(CAPI.HCCE_LOCAL_MACHINE) : new IntPtr(CAPI.HCCE_CURRENT_USER),
  185. certificate.CertContext,
  186. chainPolicy.ExtraStore,
  187. chainPolicy.ApplicationPolicy,
  188. chainPolicy.CertificatePolicy,
  189. chainPolicy.RevocationMode,
  190. chainPolicy.RevocationFlag,
  191. chainPolicy.VerificationTime,
  192. chainPolicy.UrlRetrievalTimeout,
  193. ref m_safeCertChainHandle);
  194. if (hr != CAPI.S_OK)
  195. return false;
  196. // Init.
  197. Init();
  198. // Verify the chain using the specified policy.
  199. CAPI.CERT_CHAIN_POLICY_PARA PolicyPara = new CAPI.CERT_CHAIN_POLICY_PARA(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_PARA)));
  200. CAPI.CERT_CHAIN_POLICY_STATUS PolicyStatus = new CAPI.CERT_CHAIN_POLICY_STATUS(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_STATUS)));
  201. PolicyPara.dwFlags = (uint) chainPolicy.VerificationFlags;
  202. if (!CAPI.CertVerifyCertificateChainPolicy(new IntPtr(CAPI.CERT_CHAIN_POLICY_BASE),
  203. m_safeCertChainHandle,
  204. ref PolicyPara,
  205. ref PolicyStatus))
  206. // The API failed.
  207. throw new CryptographicException(Marshal.GetLastWin32Error());
  208. CAPI.SetLastError(PolicyStatus.dwError);
  209. return (PolicyStatus.dwError == 0);
  210. }
  211. }
  212. [SecurityCritical]
  213. #if !FEATURE_CORESYSTEM
  214. [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
  215. [PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted=true)]
  216. #endif
  217. public void Reset () {
  218. m_status = 0;
  219. m_chainStatus = null;
  220. m_chainElementCollection = new X509ChainElementCollection();
  221. if (!m_safeCertChainHandle.IsInvalid) {
  222. m_safeCertChainHandle.Dispose();
  223. m_safeCertChainHandle = SafeX509ChainHandle.InvalidHandle;
  224. }
  225. }
  226. [SecuritySafeCritical]
  227. public void Dispose()
  228. {
  229. Dispose(true);
  230. GC.SuppressFinalize(this);
  231. }
  232. [SecuritySafeCritical]
  233. protected virtual void Dispose(bool disposing)
  234. {
  235. if (disposing)
  236. {
  237. Reset();
  238. }
  239. }
  240. [SecurityCritical]
  241. private unsafe void Init () {
  242. using (SafeX509ChainHandle safeCertChainHandle = CAPI.CertDuplicateCertificateChain(m_safeCertChainHandle)) {
  243. CAPI.CERT_CHAIN_CONTEXT pChain = new CAPI.CERT_CHAIN_CONTEXT(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_CONTEXT)));
  244. uint cbSize = (uint) Marshal.ReadInt32(safeCertChainHandle.DangerousGetHandle());
  245. if (cbSize > Marshal.SizeOf(pChain))
  246. cbSize = (uint) Marshal.SizeOf(pChain);
  247. X509Utils.memcpy(m_safeCertChainHandle.DangerousGetHandle(), new IntPtr(&pChain), cbSize);
  248. m_status = pChain.dwErrorStatus;
  249. Debug.Assert(pChain.cChain > 0);
  250. m_chainElementCollection = new X509ChainElementCollection(Marshal.ReadIntPtr(pChain.rgpChain));
  251. }
  252. }
  253. #if FEATURE_CORESYSTEM
  254. [SecuritySafeCritical]
  255. #endif
  256. internal static X509ChainStatus[] GetChainStatusInformation (uint dwStatus) {
  257. if (dwStatus == 0)
  258. return new X509ChainStatus[0];
  259. int count = 0;
  260. for (uint bits = dwStatus; bits != 0; bits = bits >> 1) {
  261. if ((bits & 0x1) != 0)
  262. count++;
  263. }
  264. X509ChainStatus[] chainStatus = new X509ChainStatus[count];
  265. int index = 0;
  266. foreach (X509ChainErrorMapping mapping in s_x509ChainErrorMappings)
  267. {
  268. if ((dwStatus & mapping.Win32Flag) != 0)
  269. {
  270. Debug.Assert(index < chainStatus.Length);
  271. chainStatus[index].StatusInformation = X509Utils.GetSystemErrorString(mapping.Win32ErrorCode);
  272. chainStatus[index].Status = mapping.ChainStatusFlag;
  273. index++;
  274. dwStatus &= ~mapping.Win32Flag;
  275. }
  276. }
  277. int shiftCount = 0;
  278. for (uint bits = dwStatus; bits != 0; bits = bits >> 1) {
  279. if ((bits & 0x1) != 0) {
  280. Debug.Assert(index < chainStatus.Length);
  281. chainStatus[index].Status = (X509ChainStatusFlags) (1 << shiftCount);
  282. chainStatus[index].StatusInformation = SR.GetString(SR.Unknown_Error);
  283. index++;
  284. }
  285. shiftCount++;
  286. }
  287. Debug.Assert(index == chainStatus.Length);
  288. return chainStatus;
  289. }
  290. //
  291. // Builds a certificate chain.
  292. //
  293. [SecurityCritical]
  294. internal static unsafe int BuildChain (IntPtr hChainEngine,
  295. SafeCertContextHandle pCertContext,
  296. X509Certificate2Collection extraStore,
  297. OidCollection applicationPolicy,
  298. OidCollection certificatePolicy,
  299. X509RevocationMode revocationMode,
  300. X509RevocationFlag revocationFlag,
  301. DateTime verificationTime,
  302. TimeSpan timeout,
  303. ref SafeX509ChainHandle ppChainContext) {
  304. if (pCertContext == null || pCertContext.IsInvalid)
  305. throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidContextHandle), "pCertContext");
  306. SafeCertStoreHandle hCertStore = SafeCertStoreHandle.InvalidHandle;
  307. if (extraStore != null && extraStore.Count > 0)
  308. hCertStore = X509Utils.ExportToMemoryStore(extraStore);
  309. CAPI.CERT_CHAIN_PARA ChainPara = new CAPI.CERT_CHAIN_PARA();
  310. // Initialize the structure size.
  311. ChainPara.cbSize = (uint) Marshal.SizeOf(ChainPara);
  312. SafeLocalAllocHandle applicationPolicyHandle = SafeLocalAllocHandle.InvalidHandle;
  313. SafeLocalAllocHandle certificatePolicyHandle = SafeLocalAllocHandle.InvalidHandle;
  314. try {
  315. // Application policy
  316. if (applicationPolicy != null && applicationPolicy.Count > 0) {
  317. ChainPara.RequestedUsage.dwType = CAPI.USAGE_MATCH_TYPE_AND;
  318. ChainPara.RequestedUsage.Usage.cUsageIdentifier = (uint) applicationPolicy.Count;
  319. applicationPolicyHandle = X509Utils.CopyOidsToUnmanagedMemory(applicationPolicy);
  320. ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = applicationPolicyHandle.DangerousGetHandle();
  321. }
  322. // Certificate policy
  323. if (certificatePolicy != null && certificatePolicy.Count > 0) {
  324. ChainPara.RequestedIssuancePolicy.dwType = CAPI.USAGE_MATCH_TYPE_AND;
  325. ChainPara.RequestedIssuancePolicy.Usage.cUsageIdentifier = (uint) certificatePolicy.Count;
  326. certificatePolicyHandle = X509Utils.CopyOidsToUnmanagedMemory(certificatePolicy);
  327. ChainPara.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = certificatePolicyHandle.DangerousGetHandle();
  328. }
  329. ChainPara.dwUrlRetrievalTimeout = (uint) Math.Floor(timeout.TotalMilliseconds);
  330. _FILETIME ft = new _FILETIME();
  331. *((long*) &ft) = verificationTime.ToFileTime();
  332. uint flags = X509Utils.MapRevocationFlags(revocationMode, revocationFlag);
  333. // Build the chain.
  334. if (!CAPI.CertGetCertificateChain(hChainEngine,
  335. pCertContext,
  336. ref ft,
  337. hCertStore,
  338. ref ChainPara,
  339. flags,
  340. IntPtr.Zero,
  341. ref ppChainContext))
  342. return Marshal.GetHRForLastWin32Error();
  343. }
  344. finally {
  345. applicationPolicyHandle.Dispose();
  346. certificatePolicyHandle.Dispose();
  347. }
  348. return CAPI.S_OK;
  349. }
  350. private struct X509ChainErrorMapping
  351. {
  352. public readonly uint Win32Flag;
  353. public readonly int Win32ErrorCode;
  354. public readonly X509ChainStatusFlags ChainStatusFlag;
  355. public X509ChainErrorMapping(uint win32Flag, int win32ErrorCode, X509ChainStatusFlags chainStatusFlag)
  356. {
  357. Win32Flag = win32Flag;
  358. Win32ErrorCode = win32ErrorCode;
  359. ChainStatusFlag = chainStatusFlag;
  360. }
  361. }
  362. private static readonly X509ChainErrorMapping[] s_x509ChainErrorMappings = new[]
  363. {
  364. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_SIGNATURE_VALID, CAPI.TRUST_E_CERT_SIGNATURE, X509ChainStatusFlags.NotSignatureValid),
  365. new X509ChainErrorMapping(CAPI.CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID, CAPI.TRUST_E_CERT_SIGNATURE, X509ChainStatusFlags.CtlNotSignatureValid),
  366. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_UNTRUSTED_ROOT, CAPI.CERT_E_UNTRUSTEDROOT, X509ChainStatusFlags.UntrustedRoot),
  367. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_PARTIAL_CHAIN, CAPI.CERT_E_CHAINING, X509ChainStatusFlags.PartialChain),
  368. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_REVOKED, CAPI.CRYPT_E_REVOKED, X509ChainStatusFlags.Revoked),
  369. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_VALID_FOR_USAGE, CAPI.CERT_E_WRONG_USAGE, X509ChainStatusFlags.NotValidForUsage),
  370. new X509ChainErrorMapping(CAPI.CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE, CAPI.CERT_E_WRONG_USAGE, X509ChainStatusFlags.CtlNotValidForUsage),
  371. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_TIME_VALID, CAPI.CERT_E_EXPIRED, X509ChainStatusFlags.NotTimeValid),
  372. new X509ChainErrorMapping(CAPI.CERT_TRUST_CTL_IS_NOT_TIME_VALID, CAPI.CERT_E_EXPIRED, X509ChainStatusFlags.CtlNotTimeValid),
  373. new X509ChainErrorMapping(CAPI.CERT_TRUST_INVALID_NAME_CONSTRAINTS, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.InvalidNameConstraints),
  374. new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasNotSupportedNameConstraint),
  375. new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasNotDefinedNameConstraint),
  376. new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasNotPermittedNameConstraint),
  377. new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT, CAPI.CERT_E_INVALID_NAME, X509ChainStatusFlags.HasExcludedNameConstraint),
  378. new X509ChainErrorMapping(CAPI.CERT_TRUST_INVALID_POLICY_CONSTRAINTS, CAPI.CERT_E_INVALID_POLICY, X509ChainStatusFlags.InvalidPolicyConstraints),
  379. new X509ChainErrorMapping(CAPI.CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY, CAPI.CERT_E_INVALID_POLICY, X509ChainStatusFlags.NoIssuanceChainPolicy),
  380. new X509ChainErrorMapping(CAPI.CERT_TRUST_INVALID_BASIC_CONSTRAINTS, CAPI.TRUST_E_BASIC_CONSTRAINTS, X509ChainStatusFlags.InvalidBasicConstraints),
  381. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_NOT_TIME_NESTED, CAPI.CERT_E_VALIDITYPERIODNESTING, X509ChainStatusFlags.NotTimeNested),
  382. new X509ChainErrorMapping(CAPI.CERT_TRUST_REVOCATION_STATUS_UNKNOWN, CAPI.CRYPT_E_NO_REVOCATION_CHECK, X509ChainStatusFlags.RevocationStatusUnknown),
  383. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_OFFLINE_REVOCATION, CAPI.CRYPT_E_REVOCATION_OFFLINE, X509ChainStatusFlags.OfflineRevocation),
  384. new X509ChainErrorMapping(CAPI.CERT_TRUST_IS_EXPLICIT_DISTRUST, CAPI.TRUST_E_EXPLICIT_DISTRUST, X509ChainStatusFlags.ExplicitDistrust),
  385. new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT, CAPI.CERT_E_CRITICAL, X509ChainStatusFlags.HasNotSupportedCriticalExtension),
  386. new X509ChainErrorMapping(CAPI.CERT_TRUST_HAS_WEAK_SIGNATURE, CAPI.CERTSRV_E_WEAK_SIGNATURE_OR_KEY, X509ChainStatusFlags.HasWeakSignature),
  387. };
  388. }
  389. }