Invalid provider type specified when accessing X509Certificate2.PrivateKey

Today, I was attempting to digitally sign a byte array with my private key so that I could produce an event on the event bus and a consumer could ensure that the message came from me and was not modified while in transit.

public byte[] SignData(byte[] data)
{
  X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
  certStore.Open(OpenFlags.ReadOnly);

  // the DN I get is CN=name,CN=Users,DC=example,DC=com
  // but the DN on the cert has spaces after each comma
  string spacedDN = UserPrincipal.Current.DistinguishedName.Replace(",", ", ");

  X509Certificate2 cert = certStore.Certificates
    .Find(
      X509FindType.FindBySubjectDistinguishedName,
      spacedDN,
      true)
    .OfType<X509Certificate2>()
    .FirstOrDefault();

  if (null == cert) { // handle no cert }

  RSACryptoServiceProvider rsaProvider = cert.PrivateKey as RSACryptoServiceProvider;
  return rsaProvider.SignData(data, new SHA1CryptoServiceProvider());
}

When I run this as myself, I get the following exception:  
System.Security.Cryptography.CryptographicException: Invalid provider type specified.
... the stack trace boils down to ...
System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
This wasn't a terribly enlightening exception message, so I asked google and found this link and this link.  It turns out that by using the newest Certificate Templates (version 3), I am using Microsoft's new Key Storage Provider (KSP), and not the Cryptographic Storage Provider (CSP) that we normally expect.  Windows Server 2008-based certificate templates use the KSP as part of what M$ calls Cryptography Next Generation (CNG).  While .NET 4 does provide managed implementations of the CNG stuff, they are not FIPS-compliant, which is a deal-breaker for me.

So what is a FIPS-constrained boy to do?  Well, I had to delete my auto-enrollment templates that were Windows Server 2008-based and replace them with Windows Server 2003-based templates.  When you duplicate a template using the Certificate Templates MMC snap-in, it will ask you to choose between 2008-based and 2003-based.  Select 2003-based and then revoke any certs you may have issued based on the 2008-based templates.  Let auto-enrollment take its course and then you'll find that you no longer get the invalid provider exception when you access your private key.

Comments

  1. Hi John - I have been mulling over your article and just wondered if you know if KSP keys are stored here: ../Microsoft/Crypto/Keys and CSP keys are stored here: ../Microsoft/Crypto/RSA.

    I ask because I'm puzzled why a private key someone has given me keeps getting created in ../Microsoft/Crypto/Keys while I'm used to them being created here: ../Microsoft/Crypto/RSA.

    The error I get when I try to access the key in BizTalk 2010 is the same as you got.

    It's a long shot but you might know!

    ReplyDelete
    Replies
    1. Hi James,

      I'm having the same issue with our BizTalk 2013R2 CU2 server when calling a WCF-WSHttp end-point using a certificate for the Message Security.

      Were you able to resolve this issue? If so what did you do to get it fixed.

      Thanks

      Delete
  2. The problem for me was the template under which the certificate was issued. At the certificate authority, when you define a template, you're given the choice between 2008-based and 2003-based templates. Once you select 2003-based, you can re-issue yourself a certificate and you shouldn't have problems any longer because PrivateKey should then be accessible.

    ReplyDelete
  3. I am new to accessing the X509Certificate2.PrivateKey. I am using microsoft cryptographic storage provider(CSP).Can you tell me what exactly 2008-based and 2003-based templates.

    ReplyDelete

Post a Comment

Popular posts from this blog

Establishing a SSL connection to RabbitMQ using the .NET client

Join CentOS to a Windows Domain

Configure a FIPS 140-2 Compliant Java Provider on RedHat/CentOS/Fedora