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

https://github.com/kumpera/mono · C# · 112 lines · 84 code · 16 blank · 12 comment · 12 complexity · a8998892c0d20ed48aedccf6bcf9a4c9 MD5 · raw file

  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. using System.Runtime.InteropServices;
  3. using Microsoft.Win32.SafeHandles;
  4. namespace System.Security.Cryptography.X509Certificates
  5. {
  6. /// <summary>
  7. /// Provides extension methods for retrieving <see cref="RSA" /> implementations for the
  8. /// public and private keys of a <see cref="X509Certificate2" />.
  9. /// </summary>
  10. public static class RSACertificateExtensions
  11. {
  12. /// <summary>
  13. /// Gets the <see cref="RSA" /> public key from the certificate or null if the certificate does not have an RSA public key.
  14. /// </summary>
  15. [SecuritySafeCritical]
  16. public static RSA GetRSAPublicKey(this X509Certificate2 certificate)
  17. {
  18. if (certificate == null)
  19. {
  20. throw new ArgumentNullException("certificate");
  21. }
  22. if (!IsRSA(certificate))
  23. {
  24. return null;
  25. }
  26. PublicKey publicKey = certificate.PublicKey;
  27. AsnEncodedData asn = publicKey.EncodedKeyValue;
  28. IntPtr structType = new IntPtr(CapiNative.CNG_RSA_PUBLIC_KEY_BLOB);
  29. SafeLocalAllocHandle cngBlobHandle;
  30. uint cngBlobLength;
  31. bool result = CapiNative.DecodeObject(structType, asn.RawData, out cngBlobHandle, out cngBlobLength);
  32. if (!result)
  33. {
  34. throw new CryptographicException(Marshal.GetLastWin32Error());
  35. }
  36. byte[] cngBlob = new byte[cngBlobLength];
  37. using (cngBlobHandle)
  38. {
  39. Marshal.Copy(cngBlobHandle.DangerousGetHandle(), cngBlob, 0, cngBlob.Length);
  40. }
  41. CngKey key = CngKey.Import(cngBlob, CngKeyBlobFormat.GenericPublicBlob);
  42. return new RSACng(key);
  43. }
  44. /// <summary>
  45. /// Gets the <see cref="RSA" /> private key from the certificate or null if the certificate does not have an RSA private key.
  46. /// </summary>
  47. [SecuritySafeCritical]
  48. public static RSA GetRSAPrivateKey(this X509Certificate2 certificate)
  49. {
  50. if (certificate == null)
  51. {
  52. throw new ArgumentNullException("certificate");
  53. }
  54. if (!certificate.HasPrivateKey || !IsRSA(certificate))
  55. {
  56. return null;
  57. }
  58. using (SafeCertContextHandle certificateContext = X509Native.GetCertificateContext(certificate))
  59. using (SafeNCryptKeyHandle privateKeyHandle = X509Native.TryAcquireCngPrivateKey(certificateContext))
  60. {
  61. if (privateKeyHandle == null)
  62. {
  63. if (LocalAppContextSwitches.DontReliablyClonePrivateKey)
  64. return (RSA)certificate.PrivateKey;
  65. // fall back to CAPI if we cannot acquire the key using CNG.
  66. RSACryptoServiceProvider rsaCsp = (RSACryptoServiceProvider)certificate.PrivateKey;
  67. CspParameters cspParameters = DSACertificateExtensions.CopyCspParameters(rsaCsp);
  68. RSACryptoServiceProvider clone = new RSACryptoServiceProvider(cspParameters);
  69. return clone;
  70. }
  71. CngKey key = CngKey.Open(privateKeyHandle, CngKeyHandleOpenOptions.None);
  72. return new RSACng(key);
  73. }
  74. }
  75. private static bool IsRSA(X509Certificate2 certificate)
  76. {
  77. uint algorithmId = OidToAlgorithmId(certificate.PublicKey.Oid);
  78. switch (algorithmId)
  79. {
  80. case CapiNative.CALG_RSA_SIGN:
  81. case CapiNative.CALG_RSA_KEYX:
  82. return true;
  83. default:
  84. return false;
  85. }
  86. }
  87. private static uint OidToAlgorithmId(Oid oid)
  88. {
  89. using (SafeLocalAllocHandle oidHandle = X509Utils.StringToAnsiPtr(oid.Value))
  90. {
  91. CapiNative.CRYPT_OID_INFO oidInfo = CapiNative.CryptFindOIDInfo(CapiNative.CRYPT_OID_INFO_OID_KEY, oidHandle, 0);
  92. return oidInfo.Algid;
  93. }
  94. }
  95. }
  96. }