CryptographicException: 'Invalid algorithm specified' when using SHA-512

Viewed 2452

In my WPF app (.NET 4.6), I am required to use a P12 certificate file to sign a string using the SHA-512 algorithm (to include in the header of a web request). I do so as follows:

using (var rsa = myX509Certificate2.GetRSAPrivateKey()) {
  myBytes = rsa.SignData(
    Encoding.UTF8.GetBytes(stringToSign), 
    HashAlgorithmName.SHA512, 
    RSASignaturePadding.Pkcs1
  );
}

This works in testing and for nearly all my customers, but the odd customer gets the following exception:

System.Security.Cryptography.CryptographicException: Invalid algorithm specified.
  at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
  at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
  at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
  at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
  at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
  at System.Security.Cryptography.RSA.SignData(Byte[] data, Int32 offset, Int32 count, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
  at System.Security.Cryptography.RSA.SignData(Byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)

It has happened most recently to a customer on Windows 7 SP1.

I'm struggling to find an answer via existing SO questions or from google in general. From what I can tell, it may be due to an unsupported Windows cryptography service provider being used under-the-hood, but I'm not sure, as I can't replicate the error myself.

Any ideas how to solve this one, either via code or by having affected customers install any particular Windows updates?

1 Answers

If you're using cert.GetRSAPrivateKey() and its giving back an RSACryptoServiceProvider instance that suggests it's not from a PFX, but is from a smart card with an old driver. Since you say it's from a .p12/.pfx, that could PFX contains a reference to an usual Cryptographic Service Provider name that CNG didn't take over (but I've never seen that in a software key). (Occam's Razor makes one inquire: Are you sure they're not accidentally using the wrong certificate?)

If you know it's coming from a PFX, and you've imported it with the Exportable bit, you can manually transform it from RSACryptoServiceProvider to RSACng:

using (RSA rsa = cert.GetRSAPrivateKey())
{
    byte[] toSign = Encoding.UTF8.GetBytes(stringToSign);
    myBytes = null;

    try
    {
        myBytes = rsa.SignData(
            toSign, 
            HashAlgorithmName.SHA512, 
            RSASignaturePadding.Pkcs1);
    }
    catch (CryptographicException)
    {
        try
        {
            using (RSA rsaCng = new RSACng())
            {
                rsaCng.ImportParameters(rsa.ExportParameters(true));

                myBytes = rsaCng.SignData(
                    toSign,
                    HashAlgorithmName.SHA512, 
                    RSASignaturePadding.Pkcs1);
            }
        }
        catch
        {
        }

        if (myBytes == null)
        {
            // Let the original exception continue
            throw;
        }
    }
}

(As an alternative to the original exception throw, you could decide to let the "retry" exception be thrown instead)


Alternatively, Windows 10 fixed a lot of the RSACryptoServiceProvider "invalid algorithm" behaviors, so they could always try upgrading.

Related