EncryptedXml DecryptDocument method error after .Net framework update
Asked Answered
R

3

11

I have an old function written in 2013 that decrypt xml that was encrypted by another program.

The code is realy simple

        public static void Decrypt(XmlDocument Doc)
    {
        // Check the arguments.  
        if (Doc == null)
            throw new ArgumentNullException("Doc");

        // Create a new EncryptedXml object.
        EncryptedXml exml = new EncryptedXml(Doc);

        // Decrypt the XML document.
        exml.DecryptDocument();

    }

It worked like a charm until recently that some of our clients started to upgrade their framework to 4.6.2, so the method DecryptDocument() stopped working. Now it throws an exception "The algorithm group '' is invalid". If I remove .net framework 4.6.2 it works again.

The sample code in this link will reproduce the error, it will encrypt successfully then fail to decrypt.

I'm using A3 certificates, pendrive token. Anyone have faced this problem? there is any work around in .net 4.6.2?

Edit 1:

Stacktrace:

at System.Security.Cryptography.CngAlgorithmGroup..ctor(String algorithmGroup) at System.Security.Cryptography.CngKey.get_AlgorithmGroup() at System.Security.Cryptography.RSACng..ctor(CngKey key) at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate) at System.Security.Cryptography.CngLightup.GetRSAPrivateKey(X509Certificate2 cert) at System.Security.Cryptography.Xml.EncryptedXml.DecryptEncryptedKey(EncryptedKey encryptedKey) at System.Security.Cryptography.Xml.EncryptedXml.GetDecryptionKey(EncryptedData encryptedData, String symmetricAlgorithmUri) at System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument() at Criptografar.Program.Decrypt(XmlDocument Doc) in C:\Users\leoka\Documents\Visual Studio 2017\Projects\ConsoleApp4\Criptografar\Program.cs:line 152 at Criptografar.Program.Main(String[] args) in C:\Users\leoka\Documents\Visual Studio 2017\Projects\ConsoleApp4\Criptografar\Program.cs:line 83

Ragamuffin answered 29/5, 2017 at 21:18 Comment(10)
Do you have a full stacktrace?Kant
Did clients to a complete rebuild? Have client make copy of bin folder in project. Then delete bin folder and recompile. I suspect error is due to dependencies in compiler. The compiler doesn't have any dependencies to Net Version so when new Net is installed a full recompile is required. The compiler will not automatically do a full compile.Anta
@Anta It fails even on my development machine, I started a new project, pasted the sample code from this link. It failed to decrypt.Ragamuffin
I would start by comparing .proj files (which are ASCII) to make sure no settings changed. VS 2013 change a lot of default settings. VS2013 changes default for character encoding from UTF8 to Unicode which may explain the issue. I would think the issue is with VS not Net Libraries. Just changing Net Libraries a completely rebuilding project should work. Occasionally Microsoft changes default settings when Net is upgraded, but that is unusual.Anta
I'm fairly confident that this is a .net framework problem. The program is compiled for .NET 4.0. If I copy to a virtual machine newly installed with .NET 4.6.2, the program will not work. If on this same machine I remove the .net framework and install 4.5.2 the program will work correctly. I tried recompiling the program to target the .net framework 4.6.2, I made a clean rebuild, but it still does not work. Old versions of the program, compiled in 2013, also stop working in .NET 4.6.2.Ragamuffin
The driver for your certificate's private key reports that it has an algorithm group of the empty string. That's not expected. If you can break on the exception and see what values for Algorithm, and Provider(.Name) are, that'd be useful for filing an issue at github.com/dotnet/corefxRickeyricki
Try debugging during encryption by creating a variable with the value of cert.GetRSAPrivateKey(). In there, tell us what the respective values of .Key.Algorithm, .Key.AlgorithmGroup and .Key.Provider are.Seductress
cert.GetRSAPrivateKey() also throws the same exception The algorithm group '' is invalid. Parameter name: algorithmGroup stacktrace: at System.Security.Cryptography.CngAlgorithmGroup..ctor(String algorithmGroup) at System.Security.Cryptography.CngKey.get_AlgorithmGroup() at System.Security.Cryptography.RSACng..ctor(CngKey key) at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate)Ragamuffin
It only occurs with A3 certs?Idolize
I suppose so, I created a pfx self signed certificate and it worked without any errorRagamuffin
M
2

