PageRenderTime 32ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 544 lines | 469 code | 64 blank | 11 comment | 43 complexity | 075b70ea8112a3d9775755c33ac30ed9 MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System;
  5. using System.Text;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using System.Collections.Generic;
  9. using System.Runtime.InteropServices;
  10. using Internal.NativeCrypto;
  11. using Internal.Cryptography;
  12. using Internal.Cryptography.Pal.Native;
  13. using FILETIME = Internal.Cryptography.Pal.Native.FILETIME;
  14. using System.Security.Cryptography;
  15. using SafeX509ChainHandle = Microsoft.Win32.SafeHandles.SafeX509ChainHandle;
  16. using System.Security.Cryptography.X509Certificates;
  17. namespace Internal.Cryptography.Pal
  18. {
  19. internal sealed partial class CertificatePal : IDisposable, ICertificatePal
  20. {
  21. public static ICertificatePal FromHandle(IntPtr handle)
  22. {
  23. if (handle == IntPtr.Zero)
  24. throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
  25. SafeCertContextHandle safeCertContextHandle = Interop.crypt32.CertDuplicateCertificateContext(handle);
  26. if (safeCertContextHandle.IsInvalid)
  27. throw ErrorCode.HRESULT_INVALID_HANDLE.ToCryptographicException();
  28. CRYPTOAPI_BLOB dataBlob;
  29. int cbData = 0;
  30. bool deleteKeyContainer = Interop.crypt32.CertGetCertificateContextProperty(safeCertContextHandle, CertContextPropId.CERT_DELETE_KEYSET_PROP_ID, out dataBlob, ref cbData);
  31. return new CertificatePal(safeCertContextHandle, deleteKeyContainer);
  32. }
  33. public IntPtr Handle
  34. {
  35. get { return _certContext.DangerousGetHandle(); }
  36. }
  37. public string Issuer
  38. {
  39. get
  40. {
  41. return GetIssuerOrSubject(issuer: true);
  42. }
  43. }
  44. public string Subject
  45. {
  46. get
  47. {
  48. return GetIssuerOrSubject(issuer: false);
  49. }
  50. }
  51. public byte[] Thumbprint
  52. {
  53. get
  54. {
  55. int cbData = 0;
  56. if (!Interop.crypt32.CertGetCertificateContextProperty(_certContext, CertContextPropId.CERT_SHA1_HASH_PROP_ID, null, ref cbData))
  57. throw Marshal.GetHRForLastWin32Error().ToCryptographicException();
  58. byte[] thumbprint = new byte[cbData];
  59. if (!Interop.crypt32.CertGetCertificateContextProperty(_certContext, CertContextPropId.CERT_SHA1_HASH_PROP_ID, thumbprint, ref cbData))
  60. throw Marshal.GetHRForLastWin32Error().ToCryptographicException();;
  61. return thumbprint;
  62. }
  63. }
  64. public string KeyAlgorithm
  65. {
  66. get
  67. {
  68. unsafe
  69. {
  70. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  71. string keyAlgorithm = Marshal.PtrToStringAnsi(pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId);
  72. GC.KeepAlive(this);
  73. return keyAlgorithm;
  74. }
  75. }
  76. }
  77. public byte[] KeyAlgorithmParameters
  78. {
  79. get
  80. {
  81. unsafe
  82. {
  83. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  84. string keyAlgorithmOid = Marshal.PtrToStringAnsi(pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId);
  85. int algId;
  86. if (keyAlgorithmOid == Oids.RsaRsa)
  87. algId = AlgId.CALG_RSA_KEYX; // Fast-path for the most common case.
  88. else
  89. algId = OidInfo.FindOidInfo(CryptOidInfoKeyType.CRYPT_OID_INFO_OID_KEY, keyAlgorithmOid, OidGroup.PublicKeyAlgorithm, fallBackToAllGroups: true).AlgId;
  90. unsafe
  91. {
  92. byte* NULL_ASN_TAG = (byte*)0x5;
  93. byte[] keyAlgorithmParameters;
  94. if (algId == AlgId.CALG_DSS_SIGN
  95. && pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData == 0
  96. && pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.pbData == NULL_ASN_TAG)
  97. {
  98. //
  99. // DSS certificates may not have the DSS parameters in the certificate. In this case, we try to build
  100. // the certificate chain and propagate the parameters down from the certificate chain.
  101. //
  102. keyAlgorithmParameters = PropagateKeyAlgorithmParametersFromChain();
  103. }
  104. else
  105. {
  106. keyAlgorithmParameters = pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.ToByteArray();
  107. }
  108. GC.KeepAlive(this);
  109. return keyAlgorithmParameters;
  110. }
  111. }
  112. }
  113. }
  114. private byte[] PropagateKeyAlgorithmParametersFromChain()
  115. {
  116. unsafe
  117. {
  118. SafeX509ChainHandle certChainContext = null;
  119. try
  120. {
  121. int cbData = 0;
  122. if (!Interop.crypt32.CertGetCertificateContextProperty(_certContext, CertContextPropId.CERT_PUBKEY_ALG_PARA_PROP_ID, null, ref cbData))
  123. {
  124. CERT_CHAIN_PARA chainPara = new CERT_CHAIN_PARA();
  125. chainPara.cbSize = sizeof(CERT_CHAIN_PARA);
  126. if (!Interop.crypt32.CertGetCertificateChain(ChainEngine.HCCE_CURRENT_USER, _certContext, (FILETIME*)null, SafeCertStoreHandle.InvalidHandle, ref chainPara, CertChainFlags.None, IntPtr.Zero, out certChainContext))
  127. throw Marshal.GetHRForLastWin32Error().ToCryptographicException();;
  128. if (!Interop.crypt32.CertGetCertificateContextProperty(_certContext, CertContextPropId.CERT_PUBKEY_ALG_PARA_PROP_ID, null, ref cbData))
  129. throw Marshal.GetHRForLastWin32Error().ToCryptographicException();;
  130. }
  131. byte[] keyAlgorithmParameters = new byte[cbData];
  132. if (!Interop.crypt32.CertGetCertificateContextProperty(_certContext, CertContextPropId.CERT_PUBKEY_ALG_PARA_PROP_ID, keyAlgorithmParameters, ref cbData))
  133. throw Marshal.GetHRForLastWin32Error().ToCryptographicException();;
  134. return keyAlgorithmParameters;
  135. }
  136. finally
  137. {
  138. if (certChainContext != null)
  139. certChainContext.Dispose();
  140. }
  141. }
  142. }
  143. public byte[] PublicKeyValue
  144. {
  145. get
  146. {
  147. unsafe
  148. {
  149. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  150. byte[] publicKey = pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.ToByteArray();
  151. GC.KeepAlive(this);
  152. return publicKey;
  153. }
  154. }
  155. }
  156. public byte[] SerialNumber
  157. {
  158. get
  159. {
  160. unsafe
  161. {
  162. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  163. byte[] serialNumber = pCertContext->pCertInfo->SerialNumber.ToByteArray();
  164. GC.KeepAlive(this);
  165. return serialNumber;
  166. }
  167. }
  168. }
  169. public string SignatureAlgorithm
  170. {
  171. get
  172. {
  173. unsafe
  174. {
  175. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  176. string signatureAlgorithm = Marshal.PtrToStringAnsi(pCertContext->pCertInfo->SignatureAlgorithm.pszObjId);
  177. GC.KeepAlive(this);
  178. return signatureAlgorithm;
  179. }
  180. }
  181. }
  182. public DateTime NotAfter
  183. {
  184. get
  185. {
  186. unsafe
  187. {
  188. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  189. DateTime notAfter = pCertContext->pCertInfo->NotAfter.ToDateTime();
  190. GC.KeepAlive(this);
  191. return notAfter;
  192. }
  193. }
  194. }
  195. public DateTime NotBefore
  196. {
  197. get
  198. {
  199. unsafe
  200. {
  201. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  202. DateTime notBefore = pCertContext->pCertInfo->NotBefore.ToDateTime();
  203. GC.KeepAlive(this);
  204. return notBefore;
  205. }
  206. }
  207. }
  208. public byte[] RawData
  209. {
  210. get
  211. {
  212. unsafe
  213. {
  214. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  215. int count = pCertContext->cbCertEncoded;
  216. byte[] rawData = new byte[count];
  217. Marshal.Copy((IntPtr)(pCertContext->pbCertEncoded), rawData, 0, count);
  218. GC.KeepAlive(this);
  219. return rawData;
  220. }
  221. }
  222. }
  223. public int Version
  224. {
  225. get
  226. {
  227. unsafe
  228. {
  229. CERT_CONTEXT* pCertContext = _certContext.CertContext;
  230. int version = pCertContext->pCertInfo->dwVersion + 1;
  231. GC.KeepAlive(this);
  232. return version;
  233. }
  234. }
  235. }
  236. public bool Archived
  237. {
  238. get
  239. {
  240. int uninteresting = 0;
  241. bool archivePropertyExists = Interop.crypt32.CertGetCertificateContextProperty(_certContext, CertContextPropId.CERT_ARCHIVED_PROP_ID, null, ref uninteresting);
  242. return archivePropertyExists;
  243. }
  244. set
  245. {
  246. unsafe
  247. {
  248. CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(0, (byte*)null);
  249. CRYPTOAPI_BLOB* pValue = value ? &blob : (CRYPTOAPI_BLOB*)null;
  250. if (!Interop.crypt32.CertSetCertificateContextProperty(_certContext, CertContextPropId.CERT_ARCHIVED_PROP_ID, CertSetPropertyFlags.None, pValue))
  251. throw Marshal.GetLastWin32Error().ToCryptographicException();
  252. }
  253. }
  254. }
  255. public string FriendlyName
  256. {
  257. get
  258. {
  259. int cbData = 0;
  260. if (!Interop.crypt32.CertGetCertificateContextPropertyString(_certContext, CertContextPropId.CERT_FRIENDLY_NAME_PROP_ID, null, ref cbData))
  261. return string.Empty;
  262. StringBuilder sb = new StringBuilder((cbData + 1) / 2);
  263. if (!Interop.crypt32.CertGetCertificateContextPropertyString(_certContext, CertContextPropId.CERT_FRIENDLY_NAME_PROP_ID, sb, ref cbData))
  264. return string.Empty;
  265. return sb.ToString();
  266. }
  267. set
  268. {
  269. string friendlyName = (value == null) ? string.Empty : value;
  270. unsafe
  271. {
  272. IntPtr pFriendlyName = Marshal.StringToHGlobalUni(friendlyName);
  273. try
  274. {
  275. CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(checked(2 * (friendlyName.Length + 1)), (byte*)pFriendlyName);
  276. if (!Interop.crypt32.CertSetCertificateContextProperty(_certContext, CertContextPropId.CERT_FRIENDLY_NAME_PROP_ID, CertSetPropertyFlags.None, &blob))
  277. throw Marshal.GetLastWin32Error().ToCryptographicException();
  278. }
  279. finally
  280. {
  281. Marshal.FreeHGlobal(pFriendlyName);
  282. }
  283. }
  284. }
  285. }
  286. public X500DistinguishedName SubjectName
  287. {
  288. get
  289. {
  290. unsafe
  291. {
  292. byte[] encodedSubjectName = _certContext.CertContext->pCertInfo->Subject.ToByteArray();
  293. X500DistinguishedName subjectName = new X500DistinguishedName(encodedSubjectName);
  294. GC.KeepAlive(this);
  295. return subjectName;
  296. }
  297. }
  298. }
  299. public X500DistinguishedName IssuerName
  300. {
  301. get
  302. {
  303. unsafe
  304. {
  305. byte[] encodedIssuerName = _certContext.CertContext->pCertInfo->Issuer.ToByteArray();
  306. X500DistinguishedName issuerName = new X500DistinguishedName(encodedIssuerName);
  307. GC.KeepAlive(this);
  308. return issuerName;
  309. }
  310. }
  311. }
  312. public IEnumerable<X509Extension> Extensions
  313. {
  314. get
  315. {
  316. unsafe
  317. {
  318. CERT_INFO* pCertInfo = _certContext.CertContext->pCertInfo;
  319. int numExtensions = pCertInfo->cExtension;
  320. X509Extension[] extensions = new X509Extension[numExtensions];
  321. for (int i = 0; i < numExtensions; i++)
  322. {
  323. CERT_EXTENSION* pCertExtension = pCertInfo->rgExtension + i;
  324. string oidValue = Marshal.PtrToStringAnsi(pCertExtension->pszObjId);
  325. Oid oid = new Oid(oidValue);
  326. bool critical = pCertExtension->fCritical != 0;
  327. byte[] rawData = pCertExtension->Value.ToByteArray();
  328. extensions[i] = new X509Extension(oid, rawData, critical);
  329. }
  330. GC.KeepAlive(this);
  331. return extensions;
  332. }
  333. }
  334. }
  335. public string GetNameInfo(X509NameType nameType, bool forIssuer)
  336. {
  337. CertNameType certNameType = MapNameType(nameType);
  338. CertNameFlags certNameFlags = forIssuer ? CertNameFlags.CERT_NAME_ISSUER_FLAG : CertNameFlags.None;
  339. CertNameStrTypeAndFlags strType = CertNameStrTypeAndFlags.CERT_X500_NAME_STR | CertNameStrTypeAndFlags.CERT_NAME_STR_REVERSE_FLAG;
  340. int cchCount = Interop.crypt32.CertGetNameString(_certContext, certNameType, certNameFlags, ref strType, null, 0);
  341. if (cchCount == 0)
  342. throw Marshal.GetLastWin32Error().ToCryptographicException();
  343. StringBuilder sb = new StringBuilder(cchCount);
  344. if (Interop.crypt32.CertGetNameString(_certContext, certNameType, certNameFlags, ref strType, sb, cchCount) == 0)
  345. throw Marshal.GetLastWin32Error().ToCryptographicException();
  346. return sb.ToString();
  347. }
  348. public void AppendPrivateKeyInfo(StringBuilder sb)
  349. {
  350. #if NETNATIVE
  351. if (HasPrivateKey)
  352. {
  353. // Similar to the Unix implementation, in UWP merely acknowledge that there -is- a private key.
  354. sb.AppendLine();
  355. sb.AppendLine();
  356. sb.AppendLine("[Private Key]");
  357. }
  358. #else
  359. CspKeyContainerInfo cspKeyContainerInfo = null;
  360. try
  361. {
  362. if (HasPrivateKey)
  363. {
  364. CspParameters parameters = GetPrivateKey();
  365. cspKeyContainerInfo = new CspKeyContainerInfo(parameters);
  366. }
  367. }
  368. // We could not access the key container. Just return.
  369. catch (CryptographicException) { }
  370. if (cspKeyContainerInfo == null)
  371. return;
  372. sb.Append(Environment.NewLine + Environment.NewLine + "[Private Key]");
  373. sb.Append(Environment.NewLine + " Key Store: ");
  374. sb.Append(cspKeyContainerInfo.MachineKeyStore ? "Machine" : "User");
  375. sb.Append(Environment.NewLine + " Provider Name: ");
  376. sb.Append(cspKeyContainerInfo.ProviderName);
  377. sb.Append(Environment.NewLine + " Provider type: ");
  378. sb.Append(cspKeyContainerInfo.ProviderType);
  379. sb.Append(Environment.NewLine + " Key Spec: ");
  380. sb.Append(cspKeyContainerInfo.KeyNumber);
  381. sb.Append(Environment.NewLine + " Key Container Name: ");
  382. sb.Append(cspKeyContainerInfo.KeyContainerName);
  383. try
  384. {
  385. string uniqueKeyContainer = cspKeyContainerInfo.UniqueKeyContainerName;
  386. sb.Append(Environment.NewLine + " Unique Key Container Name: ");
  387. sb.Append(uniqueKeyContainer);
  388. }
  389. catch (CryptographicException) { }
  390. catch (NotSupportedException) { }
  391. bool b = false;
  392. try
  393. {
  394. b = cspKeyContainerInfo.HardwareDevice;
  395. sb.Append(Environment.NewLine + " Hardware Device: ");
  396. sb.Append(b);
  397. }
  398. catch (CryptographicException) { }
  399. try
  400. {
  401. b = cspKeyContainerInfo.Removable;
  402. sb.Append(Environment.NewLine + " Removable: ");
  403. sb.Append(b);
  404. }
  405. catch (CryptographicException) { }
  406. try
  407. {
  408. b = cspKeyContainerInfo.Protected;
  409. sb.Append(Environment.NewLine + " Protected: ");
  410. sb.Append(b);
  411. }
  412. catch (CryptographicException) { }
  413. catch (NotSupportedException) { }
  414. #endif // #if NETNATIVE / #else
  415. }
  416. public void Dispose()
  417. {
  418. SafeCertContextHandle certContext = _certContext;
  419. _certContext = null;
  420. if (certContext != null && !certContext.IsInvalid)
  421. {
  422. certContext.Dispose();
  423. }
  424. }
  425. internal SafeCertContextHandle CertContext
  426. {
  427. get
  428. {
  429. SafeCertContextHandle certContext = Interop.crypt32.CertDuplicateCertificateContext(_certContext.DangerousGetHandle());
  430. GC.KeepAlive(_certContext);
  431. return certContext;
  432. }
  433. }
  434. private static CertNameType MapNameType(X509NameType nameType)
  435. {
  436. switch (nameType)
  437. {
  438. case X509NameType.SimpleName:
  439. return CertNameType.CERT_NAME_SIMPLE_DISPLAY_TYPE;
  440. case X509NameType.EmailName:
  441. return CertNameType.CERT_NAME_EMAIL_TYPE;
  442. case X509NameType.UpnName:
  443. return CertNameType.CERT_NAME_UPN_TYPE;
  444. case X509NameType.DnsName:
  445. case X509NameType.DnsFromAlternativeName:
  446. return CertNameType.CERT_NAME_DNS_TYPE;
  447. case X509NameType.UrlName:
  448. return CertNameType.CERT_NAME_URL_TYPE;
  449. default:
  450. throw new ArgumentException(SR.Argument_InvalidNameType);
  451. }
  452. }
  453. private string GetIssuerOrSubject(bool issuer)
  454. {
  455. CertNameFlags flags = issuer ? CertNameFlags.CERT_NAME_ISSUER_FLAG : CertNameFlags.None;
  456. CertNameStringType stringType = CertNameStringType.CERT_X500_NAME_STR | CertNameStringType.CERT_NAME_STR_REVERSE_FLAG;
  457. int cchCount = Interop.crypt32.CertGetNameString(_certContext, CertNameType.CERT_NAME_RDN_TYPE, flags, ref stringType, null, 0);
  458. if (cchCount == 0)
  459. throw Marshal.GetHRForLastWin32Error().ToCryptographicException();;
  460. StringBuilder sb = new StringBuilder(cchCount);
  461. cchCount = Interop.crypt32.CertGetNameString(_certContext, CertNameType.CERT_NAME_RDN_TYPE, flags, ref stringType, sb, cchCount);
  462. if (cchCount == 0)
  463. throw Marshal.GetHRForLastWin32Error().ToCryptographicException();;
  464. return sb.ToString();
  465. }
  466. private CertificatePal(SafeCertContextHandle certContext, bool deleteKeyContainer)
  467. {
  468. if (deleteKeyContainer)
  469. {
  470. // We need to delete any associated key container upon disposition. Thus, replace the safehandle we got with a safehandle whose
  471. // Release() method performs the key container deletion.
  472. SafeCertContextHandle oldCertContext = certContext;
  473. certContext = Interop.crypt32.CertDuplicateCertificateContextWithKeyContainerDeletion(oldCertContext.DangerousGetHandle());
  474. GC.KeepAlive(oldCertContext);
  475. }
  476. _certContext = certContext;
  477. }
  478. private SafeCertContextHandle _certContext;
  479. }
  480. }