Tuesday, January 10, 2012

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);

  // 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

  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 ...
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.