I've upgraded one of our web applications from .NET Framework 4.6 to 4.8. Somewhere deep inside the code an XML string gets signed and this signed XML is sent back to the client which will verify the signature. The XML signing uses the SignedXml
class.
Due to its legacy nature, SHA1 is still used (bear with me!). To not break this behavior when upgrading the .NET Framework, I had to set the following AppContext switches before actually signing the XML:
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
That solved all issues on my local machine and I could successfully sign the XMLs.
Now when moving forward and deploying the new code version onto our staging environment, a new problem came up: the XML signing fails with an Invalid algorithm specified
exception. Here is the stack trace:
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.Xml.SignedXml.ComputeSignature()
Now I stumbled across this question and especially that particular answer.
So I checked the CSPs using certutil
and I'm getting the following results:
Microsoft Enhanced RSA and AES Cryptographic Provider
→ worksMicrosoft Enhanced Cryptographic Provider v1.0
→ doesn't workQuestion: is there any chance to mitigate this behavior, e. g. by using another piece of code to sign the XML? Or do I have to recreate the certificate?
Thanks in advance!
Finally I found a solution that worked for me. There are two major points:
SwissSign provided us with the certificates for our staging environment (pem
and key
file) and we created the p12
file on our own.
By specifying the OpenSSL parameter CSP
when creating the p12
file, the correct CSP can be set. A command might looks like:
openssl pkcs12 -export -out Output.p12 -inkey My.key -in My.chain.pem -name My -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider"
In case you only have the p12
file, you have to convert it from PKCS10 into PEM format before by calling:
openssl pkcs12 -in my.p12 -out my.pem
As mentioned earlier, the following AppContext switches have to be set:
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
In my case I was setting the switches somewhere deep in the class where I actually signed the XML. So this code got executed at runtime when the first web request was handled.
When digging through the internals of System.Security.Cryptography.Xml.SignedXml
, I found out that some of the AppContext switches are actually getting cached. My updates were simply too late.
Now I set the switches on application startup an everything is working fine 🥳