I cannot reproduce the problem myself - I don't have the "pendrive token" which I suspect is the problem - so this is guesswork. There are two generations of cryptographic APIs in Windows - the "old" one and the "new generation" one, known as CNG. Now, if you look at the source code for the CngLightup type that appears midway through your stack trace, specifically the DetectRsaCngSupport method, you'll see that .NET framework tries to use the new generation API if possible. My guess is that the "pendrive token" device does not support the new API. You can verify this by forcing the use of the old API. Unfortunately, there does not seem to be a public configuration flag that controls this, so you must resort to reflection-based hacks. For example, you can put something like this at the beginning of your program, so that it runs once, before you try the decrypting operation:

    var cngLightupType = typeof(EncryptedXml).Assembly.GetType("System.Security.Cryptography.CngLightup");
    var preferRsaCngField = cngLightupType.GetField("s_preferRsaCng", BindingFlags.Static | BindingFlags.NonPublic);
    var getRsaPublicKeyField = cngLightupType.GetField("s_getRsaPublicKey", BindingFlags.Static | BindingFlags.NonPublic);
    var getRsaPrivateKeyField = cngLightupType.GetField("s_getRsaPrivateKey", BindingFlags.Static | BindingFlags.NonPublic);
    preferRsaCngField.SetValue(null, new Lazy<bool>(() => false));
    getRsaPublicKeyField.SetValue(null, null);
    getRsaPrivateKeyField.SetValue(null, null);

Do note that it is extremely hacky, not thread-safe, error handling is omitted etc. If you verify that the CNG usage is the problem, you can then ask the "pendrive token" supplier to provide drivers that work with CNG. Or you can live with the hack above, rewritten for more safety.

Metanephros answered 8/6, 2017 at 11:10 Comment(1)
Thank you very much, I tested and it worked. I can dive deeper into the problem now that I know what exactly was going wrong. And this hack will buy me some time to find a better solution, if there is any.Ragamuffin
E
2

There are some runtime changes in .Net 4.6.2 that affect EncrtyptedXml - see https://msdn.microsoft.com/en-us/library/mt670901(v=vs.110).aspx#Anchor_5

Euphoria answered 1/6, 2017 at 20:31 Comment(1)
Thanks for the info, but this link does not say anything about not being able to use A3 certificates for decryption, with the @Seductress comment I discovered that not even an instance of RSAPrivateKey I can get. I do not think this is a problem with the token manufacturer, since several clients using different tokens are reporting the same problem. In addition, it does not indicate how to solve the problem.Ragamuffin
M
2

I cannot reproduce the problem myself - I don't have the "pendrive token" which I suspect is the problem - so this is guesswork. There are two generations of cryptographic APIs in Windows - the "old" one and the "new generation" one, known as CNG. Now, if you look at the source code for the CngLightup type that appears midway through your stack trace, specifically the DetectRsaCngSupport method, you'll see that .NET framework tries to use the new generation API if possible. My guess is that the "pendrive token" device does not support the new API. You can verify this by forcing the use of the old API. Unfortunately, there does not seem to be a public configuration flag that controls this, so you must resort to reflection-based hacks. For example, you can put something like this at the beginning of your program, so that it runs once, before you try the decrypting operation:

    var cngLightupType = typeof(EncryptedXml).Assembly.GetType("System.Security.Cryptography.CngLightup");
    var preferRsaCngField = cngLightupType.GetField("s_preferRsaCng", BindingFlags.Static | BindingFlags.NonPublic);
    var getRsaPublicKeyField = cngLightupType.GetField("s_getRsaPublicKey", BindingFlags.Static | BindingFlags.NonPublic);
    var getRsaPrivateKeyField = cngLightupType.GetField("s_getRsaPrivateKey", BindingFlags.Static | BindingFlags.NonPublic);
    preferRsaCngField.SetValue(null, new Lazy<bool>(() => false));
    getRsaPublicKeyField.SetValue(null, null);
    getRsaPrivateKeyField.SetValue(null, null);

Do note that it is extremely hacky, not thread-safe, error handling is omitted etc. If you verify that the CNG usage is the problem, you can then ask the "pendrive token" supplier to provide drivers that work with CNG. Or you can live with the hack above, rewritten for more safety.

Metanephros answered 8/6, 2017 at 11:10 Comment(1)
Thank you very much, I tested and it worked. I can dive deeper into the problem now that I know what exactly was going wrong. And this hack will buy me some time to find a better solution, if there is any.Ragamuffin
C
0

I ran into something very similar today that turned out to be a bug in .NET 4.6.2: https://github.com/Microsoft/dotnet/issues/341

According to this issue, there are two workarounds:

1) Upgrading the OS to Windows Server 2012R2 or newer, 2) loading the user profile.

Claver answered 6/6, 2017 at 1:33 Comment(1)
Thank you for your suggestion. I Installed a virtual machine with Windows Server 2012R2, I applied all windows updates and it still does not work with .net framework 4.6.2. I'm using a desktop application, so configuring IIS to load user profile doesn't fit.Ragamuffin

© 2022 - 2024 — McMap. All rights reserved.