SHA256 signing stops working in .NET 4.5

Thuan picture Thuan · Oct 27, 2013 · Viewed 11.3k times · Source

We have a piece of code which creates a SigningCredentials object to use to sign xml document by using SHA256 algorithm. It works with .NET 3.5 perfectly. However, when we upgrade our codebase to .NET 4.5, it stops working. Same code, same certificate! I have spent hours on debugging and searching on the internet without any luck.

Could anyone please tell me what the problem here is? Thank you in advance.

Code to create SigningCredentials:

public SigningCredentials CreateSigningCredentials(X509Certificate2 cert)
{
    var ski = new SecurityKeyIdentifier(new X509RawDataKeyIdentifierClause(cert));
    return new SigningCredentials(new X509AsymmetricSecurityKey(cert), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2001/04/xmlenc#sha256", ski);
}

Exception:

[CryptographicException: Invalid algorithm specified.
]
   System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr) +41
   System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature) +0
   System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash) +118
   System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash) +334
   System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash) +321
   System.IdentityModel.SignedXml.ComputeSignature(HashAlgorithm hash, AsymmetricSignatureFormatter formatter, String signatureMethod) +323
   System.IdentityModel.SignedXml.ComputeSignature(SecurityKey signingKey) +690
   System.IdentityModel.EnvelopedSignatureWriter.ComputeSignature() +338
   System.IdentityModel.EnvelopedSignatureWriter.OnEndRootElement() +278
   System.IdentityModel.Metadata.MetadataSerializer.WriteEntityDescriptor(XmlWriter inputWriter, EntityDescriptor entityDescriptor) +1109

Answer

tyger picture tyger · Feb 25, 2015

Had the same problem with XmlDsig (trying to make enveloping signature of xml document with RSA-SHA256 algorithm). First I were getting exception

System.Security.Cryptography.CryptographicException: SignatureDescription could not be created for the signature algorithm supplied.

Then I found mention of RSAPKCS1SHA256SignatureDescription - signature description implementation for RSA-SHA256 signatures.
Full implementation here:
http://clrsecurity.codeplex.com/SourceControl/changeset/view/47833#269110
or here: https://gist.github.com/sneal/f35de432115b840c4c1f

You have to manually call once per appdomain:

CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

After that I got a new exception:

System.Security.Cryptography.CryptographicException: Invalid algorithm specified.

This brought me to your question. After reading suggested article I have made a new key and certificate (with OpenSSL) using Microsoft Enhanced RSA and AES Cryptographic Provider.
To my surprise, this new certificate allowed me to successfully make a signature.

After some more investigation I have found interesting answer of Andrew here https://stackoverflow.com/a/17285774/328785, where he used RSACryptoServiceProvider to prepare SecretKey for SignedXml class. Specifically this part (my interpretation):

var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certificates =  store.Certificates.Find(X509FindType.FindBySerialNumber,  "54dba096", true);
var certificate = certificates[0];

// next three lines
var cspParams = new CspParameters(24) { KeyContainerName = "XML_DSIG_RSA_KEY" };
var key = new RSACryptoServiceProvider(cspParams);
key.FromXmlString(certificate.PrivateKey.ToXmlString(true));

SignedXml sxml = new SignedXml(doc);
sxml.SigningKey = key;

And this solutions worked fine even with old key!