"Invalid provider type specified" error when accessing X509Certificate2.PrivateKey on CNG certificates

Hi all, 

You may get the following exception when trying to access X509Certificate2.PrivateKey on a .NET 3.5 (or older) app:

"
System.Security.Cryptography.CryptographicException: Invalid provider type specified.

at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()

"

When this happened to me, I used my CryptoAPI Tracer on the problematic app to find out which CryptoAPI was failing and causing that exception. This was the one:

"
CryptAcquireContextA (0x448)

IN
pszContainer
0078a948 "anycontainername"

pszProvider
00773fb8 "Microsoft Software Key Storage P"
00773fd8 "rovider"

dwProvType
0

dwFlags
0

OUT
hProv
NULL

RESULT
CryptAcquireContextA (0x448) FAILED
LastErrorValue: (HRESULT) 0x80090014 (2148073492) - Invalid provider type specified.
LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0

"

So the certificate I was using was associated to Microsoft Software Key Storage Provider, which is not a CSP but a KSP:

CNG Key Storage Providers
"
Unlike Cryptography API (CryptoAPI), Cryptography API: Next Generation (CNG) separates cryptographic providers from key storage providers (KSPs) . KSPs can be used to create, delete, export, import, open and store keys. Depending on implementation, they can also be used for asymmetric encryption, secret agreement, and signing. Microsoft installs the following KSPs beginning with Windows Vista and Windows Server 2008.
"

This is a CNG certificate! This is what CertUtil.exe returns on the private key of that certificate:

"
Key Container = ...
Unique container name: ...
Provider = Microsoft Software Key Storage Provider
Private key is NOT exportable
Encryption test passed

"

So certutil.exe knows how to deal with this kind of CNG certificates. I used my CryptoAPI Tracer on CertUtil, and this tool is using CryptAcquireCertificatePrivateKey instead of CryptAcquireContext to access the keys of the cert. CryptAcquireCertificatePrivateKey has specific flags to deal with CNG:

CryptAcquireCertificatePrivateKey Function
"
CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG
This function will attempt to obtain the key by using CryptoAPI. If that fails, this function will attempt to obtain the key by using the Cryptography API: Next Generation (CNG).
The pdwKeySpec variable receives the CERT_NCRYPT_KEY_SPEC flag if CNG is used to obtain the key.
...
pdwKeySpec [out]
CERT_NCRYPT_KEY_SPEC
The key is a CNG key.

"

.NET is not CNG aware yet (at least up to version 3.5 SP1) . It uses CryptAcquireContext instead of CryptAcquireCertificatePrivateKey and CryptAcquireContext has no flags to deal with CNG.

A possible workaround to this may be to use CryptoAPI/CNG API directly to deal with CNG keys. That is what certutil.exe does. But if we want an easier and pure .NET solution which understands CNG, this will help to implement it:

CLR Security
"
Security.Cryptography.dll provides a new set of algorithm implementations to augment the built in .NET framework supported algorithms. It also provides some APIs to extend the existing framework cryptography APIs. Within this project you will find:
§ A CNG implementation of the AES, RSA, and TripleDES encryption algorithms
§ A CNG implementation of a random number generator
§ A class that allows dynamically creating algorithms both from this library as well as all of the algorithms that ship with .NET 3.5
§ An enumerator over all of the installed CNG providers on the current machine
§ Extension methods that allow access to all of the keys installed in a CNG provider, as well as all of the algorithms the provider supports

"

If you want to use CNG with this dll, you have to do the following:

1) Change .NET version to 3.5. As far as I know the dll requires us to move at least to that version.

2) Reference Security.Cryptography.dll in your project.

3) Include "using Security.Cryptography.X509Certificates;" statement (in C#, of course) to activate CNG extensions for standard X509Certificate2 class.

Then you may check whether the cert has a CNG key using the extension .HasCngKey() for X509Certificate2. If you have a CNG key, you can then instantiate an RSACng object using the extension .GetCngPrivateKey() for X509Certificate2. You can then i.e. decrypt with RSACng.DecryptValue() .

 

I hope this helps.

Regards,

 

Alex (Alejandro Campos Magencio)