2

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:

  • Local → Microsoft Enhanced RSA and AES Cryptographic Provider → works
  • Staging → Microsoft Enhanced Cryptographic Provider v1.0 → doesn't work

Question: 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!

mu88
  • 4,156
  • 1
  • 23
  • 47
  • 1
    Did you try targeting a different version of Net? You may need to build a class library to do the encryption with the target Net 4.6 and then call the library from the Net 4.8 application. – jdweng Feb 23 '22 at 10:02
  • @jdweng will that be sufficient? Will the .NET 4.6 assembly embedded into a .NET 4.8 process really end up being executed with 4.6? – mu88 Feb 23 '22 at 12:42
  • There is conditional compile predirectives in the source code for the libraries for the target options to work. So the intermediate obj files are different for different targets. It still runs with the version of Net installed on the machine where code is compiled. – jdweng Feb 23 '22 at 12:50
  • @jdweng I moved the signing code into a different assembly targeting .NET 4.6.2, but the problem stays the same. Any other ideas? – mu88 Mar 01 '22 at 12:02
  • Was original code 4.6 or 4.6.2? – jdweng Mar 01 '22 at 22:56
  • .NET Framework 4.6.2 – mu88 Mar 02 '22 at 06:47

1 Answers1

1

Finally I found a solution that worked for me. There are two major points:

1. Add CSP to already existing certificate

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

2. Set AppContext switches on application startup

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

mu88
  • 4,156
  • 1
  • 23
  • 47