PageRenderTime 24ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/Visual Studio 2013/Source/ndp/clr/src/ManagedLibraries/Security/System/Security/Cryptography/X509/X509Utils.cs

https://github.com/tforsberg/z
C# | 396 lines | 301 code | 57 blank | 38 comment | 75 complexity | 7ce1bd778cf67eca36dc12607b3da844 MD5 | raw file
  1. // ==++==
  2. //
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. //
  5. // ==--==
  6. // <OWNER>[....]</OWNER>
  7. //
  8. //
  9. // X509Utils.cs
  10. //
  11. namespace System.Security.Cryptography.X509Certificates {
  12. using System;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. using System.IO;
  16. using System.Runtime.InteropServices;
  17. using System.Security.Cryptography;
  18. using System.Security.Cryptography.Xml;
  19. using System.Security.Permissions;
  20. using System.Text;
  21. using _FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
  22. internal class X509Utils {
  23. private X509Utils () {}
  24. // this method maps X509RevocationFlag to crypto API flags.
  25. internal static uint MapRevocationFlags (X509RevocationMode revocationMode, X509RevocationFlag revocationFlag) {
  26. uint dwFlags = 0;
  27. if (revocationMode == X509RevocationMode.NoCheck)
  28. return dwFlags;
  29. if (revocationMode == X509RevocationMode.Offline)
  30. dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
  31. if (revocationFlag == X509RevocationFlag.EndCertificateOnly)
  32. dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_END_CERT;
  33. else if (revocationFlag == X509RevocationFlag.EntireChain)
  34. dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_CHAIN;
  35. else
  36. dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
  37. return dwFlags;
  38. }
  39. private static readonly char[] hexValues = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  40. internal static string EncodeHexString (byte[] sArray) {
  41. return EncodeHexString(sArray, 0, (uint) sArray.Length);
  42. }
  43. internal static string EncodeHexString (byte[] sArray, uint start, uint end) {
  44. String result = null;
  45. if (sArray != null) {
  46. char[] hexOrder = new char[(end - start) * 2];
  47. uint digit;
  48. for (uint i = start, j = 0; i < end; i++) {
  49. digit = (uint) ((sArray[i] & 0xf0) >> 4);
  50. hexOrder[j++] = hexValues[digit];
  51. digit = (uint) (sArray[i] & 0x0f);
  52. hexOrder[j++] = hexValues[digit];
  53. }
  54. result = new String(hexOrder);
  55. }
  56. return result;
  57. }
  58. internal static string EncodeHexStringFromInt (byte[] sArray) {
  59. return EncodeHexStringFromInt(sArray, 0, (uint) sArray.Length);
  60. }
  61. internal static string EncodeHexStringFromInt (byte[] sArray, uint start, uint end) {
  62. String result = null;
  63. if(sArray != null) {
  64. char[] hexOrder = new char[(end - start) * 2];
  65. uint i = end;
  66. uint digit, j=0;
  67. while (i-- > start) {
  68. digit = (uint) (sArray[i] & 0xf0) >> 4;
  69. hexOrder[j++] = hexValues[digit];
  70. digit = (uint) (sArray[i] & 0x0f);
  71. hexOrder[j++] = hexValues[digit];
  72. }
  73. result = new String(hexOrder);
  74. }
  75. return result;
  76. }
  77. internal static byte HexToByte (char val) {
  78. if (val <= '9' && val >= '0')
  79. return (byte) (val - '0');
  80. else if (val >= 'a' && val <= 'f')
  81. return (byte) ((val - 'a') + 10);
  82. else if (val >= 'A' && val <= 'F')
  83. return (byte) ((val - 'A') + 10);
  84. else
  85. return 0xFF;
  86. }
  87. internal static byte[] DecodeHexString (string s) {
  88. string hexString = Utils.DiscardWhiteSpaces(s);
  89. uint cbHex = (uint) hexString.Length / 2;
  90. byte[] hex = new byte[cbHex];
  91. int i = 0;
  92. for (int index = 0; index < cbHex; index++) {
  93. hex[index] = (byte) ((HexToByte(hexString[i]) << 4) | HexToByte(hexString[i+1]));
  94. i += 2;
  95. }
  96. return hex;
  97. }
  98. [SecurityCritical]
  99. internal static unsafe bool MemEqual (byte * pbBuf1, uint cbBuf1, byte * pbBuf2, uint cbBuf2) {
  100. if (cbBuf1 != cbBuf2)
  101. return false;
  102. while (cbBuf1-- > 0) {
  103. if (*pbBuf1++ != *pbBuf2++) {
  104. return false;
  105. }
  106. }
  107. return true;
  108. }
  109. [SecurityCritical]
  110. internal static SafeLocalAllocHandle StringToAnsiPtr (string s) {
  111. byte[] arr = new byte[s.Length + 1];
  112. Encoding.ASCII.GetBytes(s, 0, s.Length, arr, 0);
  113. SafeLocalAllocHandle pb = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(arr.Length));
  114. Marshal.Copy(arr, 0, pb.DangerousGetHandle(), arr.Length);
  115. return pb;
  116. }
  117. [SecurityCritical]
  118. internal static SafeCertContextHandle GetCertContext (X509Certificate2 certificate) {
  119. SafeCertContextHandle safeCertContext = CAPI.CertDuplicateCertificateContext(certificate.Handle);
  120. GC.KeepAlive(certificate);
  121. return safeCertContext;
  122. }
  123. [SecurityCritical]
  124. internal static bool GetPrivateKeyInfo (SafeCertContextHandle safeCertContext, ref CspParameters parameters) {
  125. SafeLocalAllocHandle ptr = SafeLocalAllocHandle.InvalidHandle;
  126. uint cbData = 0;
  127. if (!CAPI.CAPISafe.CertGetCertificateContextProperty(safeCertContext,
  128. CAPI.CERT_KEY_PROV_INFO_PROP_ID,
  129. ptr,
  130. ref cbData)) {
  131. int dwErrorCode = Marshal.GetLastWin32Error();
  132. if (dwErrorCode == CAPI.CRYPT_E_NOT_FOUND)
  133. return false;
  134. else
  135. throw new CryptographicException(Marshal.GetLastWin32Error());
  136. }
  137. ptr = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbData));
  138. if (!CAPI.CAPISafe.CertGetCertificateContextProperty(safeCertContext,
  139. CAPI.CERT_KEY_PROV_INFO_PROP_ID,
  140. ptr,
  141. ref cbData)) {
  142. int dwErrorCode = Marshal.GetLastWin32Error();
  143. if (dwErrorCode == CAPI.CRYPT_E_NOT_FOUND)
  144. return false;
  145. else
  146. throw new CryptographicException(Marshal.GetLastWin32Error());
  147. }
  148. CAPI.CRYPT_KEY_PROV_INFO pKeyProvInfo = (CAPI.CRYPT_KEY_PROV_INFO) Marshal.PtrToStructure(ptr.DangerousGetHandle(), typeof(CAPI.CRYPT_KEY_PROV_INFO));
  149. parameters.ProviderName = pKeyProvInfo.pwszProvName;
  150. parameters.KeyContainerName = pKeyProvInfo.pwszContainerName;
  151. parameters.ProviderType = (int) pKeyProvInfo.dwProvType;
  152. parameters.KeyNumber = (int) pKeyProvInfo.dwKeySpec;
  153. parameters.Flags = (CspProviderFlags) ((pKeyProvInfo.dwFlags & CAPI.CRYPT_MACHINE_KEYSET) == CAPI.CRYPT_MACHINE_KEYSET ? CspProviderFlags.UseMachineKeyStore : 0);
  154. ptr.Dispose();
  155. return true;
  156. }
  157. // this method create a memory store from a certificate collection
  158. [SecurityCritical]
  159. internal static SafeCertStoreHandle ExportToMemoryStore (X509Certificate2Collection collection) {
  160. //
  161. // We need to Assert all StorePermission flags since this is a memory store and we want
  162. // semi-trusted code to be able to export certificates to a memory store.
  163. //
  164. StorePermission sp = new StorePermission(StorePermissionFlags.AllFlags);
  165. sp.Assert();
  166. SafeCertStoreHandle safeCertStoreHandle = SafeCertStoreHandle.InvalidHandle;
  167. // we always want to use CERT_STORE_ENUM_ARCHIVED_FLAG since we want to preserve the collection in this operation.
  168. // By default, Archived certificates will not be included.
  169. safeCertStoreHandle = CAPI.CertOpenStore(new IntPtr(CAPI.CERT_STORE_PROV_MEMORY),
  170. CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
  171. IntPtr.Zero,
  172. CAPI.CERT_STORE_ENUM_ARCHIVED_FLAG | CAPI.CERT_STORE_CREATE_NEW_FLAG,
  173. null);
  174. if (safeCertStoreHandle == null || safeCertStoreHandle.IsInvalid)
  175. throw new CryptographicException(Marshal.GetLastWin32Error());
  176. //
  177. // We use CertAddCertificateLinkToStore to keep a link to the original store, so any property changes get
  178. // applied to the original store. This has a limit of 99 links per cert context however.
  179. //
  180. foreach (X509Certificate2 x509 in collection) {
  181. if (!CAPI.CertAddCertificateLinkToStore(safeCertStoreHandle,
  182. X509Utils.GetCertContext(x509),
  183. CAPI.CERT_STORE_ADD_ALWAYS,
  184. SafeCertContextHandle.InvalidHandle))
  185. throw new CryptographicException(Marshal.GetLastWin32Error());
  186. }
  187. return safeCertStoreHandle;
  188. }
  189. [SecuritySafeCritical]
  190. internal static uint OidToAlgId (string value) {
  191. SafeLocalAllocHandle pszOid = StringToAnsiPtr(value);
  192. CAPI.CRYPT_OID_INFO pOIDInfo = CAPI.CryptFindOIDInfo(CAPI.CRYPT_OID_INFO_OID_KEY, pszOid, 0);
  193. return pOIDInfo.Algid;
  194. }
  195. internal static bool IsSelfSigned (X509Chain chain) {
  196. X509ChainElementCollection elements = chain.ChainElements;
  197. if (elements.Count != 1)
  198. return false;
  199. X509Certificate2 certificate = elements[0].Certificate;
  200. if (String.Compare(certificate.SubjectName.Name, certificate.IssuerName.Name, StringComparison.OrdinalIgnoreCase) == 0)
  201. return true;
  202. return false;
  203. }
  204. [SecurityCritical]
  205. internal static SafeLocalAllocHandle CopyOidsToUnmanagedMemory (OidCollection oids) {
  206. SafeLocalAllocHandle safeLocalAllocHandle = SafeLocalAllocHandle.InvalidHandle;
  207. if (oids == null || oids.Count == 0)
  208. return safeLocalAllocHandle;
  209. int ptrSize = oids.Count * Marshal.SizeOf(typeof(IntPtr));
  210. int oidSize = 0;
  211. foreach (Oid oid in oids) {
  212. oidSize += (oid.Value.Length + 1);
  213. }
  214. safeLocalAllocHandle = CAPI.LocalAlloc(CAPI.LPTR, new IntPtr((uint) ptrSize + (uint) oidSize));
  215. IntPtr pOid = new IntPtr((long)safeLocalAllocHandle.DangerousGetHandle() + ptrSize);
  216. for (int index=0; index < oids.Count; index++) {
  217. Marshal.WriteIntPtr(new IntPtr((long) safeLocalAllocHandle.DangerousGetHandle() + index * Marshal.SizeOf(typeof(IntPtr))), pOid);
  218. byte[] ansiOid = Encoding.ASCII.GetBytes(oids[index].Value);
  219. Marshal.Copy(ansiOid, 0, pOid, ansiOid.Length);
  220. pOid = new IntPtr((long) pOid + oids[index].Value.Length + 1);
  221. }
  222. return safeLocalAllocHandle;
  223. }
  224. //
  225. // Builds a certificate chain.
  226. //
  227. [SecurityCritical]
  228. internal static X509Certificate2Collection GetCertificates(SafeCertStoreHandle safeCertStoreHandle) {
  229. X509Certificate2Collection collection = new X509Certificate2Collection();
  230. IntPtr pEnumContext = CAPI.CertEnumCertificatesInStore(safeCertStoreHandle, IntPtr.Zero);
  231. while (pEnumContext != IntPtr.Zero) {
  232. X509Certificate2 certificate = new X509Certificate2(pEnumContext);
  233. collection.Add(certificate);
  234. pEnumContext = CAPI.CertEnumCertificatesInStore(safeCertStoreHandle, pEnumContext);
  235. }
  236. return collection;
  237. }
  238. [SecurityCritical]
  239. internal static unsafe int BuildChain (IntPtr hChainEngine,
  240. SafeCertContextHandle pCertContext,
  241. X509Certificate2Collection extraStore,
  242. OidCollection applicationPolicy,
  243. OidCollection certificatePolicy,
  244. X509RevocationMode revocationMode,
  245. X509RevocationFlag revocationFlag,
  246. DateTime verificationTime,
  247. TimeSpan timeout,
  248. ref SafeCertChainHandle ppChainContext) {
  249. if (pCertContext == null || pCertContext.IsInvalid)
  250. throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_InvalidContextHandle"), "pCertContext");
  251. SafeCertStoreHandle hCertStore = SafeCertStoreHandle.InvalidHandle;
  252. if (extraStore != null && extraStore.Count > 0)
  253. hCertStore = X509Utils.ExportToMemoryStore(extraStore);
  254. CAPI.CERT_CHAIN_PARA ChainPara = new CAPI.CERT_CHAIN_PARA();
  255. // Initialize the structure size.
  256. ChainPara.cbSize = (uint) Marshal.SizeOf(ChainPara);
  257. // Application policy
  258. SafeLocalAllocHandle applicationPolicyHandle = SafeLocalAllocHandle.InvalidHandle;
  259. if (applicationPolicy != null && applicationPolicy.Count > 0) {
  260. ChainPara.RequestedUsage.dwType = CAPI.USAGE_MATCH_TYPE_AND;
  261. ChainPara.RequestedUsage.Usage.cUsageIdentifier = (uint) applicationPolicy.Count;
  262. applicationPolicyHandle = X509Utils.CopyOidsToUnmanagedMemory(applicationPolicy);
  263. ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = applicationPolicyHandle.DangerousGetHandle();
  264. }
  265. // Certificate policy
  266. SafeLocalAllocHandle certificatePolicyHandle = SafeLocalAllocHandle.InvalidHandle;
  267. if (certificatePolicy != null && certificatePolicy.Count > 0) {
  268. ChainPara.RequestedIssuancePolicy.dwType = CAPI.USAGE_MATCH_TYPE_AND;
  269. ChainPara.RequestedIssuancePolicy.Usage.cUsageIdentifier = (uint) certificatePolicy.Count;
  270. certificatePolicyHandle = X509Utils.CopyOidsToUnmanagedMemory(certificatePolicy);
  271. ChainPara.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = certificatePolicyHandle.DangerousGetHandle();
  272. }
  273. ChainPara.dwUrlRetrievalTimeout = (uint) timeout.Milliseconds;
  274. _FILETIME ft = new _FILETIME();
  275. *((long*) &ft) = verificationTime.ToFileTime();
  276. uint flags = X509Utils.MapRevocationFlags(revocationMode, revocationFlag);
  277. // Build the chain.
  278. if (!CAPI.CAPISafe.CertGetCertificateChain(hChainEngine,
  279. pCertContext,
  280. ref ft,
  281. hCertStore,
  282. ref ChainPara,
  283. flags,
  284. IntPtr.Zero,
  285. ref ppChainContext))
  286. return Marshal.GetHRForLastWin32Error();
  287. applicationPolicyHandle.Dispose();
  288. certificatePolicyHandle.Dispose();
  289. return CAPI.S_OK;
  290. }
  291. //
  292. // Verifies whether a certificate is valid for the specified policy.
  293. // S_OK means the certificate is valid for the specified policy.
  294. // S_FALSE means the certificate is invalid for the specified policy.
  295. // Anything else is an error.
  296. //
  297. [SecurityCritical]
  298. internal static unsafe int VerifyCertificate (SafeCertContextHandle pCertContext,
  299. OidCollection applicationPolicy,
  300. OidCollection certificatePolicy,
  301. X509RevocationMode revocationMode,
  302. X509RevocationFlag revocationFlag,
  303. DateTime verificationTime,
  304. TimeSpan timeout,
  305. X509Certificate2Collection extraStore,
  306. IntPtr pszPolicy,
  307. IntPtr pdwErrorStatus) {
  308. if (pCertContext == null || pCertContext.IsInvalid)
  309. throw new ArgumentException("pCertContext");
  310. CAPI.CERT_CHAIN_POLICY_PARA PolicyPara = new CAPI.CERT_CHAIN_POLICY_PARA(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_PARA)));
  311. CAPI.CERT_CHAIN_POLICY_STATUS PolicyStatus = new CAPI.CERT_CHAIN_POLICY_STATUS(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_STATUS)));
  312. // Build the chain.
  313. SafeCertChainHandle pChainContext = SafeCertChainHandle.InvalidHandle;
  314. int hr = X509Utils.BuildChain(new IntPtr(CAPI.HCCE_CURRENT_USER),
  315. pCertContext,
  316. extraStore,
  317. applicationPolicy,
  318. certificatePolicy,
  319. revocationMode,
  320. revocationFlag,
  321. verificationTime,
  322. timeout,
  323. ref pChainContext);
  324. if (hr != CAPI.S_OK)
  325. return hr;
  326. // Verify the chain using the specified policy.
  327. if (CAPI.CAPISafe.CertVerifyCertificateChainPolicy(pszPolicy, pChainContext, ref PolicyPara, ref PolicyStatus)) {
  328. if (pdwErrorStatus != IntPtr.Zero)
  329. *(uint*) pdwErrorStatus = PolicyStatus.dwError;
  330. if (PolicyStatus.dwError != 0)
  331. return CAPI.S_FALSE;
  332. } else {
  333. // The API failed.
  334. return Marshal.GetHRForLastWin32Error();
  335. }
  336. return CAPI.S_OK;
  337. }
  338. }
  339. }