"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)
Comments
Anonymous
May 22, 2012
Nice one, helped me a lot.Anonymous
September 17, 2013
Sort of. I'm getting this error in PowerShell with some certificates, but not others. What properties need to be set/validated on the cert template to ensure that the correct type of cert is generated?ThanksAnonymous
July 24, 2014
The comment has been removedAnonymous
January 08, 2015
Hi, Great Job!. One question, how can i obtain the private key?Anonymous
January 23, 2015
Take a look here Ben,I experienced the same thing, they talk about it here.www.iis.net/.../troubleshooting-ssl-related-issues-server-certificateAnonymous
October 08, 2015
I've also seen this problem but the cause was permissions on the MachineKeys directory being incorrect. After changing these permissions the current certificates carry on working. Subsequent certificate imports , however, appear to still work but the private key has its permissions correupted and when read presents as though it has a CNG private key. I had to delete the certificate, correct the permissions and reimport.Anonymous
November 03, 2015
Ian , you mention that you have seen problem related to permissions on MachineKeys directory. Can you give me more details on where can I correct the permissions for it?