Reputation: 33
I'm attempting to apply an rsa-sha512 signature to a message, using a certificate on the local HDD. The final SignData raises a cryptographic exception "Invalid algorithm specified". However, if I use SignData on a new instance of RSACryptoServiceProvider (created by importing an export of the original RSACryptoServiceProvider), I don't get that exception. Is there some reason that the original version raises the exception? Since the "copy" is evidently different, I'd prefer to use the original.
The c# code I'm using is as follows:
X509Certificate2 cert = new X509Certificate2("C:\\Certs\\" + certName + ".p12", certPassword, X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PrivateKey;
UTF8Encoding ByteConverter = new UTF8Encoding();
byte[] unsignedBytes = ByteConverter.GetBytes(unsignedText);
byte[] signature;
//This raises an exception, "Invalid algorithm specified."
signature = csp.SignData(unsignedBytes, new SHA512CryptoServiceProvider());
//But if I make a copy of the RSACryptoServiceProvider, no exception is raised
RSACryptoServiceProvider cspCopy = new RSACryptoServiceProvider();
RSAParameters Key = csp.ExportParameters(true);
cspCopy.ImportParameters(Key);
signature = cspCopy.SignData(unsignedBytes, new SHA512CryptoServiceProvider());
Upvotes: 2
Views: 2164
Reputation: 33088
To quote myself from a previous answer:
Software-backed RSACryptoServiceProvider is only capable of doing RSA signature using a SHA-2 digest algorithm when the CSP ProviderType value is 24 (PROV_RSA_AES) and the ProviderName is "Microsoft Enhanced RSA and AES Cryptographic Provider" (MS_ENH_RSA_AES_PROV). Hardware may or may not require PROV_RSA_AES, just depends on the hardware.
In this case, the PFX identifies the private key to belong to an older CSP (or, maybe, it has no CSP identifier and the PFX import is picking bad defaults). For software keys it's possible to extract the CspParameterInfo data from the key and re-open it using ProviderType 24, which is one way to work around the problem. Exporting the raw RSA parameters and importing it into a new object (which defaults to ProviderType 24) is a more aggressive workaround.
The better way to work around the problem is to abandon RSACryptoServiceProvider. Instead of using cert.PrivateKey
, use cert.GetRSAPrivateKey()
, which will almost always return an RSACng instance, which doesn't have this problem (but don't cast it if you can avoid it (if nothing else, see "almost" always)).
byte[] signature;
using (RSA rsa = cert.GetRSAPrivateKey())
{
signature = rsa.SignData(
unsignedBytes,
HashAlgorithmName.SHA512,
RSASignaturePadding.Pkcs1);
}
The using
statement is correct for GetRSAPrivateKey
, because it returns a distinct object per call.
RSACng and GetRSAPrivateKey both require .NET 4.6, but that's more than two years old at this point (and 4 (and a half) newer releases have happened in that time), so shouldn't cause you difficulty as a dependency.
Upvotes: 4