title: Cannot export RSA private key parameters, the requested operation is not supported
body: <p>I have a certificate file provided by another party which I'm loading in my application and cannot export its private key parameters. It looks like the certificate is using CNG rather than CryptoAPI, so I can't access the private key directly, only with GetRSAPrivateKey() method. The method returns <code>RSACngKey</code> rather than <code>RSACryptoServiceProvider</code> which is a different implementation of RSA. The problem is that the returned key seems to be missing <code>CngExportPolicies.AllowPlaintextExport</code> in its export policies, and so I can't export RSA parameters from this certificate. I can reproduce the problem by generating a new certificate that misses necessary export policies:</p>
  
  <pre><code>using System;
  using System.Security.Cryptography;
  using System.Security.Cryptography.X509Certificates;
  
  namespace TestRsaCngConsole
  {
      class Program
      {
          static void Main(string[] args)
          {
              var oldCertificate = CreateCertificate();
              var oldCertificateBytes = oldCertificate.Export(X509ContentType.Pfx, "");
              var newCertificate = new X509Certificate2(oldCertificateBytes, "",
                  X509KeyStorageFlags.Exportable | 
                  X509KeyStorageFlags.MachineKeySet | 
                  X509KeyStorageFlags.PersistKeySet);
  
              LogCertificate(oldCertificate, "old certificate"); // this fails
              LogCertificate(newCertificate, "new certificate"); // works only on Win10
              Console.ReadKey();
          }
  
          private static X509Certificate2 CreateCertificate()
          {
              var keyParams = new CngKeyCreationParameters();
              keyParams.KeyUsage = CngKeyUsages.Signing;
              keyParams.Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
              keyParams.ExportPolicy = CngExportPolicies.AllowExport; // here I don't have AllowPlaintextExport
              keyParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(2048), CngPropertyOptions.None));
              var cngKey = CngKey.Create(CngAlgorithm.Rsa, Guid.NewGuid().ToString(), keyParams);
              var rsaKey = new RSACng(cngKey);
              var req = new CertificateRequest("cn=mah_cert", rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); // requires .net 4.7.2
              var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
              return cert;
          }
  
          private static void LogCertificate(X509Certificate2 certificate, string name)
          {
              Console.WriteLine("----- Testing " + name + " ------");
  
              try
              {
                  var rsaPrivateKey = certificate.GetRSAPrivateKey();
                  var parameters = rsaPrivateKey.ExportParameters(true);
                  Console.WriteLine("Certificate private key RSA parameters were successfully exported.");
  
                  var privateKey = certificate.PrivateKey;
                  Console.WriteLine("Certificate private key is accessible.");
              }
              catch (Exception e)
              {
                  Console.WriteLine(e.ToString());
              }
          }
      }
  }
  </code></pre>
  
  <p>The program shows the following output when running on Windows 10:</p>
  
  <pre><code>----- Testing old certificate ------
  System.Security.Cryptography.CryptographicException: The requested operation is not supported.
  
     at System.Security.Cryptography.NCryptNative.ExportKey(SafeNCryptKeyHandle key, String format)
     at System.Security.Cryptography.CngKey.Export(CngKeyBlobFormat format)
     at System.Security.Cryptography.RSACng.ExportParameters(Boolean includePrivateParameters)
     at TestRsaCngConsole.Program.LogCertificate(X509Certificate2 certificate, String name) in D:\Projects\TestRsaCngConsole\TestRsaCngConsole\Program.cs:line 44
  ----- Testing new certificate ------
  Certificate private key RSA parameters were successfully exported.
  Certificate private key is accessible.
  </code></pre>
  
  <p>So the first certificate fails to export the private key, because it is missing AllowPlaintextExport flag in its export policies. But after re-loading the old certificate with exportable flags I can export the new certificate parameters just fine. However it doesn't work on Windows Server 2012 or Windows Server 2016 and throws exceptions for both certificates:</p>
  
  <pre><code>----- Testing old certificate ------
  System.Security.Cryptography.CryptographicException: The requested operation is not supported.
  
     at System.Security.Cryptography.NCryptNative.ExportKey(SafeNCryptKeyHandle key, String format)
     at System.Security.Cryptography.CngKey.Export(CngKeyBlobFormat format)
     at System.Security.Cryptography.RSACng.ExportParameters(Boolean includePrivateParameters)
     at TestRsaCngConsole.Program.LogCertificate(X509Certificate2 certificate, String name) in D:\Projects\TestRsaCngConsole\TestRsaCngConsole\Program.cs:line 44
  ----- Testing new certificate ------
  System.Security.Cryptography.CryptographicException: The requested operation is not supported.
  
     at System.Security.Cryptography.NCryptNative.ExportKey(SafeNCryptKeyHandle key, String format)
     at System.Security.Cryptography.CngKey.Export(CngKeyBlobFormat format)
     at System.Security.Cryptography.RSACng.ExportParameters(Boolean includePrivateParameters)
     at TestRsaCngConsole.Program.LogCertificate(X509Certificate2 certificate, String name) in D:\Projects\TestRsaCngConsole\TestRsaCngConsole\Program.cs:line 44
  </code></pre>
  
  <p>I need to be able to fix the certificate and make it possible to export the RSA parameters, even if the cert was originally missing AllowPlaintextExport. What is so different on Windows Server and is there a way to fix the certificate?</p>