0

Following this Microsoft article we are accessing a Private Key Certificate in code which was uploaded to our App Service. We can look up the certificate by its thumbprint, as the article indicates, and can read it but when we try to access its key values using the following code we get an error "CryptographicException: Keyset does not exist.". The error occurs when casting the key to RSACryptoServiceProvider.

This error message is deceptive because I know the keyset does exist. On my development VM, non-Azure I can load the Private Key File like any other certificate into my local machines certificate store and using the same code read they key and it’s values. I did have to grant access to the certificate’s key to the user (IIS application pool identity) that my site runs under locally. You can do this in the Microsoft Management Console (see image). Therefore we a speculating that this is a authorization issue.

Has anyone else encountered this?

Granting Access

        // private key
        if (certificate.HasPrivateKey)
        {
            try
            {
                rsa = certificate.PrivateKey as RSACryptoServiceProvider;
            }
            catch (Exception e)
            {
                var message = $"Certificate key cast to RSACryptoServiceProvider: {e.ToString()} Stacktrace: {e.StackTrace}";
                Error.LogError(message);
                throw new Exception(message);
            }
        }

        if (rsa is null)
        {
            var message = $"Certificate with thumbprint {certificate.Thumbprint} contains no private key.";
            Error.LogError(message);
            throw new Exception(message);
        }

        // signiture
        var cspParam = new CspParameters
        {
            KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
            KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
        };

Stack Trace: 22488 21:52:18 ERROR Certificate key cast to RSACryptoServiceProvider: System.Security.Cryptography.CryptographicException: Keyset does not exist

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() at BCU.Web.Controllers.CovidLiveAgentController.GetAccessToken() Stacktrace: 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() at BCU.Web.Controllers.CovidLiveAgentController.GetAccessToken()

M. Brandel
  • 45
  • 1
  • 7

1 Answers1

0

First of all, never ever touch X509Certificate2.PrivateKey property. It is obsolete and shall not be used if you are in a .NET Framework 4.6+. Instead, you need to use proper extension method to retrieve private key object based on private key algorithm. More details on why PrivateKey property is bad in my blog post: https://www.pkisolutions.com/accessing-and-using-certificate-private-keys-in-net-framework-net-core/

if key is stored in Local Machine store (instead of Current User), user account under which the application is executed must be granted private key Read permissions. In a given case, you should load the certificate into Current User\Personal store instead of Local Machine\Personal as specified in referenced article. This eliminates the key permission granting requirement

Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • Thank you! I'll test with various extension methods. Also some additonal info - this is an unusual case in that, see rerefenced Microsoft atricle, this .pfx cert is uploaded into our Azure App Service's private key collection. So there is not a way to pick which store it goes into or grant permissions to individual users. – M. Brandel Aug 13 '20 at 19:07
  • Even with using extension methods we still seem to have the same issue. We have been adjusting our Azure subscription and Instance count which strangely seem to fix the issue but only intermittently. When we restart our App Service the first try succeeds but then any subsequent calls to the same code fails. – M. Brandel Aug 14 '20 at 20:29