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 commastring 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());}
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.
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.
ReplyDeleteI 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!