PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/referencesource/System.Core/System/Security/Cryptography/X509Certificates/X509Native.cs

https://github.com/pruiz/mono
C# | 395 lines | 272 code | 51 blank | 72 comment | 18 complexity | 78df27d7267969b94027461aad6a93af 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. using System;
  7. using System.Runtime.CompilerServices;
  8. using System.Runtime.ConstrainedExecution;
  9. using System.Runtime.InteropServices;
  10. using System.Runtime.Versioning;
  11. using Microsoft.Win32.SafeHandles;
  12. using System.Diagnostics;
  13. using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
  14. namespace System.Security.Cryptography.X509Certificates {
  15. internal static partial class X509Native {
  16. /// <summary>
  17. /// Determine if a certificate has a specific property
  18. /// </summary>
  19. [SecuritySafeCritical]
  20. internal static bool HasCertificateProperty(SafeCertContextHandle certificateContext,
  21. CertificateProperty property) {
  22. Debug.Assert(certificateContext != null, "certificateContext != null");
  23. Debug.Assert(!certificateContext.IsClosed && !certificateContext.IsInvalid,
  24. "!certificateContext.IsClosed && !certificateContext.IsInvalid");
  25. byte[] buffer = null;
  26. int bufferSize = 0;
  27. bool gotProperty = UnsafeNativeMethods.CertGetCertificateContextProperty(certificateContext,
  28. property,
  29. buffer,
  30. ref bufferSize);
  31. return gotProperty ||
  32. (ErrorCode)Marshal.GetLastWin32Error() == ErrorCode.MoreData;
  33. }
  34. /// <summary>
  35. /// Get the NCrypt handle to the private key of a certificate
  36. /// or null if the private key cannot be acquired by NCrypt.
  37. /// </summary>
  38. [SecuritySafeCritical]
  39. internal static SafeNCryptKeyHandle TryAcquireCngPrivateKey(SafeCertContextHandle certificateContext) {
  40. Debug.Assert(certificateContext != null, "certificateContext != null");
  41. Debug.Assert(!certificateContext.IsClosed && !certificateContext.IsInvalid,
  42. "!certificateContext.IsClosed && !certificateContext.IsInvalid");
  43. bool freeKey = true;
  44. SafeNCryptKeyHandle privateKey = null;
  45. RuntimeHelpers.PrepareConstrainedRegions();
  46. try {
  47. int keySpec = 0;
  48. if (!UnsafeNativeMethods.CryptAcquireCertificatePrivateKey(certificateContext,
  49. AcquireCertificateKeyOptions.AcquireOnlyNCryptKeys,
  50. IntPtr.Zero,
  51. out privateKey,
  52. out keySpec,
  53. out freeKey)) {
  54. return null;
  55. }
  56. return privateKey;
  57. }
  58. finally {
  59. // If we're not supposed to release they key handle, then we need to bump the reference count
  60. // on the safe handle to correspond to the reference that Windows is holding on to. This will
  61. // prevent the CLR from freeing the object handle.
  62. //
  63. // This is certainly not the ideal way to solve this problem - it would be better for
  64. // SafeNCryptKeyHandle to maintain an internal bool field that we could toggle here and
  65. // have that suppress the release when the CLR calls the ReleaseHandle override. However, that
  66. // field does not currently exist, so we'll use this hack instead.
  67. if (privateKey != null && !freeKey) {
  68. bool addedRef = false;
  69. privateKey.DangerousAddRef(ref addedRef);
  70. }
  71. }
  72. }
  73. /// <summary>
  74. /// Get an arbitrary property of a certificate
  75. /// </summary>
  76. [SecuritySafeCritical]
  77. internal static byte[] GetCertificateProperty(SafeCertContextHandle certificateContext,
  78. CertificateProperty property) {
  79. Debug.Assert(certificateContext != null, "certificateContext != null");
  80. Debug.Assert(!certificateContext.IsClosed && !certificateContext.IsInvalid,
  81. "!certificateContext.IsClosed && !certificateContext.IsInvalid");
  82. byte[] buffer = null;
  83. int bufferSize = 0;
  84. if (!UnsafeNativeMethods.CertGetCertificateContextProperty(certificateContext,
  85. property,
  86. buffer,
  87. ref bufferSize)) {
  88. ErrorCode errorCode = (ErrorCode)Marshal.GetLastWin32Error();
  89. if (errorCode != ErrorCode.MoreData) {
  90. throw new CryptographicException((int)errorCode);
  91. }
  92. }
  93. buffer = new byte[bufferSize];
  94. if (!UnsafeNativeMethods.CertGetCertificateContextProperty(certificateContext,
  95. property,
  96. buffer,
  97. ref bufferSize)) {
  98. throw new CryptographicException(Marshal.GetLastWin32Error());
  99. }
  100. return buffer;
  101. }
  102. /// <summary>
  103. /// Get a property of a certificate formatted as a structure
  104. /// </summary>
  105. [SecurityCritical]
  106. internal static T GetCertificateProperty<T>(SafeCertContextHandle certificateContext,
  107. CertificateProperty property) where T : struct {
  108. Debug.Assert(certificateContext != null, "certificateContext != null");
  109. Debug.Assert(!certificateContext.IsClosed && !certificateContext.IsInvalid,
  110. "!certificateContext.IsClosed && !certificateContext.IsInvalid");
  111. byte[] rawProperty = GetCertificateProperty(certificateContext, property);
  112. Debug.Assert(rawProperty.Length >= Marshal.SizeOf(typeof(T)), "Property did not return expected structure");
  113. unsafe {
  114. fixed (byte* pRawProperty = &rawProperty[0]) {
  115. return (T)Marshal.PtrToStructure(new IntPtr(pRawProperty), typeof(T));
  116. }
  117. }
  118. }
  119. /// <summary>
  120. /// Duplicate the certificate context into a safe handle
  121. /// </summary>
  122. [SecuritySafeCritical]
  123. internal static SafeCertContextHandle DuplicateCertContext(IntPtr context) {
  124. Debug.Assert(context != IntPtr.Zero);
  125. return UnsafeNativeMethods.CertDuplicateCertificateContext(context);
  126. }
  127. // Gets a SafeHandle for the X509 certificate. The caller owns the returned handle and should dispose of it. It
  128. // can be used independently of the lifetime of the original X509Certificate.
  129. [SecuritySafeCritical]
  130. internal static SafeCertContextHandle GetCertificateContext(X509Certificate certificate) {
  131. SafeCertContextHandle certificateContext = DuplicateCertContext(certificate.Handle);
  132. // Make sure to keep the X509Certificate object alive until after its certificate context is
  133. // duplicated, otherwise it could end up being closed out from underneath us before we get a
  134. // chance to duplicate the handle.
  135. GC.KeepAlive(certificate);
  136. return certificateContext;
  137. }
  138. }
  139. /// <summary>
  140. /// Native interop layer for X509 certificate and Authenticode functions. Native definitions can be
  141. /// found in wincrypt.h or msaxlapi.h
  142. /// </summary>
  143. internal static partial class X509Native {
  144. /// <summary>
  145. /// Flags for CertVerifyAuthenticodeLicense
  146. /// </summary>
  147. [Flags]
  148. public enum AxlVerificationFlags {
  149. None = 0x00000000,
  150. NoRevocationCheck = 0x00000001, // AXL_REVOCATION_NO_
  151. RevocationCheckEndCertOnly = 0x00000002, // AXL_REVOCATION_
  152. RevocationCheckEntireChain = 0x00000004, // AXL_REVOCATION_
  153. UrlOnlyCacheRetrieval = 0x00000008, // AXL_URL_ONLY_CACHE_RETRIEVAL
  154. LifetimeSigning = 0x00000010, // AXL_LIFETIME_SIGNING
  155. TrustMicrosoftRootOnly = 0x00000020 // AXL_TRUST_MICROSOFT_ROOT_ONLY
  156. }
  157. internal const uint X509_ASN_ENCODING = 0x00000001;
  158. internal const string szOID_ECC_PUBLIC_KEY = "1.2.840.10045.2.1"; //Copied from Windows header file
  159. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  160. internal struct CERT_CONTEXT {
  161. internal uint dwCertEncodingType;
  162. internal IntPtr pbCertEncoded;
  163. internal uint cbCertEncoded;
  164. internal IntPtr pCertInfo;
  165. internal IntPtr hCertStore;
  166. }
  167. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  168. internal struct CERT_PUBLIC_KEY_INFO {
  169. internal CRYPT_ALGORITHM_IDENTIFIER Algorithm;
  170. internal CRYPT_BIT_BLOB PublicKey;
  171. }
  172. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  173. internal struct CERT_INFO {
  174. internal uint dwVersion;
  175. internal CRYPTOAPI_BLOB SerialNumber;
  176. internal CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
  177. internal CRYPTOAPI_BLOB Issuer;
  178. internal FILETIME NotBefore;
  179. internal FILETIME NotAfter;
  180. internal CRYPTOAPI_BLOB Subject;
  181. internal CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo;
  182. internal CRYPT_BIT_BLOB IssuerUniqueId;
  183. internal CRYPT_BIT_BLOB SubjectUniqueId;
  184. internal uint cExtension;
  185. internal IntPtr rgExtension; // PCERT_EXTENSION
  186. }
  187. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  188. internal struct CRYPT_ALGORITHM_IDENTIFIER {
  189. [MarshalAs(UnmanagedType.LPStr)]
  190. internal string pszObjId;
  191. internal CRYPTOAPI_BLOB Parameters;
  192. }
  193. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  194. internal struct CRYPT_BIT_BLOB {
  195. internal uint cbData;
  196. internal IntPtr pbData;
  197. internal uint cUnusedBits;
  198. }
  199. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  200. internal struct CRYPTOAPI_BLOB {
  201. internal uint cbData;
  202. internal IntPtr pbData;
  203. }
  204. /// <summary>
  205. /// Flags for the CryptAcquireCertificatePrivateKey API
  206. /// </summary>
  207. internal enum AcquireCertificateKeyOptions {
  208. None = 0x00000000,
  209. AcquireOnlyNCryptKeys = 0x00040000, // CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG
  210. }
  211. /// <summary>
  212. /// Well known certificate property IDs
  213. /// </summary>
  214. internal enum CertificateProperty {
  215. KeyProviderInfo = 2, // CERT_KEY_PROV_INFO_PROP_ID
  216. KeyContext = 5, // CERT_KEY_CONTEXT_PROP_ID
  217. }
  218. /// <summary>
  219. /// Error codes returned from X509 APIs
  220. /// </summary>
  221. internal enum ErrorCode {
  222. Success = 0x00000000, // ERROR_SUCCESS
  223. MoreData = 0x000000ea, // ERROR_MORE_DATA
  224. }
  225. [StructLayout(LayoutKind.Sequential)]
  226. internal struct CRYPT_KEY_PROV_INFO {
  227. [MarshalAs(UnmanagedType.LPWStr)]
  228. internal string pwszContainerName;
  229. [MarshalAs(UnmanagedType.LPWStr)]
  230. internal string pwszProvName;
  231. internal int dwProvType;
  232. internal int dwFlags;
  233. internal int cProvParam;
  234. internal IntPtr rgProvParam; // PCRYPT_KEY_PROV_PARAM
  235. internal int dwKeySpec;
  236. }
  237. [StructLayout(LayoutKind.Sequential)]
  238. [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
  239. public struct AXL_AUTHENTICODE_SIGNER_INFO {
  240. public int cbSize;
  241. public int dwError;
  242. public CapiNative.AlgorithmId algHash;
  243. // Each of the next fields are Unicode strings, however we need to manually marshal them since
  244. // they are allocated and freed by the native AXL code and should not have their memory handled
  245. // by the marshaller.
  246. public IntPtr pwszHash;
  247. public IntPtr pwszDescription;
  248. public IntPtr pwszDescriptionUrl;
  249. public IntPtr pChainContext; // PCERT_CHAIN_CONTEXT
  250. }
  251. [StructLayout(LayoutKind.Sequential)]
  252. [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
  253. public struct AXL_AUTHENTICODE_TIMESTAMPER_INFO {
  254. public int cbsize;
  255. public int dwError;
  256. public CapiNative.AlgorithmId algHash;
  257. public FILETIME ftTimestamp;
  258. public IntPtr pChainContext; // PCERT_CHAIN_CONTEXT
  259. }
  260. [SuppressUnmanagedCodeSecurity]
  261. [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
  262. #pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
  263. [SecurityCritical(SecurityCriticalScope.Everything)]
  264. #pragma warning restore 618
  265. public static class UnsafeNativeMethods {
  266. /// <summary>
  267. /// Get the hash value of a key blob
  268. /// </summary>
  269. [DllImport("clr")]
  270. public static extern int _AxlGetIssuerPublicKeyHash(IntPtr pCertContext,
  271. [Out]out SafeAxlBufferHandle ppwszPublicKeyHash);
  272. /// <summary>
  273. /// Release any resources used to create an authenticode signer info structure
  274. /// </summary>
  275. [DllImport("clr")]
  276. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  277. public static extern int CertFreeAuthenticodeSignerInfo(ref AXL_AUTHENTICODE_SIGNER_INFO pSignerInfo);
  278. /// <summary>
  279. /// Release any resources used to create an authenticode timestamper info structure
  280. /// </summary>
  281. [DllImport("clr")]
  282. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  283. public static extern int CertFreeAuthenticodeTimestamperInfo(ref AXL_AUTHENTICODE_TIMESTAMPER_INFO pTimestamperInfo);
  284. /// <summary>
  285. /// Verify the authenticode signature on a manifest
  286. /// </summary>
  287. /// <remarks>
  288. /// Code must have permission to open and enumerate certificate stores to use this API
  289. /// </remarks>
  290. [DllImport("clr")]
  291. public static extern int CertVerifyAuthenticodeLicense(ref CapiNative.CRYPTOAPI_BLOB pLicenseBlob,
  292. AxlVerificationFlags dwFlags,
  293. [In, Out] ref AXL_AUTHENTICODE_SIGNER_INFO pSignerInfo,
  294. [In, Out] ref AXL_AUTHENTICODE_TIMESTAMPER_INFO pTimestamperInfo);
  295. [DllImport("crypt32.dll", SetLastError = true)]
  296. [return: MarshalAs(UnmanagedType.Bool)]
  297. internal static extern bool CertGetCertificateContextProperty(SafeCertContextHandle pCertContext,
  298. CertificateProperty dwPropId,
  299. [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pvData,
  300. [In, Out] ref int pcbData);
  301. [DllImport("crypt32.dll")]
  302. internal static extern SafeCertContextHandle CertDuplicateCertificateContext(IntPtr certContext); // CERT_CONTEXT *
  303. [DllImport("crypt32.dll", SetLastError = true)]
  304. [return: MarshalAs(UnmanagedType.Bool)]
  305. internal static extern bool CryptAcquireCertificatePrivateKey(SafeCertContextHandle pCert,
  306. AcquireCertificateKeyOptions dwFlags,
  307. IntPtr pvReserved, // void *
  308. [Out] out SafeNCryptKeyHandle phCryptProvOrNCryptKey,
  309. [Out] out int dwKeySpec,
  310. [Out, MarshalAs(UnmanagedType.Bool)] out bool pfCallerFreeProvOrNCryptKey);
  311. }
  312. }
  313. internal sealed class SafeCertContextHandle : SafeHandleZeroOrMinusOneIsInvalid
  314. {
  315. [SecuritySafeCritical]
  316. private SafeCertContextHandle() : base(true) { }
  317. // 0 is an Invalid Handle
  318. [SecuritySafeCritical]
  319. internal SafeCertContextHandle(IntPtr handle)
  320. : base(true)
  321. {
  322. SetHandle(handle);
  323. }
  324. internal static SafeCertContextHandle InvalidHandle
  325. {
  326. [SecuritySafeCritical]
  327. get { return new SafeCertContextHandle(IntPtr.Zero); }
  328. }
  329. [DllImport("Crypt32.dll", SetLastError = true),
  330. ResourceExposure(ResourceScope.None)]
  331. //#if !FEATURE_CORESYSTEM
  332. // [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  333. //#endif
  334. private static extern bool CertFreeCertificateContext(IntPtr pCertContext);
  335. #if FEATURE_CORESYSTEM
  336. [SecurityCritical]
  337. #endif
  338. [SecuritySafeCritical]
  339. override protected bool ReleaseHandle()
  340. {
  341. return CertFreeCertificateContext(handle);
  342. }
  343. }
  344. }