Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SignedXml - refuses valid signed XML file #21451

Closed
karelz opened this issue May 1, 2017 · 63 comments
Closed

SignedXml - refuses valid signed XML file #21451

karelz opened this issue May 1, 2017 · 63 comments
Labels
area-System.Security bug help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@karelz
Copy link
Member

karelz commented May 1, 2017

Created based on Connect report: http://connect.microsoft.com/VisualStudio/feedback/details/1765025/verify-xml-signature-getc14ndigest-problem-in-signedxml-class (internal bug 144292)

Description: I've a problem with verifying XML signature file. The signature is done by third-party application and I am verifying the signature of that XML file in .NET framework 4.0 (check with latestes 4.6 and there is no difference).

To identify the problem, I've taken a sample.xml

<?xml version="1.0" encoding="UTF-8" standalone="no" ?><a><b>A</b></a>

When I sign the xml with the third-party app, I get signed.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<a>
  <b>A</b>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xds="http://uri.etsi.org/01903/v1.1.1#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
      <ds:Reference URI="">
        <ds:Transforms>
          <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
          <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <ds:DigestValue>/l5xchvEjxgLdPNt9RiB3TTQTcc=</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>
      cSUDo87XVNqbo+ljCZRAXvcuIXkak/10XyVWks9BNz5EdUmcVbiU84Y5qrJoAkMd
      yHPiIFkz8Dx5v5psK5oZjGtRleQ67nm1BryHA8F7EW4Otxe/8hEHsqVFK0zz3P79
      XzKD2vl4lUaTTOLlCMD+SbpbekyLkDIjXS/6IylPWbNNF3sH9MclGGhSSjREwOuJ
      Ayj8xqqibQEDvIytLN23+bZJtOGAU54ERXPnh4rccBvzByno08DHVnkQrSQCgY4E
      CvVbocIFo7GGtq8v9oj6rK3KpWUQnL1V1Aqj5fXRNRC8VnxJyIkHAXOBWC3Wr+DQ
      zEm4W0Xa+vZ8o0x/2Ct2cg==
    </ds:SignatureValue>
    <ds:KeyInfo>
      <ds:X509Data>
        <ds:X509Certificate>
          MIIEejCCA+OgAwIBAgIIBg5jkS3DgTkwDQYJKoZIhvcNAQEFBQAwOjETMBEGA1UE
          CxMKSW5mcmF4STRDQTEWMBQGA1UEChMNSW5mcmF4IGQuby5vLjELMAkGA1UEBhMC
          U0kwHhcNMTEwNTAzMDkxMzU2WhcNMTYwNTAxMDkxMzU2WjBfMRcwFQYDVQQDEw5Q
          cnZpIFVwb3JhYm5pazEQMA4GA1UECxMHaTQgdXNlcjEUMBIGA1UEChMLVGVzdCBk
          Lm8uby4xDzANBgNVBAcTBlRvbG1pbjELMAkGA1UEBhMCU0kwggEiMA0GCSqGSIb3
          DQEBAQUAA4IBDwAwggEKAoIBAQCkkkadn/Uapod4N0f7GVgDNNN/vRoNUzqbdsER
          vUA7f/MoARF0LoXAoxpAEk2DDf24oIkGjL60uqwx4di5LQr83zsdA/7Wh7RHb73j
          Cv0glfwK/YpfJcsxfLi/OajWNX5p63D9ISSDYtIgUb4k4JFElg+mbMsRFlPQNyR6
          bADQTWputmTE+reWRiHLlnXLNSH3wGO008BfYuBnsuWupK8ar5UQ5I2IyafZJONO
          4UQjsXsanYGqivWm147bihj7zfDjqEmVg0yYm9NS/jbhQSXRV9n8348engtu48ND
          g7Jsr363Ou1nvThzsxIyRBbqSCNSJPAiEYl/jF4WRO2zGmZzAgMBAAGjggHeMIIB
          2jAMBgNVHRMBAf8EAjAAMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
          BDAfBgNVHSMEGDAWgBSjdHM8IbieDPxF1rwNvMKR0T6WQDB3BgNVHR8EcDBuMGyg
          aqBohmZodHRwOi8vY2EuaW5mcmF4LnNpL2VqYmNhL3B1YmxpY3dlYi93ZWJkaXN0
          L2NlcnRkaXN0P2NtZD1jcmwmaXNzdWVyPU9VPUluZnJheEk0Q0EsTz1JbmZyYXgg
          ZC5vLm8uLEM9U0kwDgYDVR0PAQH/BAQDAgSwMB0GA1UdDgQWBBSKMErpxIQp6TQ8
          Jqr0ywYrYla4NzCB3gYIKwYBBQUHAQEEgdEwgc4wgYkGCCsGAQUFBzAChn1odHRw
          Oi8vY2EuaW5mcmF4LnNpL2VqYmNhL3B1YmxpY3dlYi93ZWJkaXN0L2NlcnRkaXN0
          P2NtZD1pZWNhY2VydCZpc3N1ZXI9T1UlM2RJbmZyYXhJNENBJTJjTyUzZEluZnJh
          eCtkLm8uby4lMmNDJTNkU0kmbGV2ZWw9MDBABggrBgEFBQcwAYY0aHR0cDovL2Nh
          LmluZnJheC5zaTo4MDgwL2VqYmNhL3B1YmxpY3dlYi9zdGF0dXMvb2NzcDANBgkq
          hkiG9w0BAQUFAAOBgQAms5UL/f0AIavlVo9C5W5Ijdt12g/59PIZeAEDWzoEKi+W
          rE7ORaq527UHgcVGw7Gr2HhOEHmggUe6k43edQ0fkNg5WXfJzf18hHA+foVqsxra
          DxBeot442A1zw9GjRnIDCl2r91tHgkneqg2EE8kf7lkRtMRck1MUbHBnLnppKg==
        </ds:X509Certificate>
      </ds:X509Data>
    </ds:KeyInfo>
  </ds:Signature>
</a>

(Note: The original issue had no (non-required) whitespace in the XML, it was pretty-printed here to allow for better human understanding)

This signed XML is valid, according to let's say this (https://www.signatur.rtr.at/en/elsi/Pruefung.html) tool, and I've verified it with a few other tools and all confirm that it's OK, except when I verify it with SignedXml class from .NET Framework.
The code I'am using to verify the XML.

Now the problem as I've managed to identify is in the SignedXml class in function GetC14NDigest where Utils.GetPropagatedAttributes(m_context) is called. m_context is signed XML document, but the namespace xmlns:xds="http://uri.etsi.org/01903/v1.1.1#" is only in Signature node, which was propagated into the SignedInfo node, but the Utils.GetPropagatedAttributes(m_context) did not return that one, and the SignedInfo node is missing that one, so the SignedInfo node is not the same as it was when the signature was constructed.

I've tried to sign an xml like this:

<a xmlns:xds="http://uri.etsi.org/01903/v1.1.1#">
  <b>A</b>
</a>

and this signed XML I was able to verify successfully with SignedXml class.

@karelz
Copy link
Member Author

karelz commented May 1, 2017

Next steps:

  1. Turn the repro into test. Confirm it repros.
  2. Fix the bug.

@karelz
Copy link
Member Author

karelz commented May 1, 2017

cc @StanislavUshakov @anthonylangsworth @tintoy @peterwurzinger - anyone interested to tackle it?

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

I could take a look tomorrow morning (AU timezone) if nobody else wants it?

@anthonylangsworth
Copy link
Contributor

anthonylangsworth commented May 2, 2017

I was going to put up my hand for it but the earliest I can get to it is tomorrow evening (also AU timezone).

@karelz
Copy link
Member Author

karelz commented May 2, 2017

Appreciate your help guys, thanks! I guess whoever starts first should ping this issue ... thanks again!

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Ok - I'll have a look at this shortly.

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

@karelz - I'll create a test for this scenario; assuming it fails, is that sufficient for a repro?

@karelz
Copy link
Member Author

karelz commented May 2, 2017

Yep, that's what I had in mind. Sorry for not being clear. (updated above)

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

NP - just bootstrapping the build on my new laptop and then I'll get right onto it.

@karelz
Copy link
Member Author

karelz commented May 2, 2017

BTW: We have new docs for contributors (mostly new contribs): https://github.com/dotnet/corefx/wiki/New-contributor-Docs
If you have gotchas, please add them - it is writeable wiki without PR system.

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

No worries - I have a Powershell profile that sets up environment variables for corefx / netfx dev, but building corefx itself has more subtle requirements it seems; had to drop back to using "Developer Command Prompt for VS2017" to get it to work.

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Hmm, interesting. After the build, I see src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt has changed:

diff --git a/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt b/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt
index 845369c..fe4e873 100644
--- a/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt
+++ b/src/shims/ApiCompatBaseline.netcoreapp.netfx461.txt
@@ -5,6 +5,11 @@ ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.ServiceMod
 ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)' referenced by the contract assembly 'Assembly(Name=System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)'.
 ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)' referenced by the contract assembly 'Assembly(Name=System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)'.
 ApiCompat Error: 0 : Unable to resolve assembly 'Assembly(Name=System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)' referenced by the contract assembly 'Assembly(Name=System.Xml.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)'.
+Compat issues with assembly mscorlib:
+TypesMustExist : Type 'System.ArgIterator' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.Console.Write(System.String, System.Object, System.Object, System.Object, System.Object, __arglist)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.Console.WriteLine(System.String, System.Object, System.Object, System.Object, System.Object, __arglist)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.String.Concat(System.Object, System.Object, System.Object, System.Object, __arglist)' does not exist in the implementation but it does exist in the contract.
 Compat issues with assembly System:
 MembersMustExist : Member 'System.CodeDom.Compiler.CompilerParameters.Evidence.get()' does not exist in the implementation but it does exist in the contract.
 MembersMustExist : Member 'System.CodeDom.Compiler.CompilerParameters.Evidence.set(System.Security.Policy.Evidence)' does not exist in the implementation but it does exist in the contract.

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Urk! Ok, have run into a more serious problem. Opened System.Security.Cryptography.Xml.sln in VS2017, hit build, and run into a wall of errors:

1>------ Build started: Project: System.Security.Cryptography.Xml (ref\System.Security.Cryptography.Xml), Configuration: netcoreapp-Debug Any CPU ------
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(3,27,3,55): error CS0234: The type or namespace name 'AllowPartiallyTrustedCallersAttribute' does not exist in the namespace 'System.Security' (are you missing an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(3,27,3,55): error CS0234: The type or namespace name 'AllowPartiallyTrustedCallers' does not exist in the namespace 'System.Security' (are you missing an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(4,18,4,25): error CS0234: The type or namespace name 'Runtime' does not exist in the namespace 'System' (are you missing an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(5,18,5,28): error CS0234: The type or namespace name 'Reflection' does not exist in the namespace 'System' (are you missing an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(6,11,6,24): error CS0246: The type or namespace name 'AssemblyTitleAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(6,11,6,24): error CS0246: The type or namespace name 'AssemblyTitle' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(7,11,7,30): error CS0246: The type or namespace name 'AssemblyDescriptionAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(7,11,7,30): error CS0246: The type or namespace name 'AssemblyDescription' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(8,11,8,31): error CS0246: The type or namespace name 'AssemblyDefaultAliasAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(8,11,8,31): error CS0246: The type or namespace name 'AssemblyDefaultAlias' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(9,11,9,26): error CS0246: The type or namespace name 'AssemblyCompanyAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(9,11,9,26): error CS0246: The type or namespace name 'AssemblyCompany' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(10,11,10,26): error CS0246: The type or namespace name 'AssemblyProductAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(10,11,10,26): error CS0246: The type or namespace name 'AssemblyProduct' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(11,11,11,28): error CS0246: The type or namespace name 'AssemblyCopyrightAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(11,11,11,28): error CS0246: The type or namespace name 'AssemblyCopyright' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(12,11,12,26): error CS0246: The type or namespace name 'AssemblyVersionAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(12,11,12,26): error CS0246: The type or namespace name 'AssemblyVersion' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(13,11,13,30): error CS0246: The type or namespace name 'AssemblyFileVersionAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(13,11,13,30): error CS0246: The type or namespace name 'AssemblyFileVersion' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(14,11,14,39): error CS0246: The type or namespace name 'AssemblyInformationalVersionAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(14,11,14,39): error CS0246: The type or namespace name 'AssemblyInformationalVersion' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(15,11,15,23): error CS0246: The type or namespace name 'CLSCompliantAttribute' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(15,11,15,23): error CS0246: The type or namespace name 'CLSCompliant' could not be found (are you missing a using directive or an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(16,18,16,28): error CS0234: The type or namespace name 'Reflection' does not exist in the namespace 'System' (are you missing an assembly reference?)
1>D:\Development\github\dotnet\corefx\bin\obj\ref\System.Security.Cryptography.Xml\4.0.0.0\netcoreapp\_AssemblyInfo.cs(17,18,17,28): error CS0234: The type or namespace name 'Reflection' does not exist 

<SNIP>

3>------ Build started: Project: System.Security.Cryptography.Xml.Tests, Configuration: netcoreapp-Debug Any CPU ------
3>  System.Security.Cryptography.Xml.Tests -> D:\Development\github\dotnet\corefx\bin\AnyOS.AnyCPU.Debug\System.Security.Cryptography.Xml.Tests\netcoreapp\System.Security.Cryptography.Xml.Tests.dll
========== Build: 1 succeeded, 2 failed, 0 up-to-date, 0 skipped ==========

@karelz I'm sure this worked on my old machine, and I have verified that required VS components are installed (read the contributor docs to be sure). Anything obvious I've missed?

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

(interestingly, the test project builds fine, it's just the other 2 that don't)

@karelz
Copy link
Member Author

karelz commented May 2, 2017

Did you compile from root first? @mellinoe @weshaggard can hopefully help troubleshoot ...

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Yep, .\build.cmd. That works fine. Interestingly, if I run msbuild src\System.Security.Cryptography.Xml\System.Security.Cryptography.Xml.sln it also fails (can't find Microsoft.CSharp.Portable.targets).

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Or if I build from developer command prompt, it just spits out the same wall of errors listed above.

@mellinoe
Copy link
Contributor

mellinoe commented May 2, 2017

Are you able to build the csproj directly from the command line?

@karelz
Copy link
Member Author

karelz commented May 2, 2017

cc @krwq

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

@mellinoe yes! Just not the solution file.

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Wrong configuration selected perhaps?

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Wrong configuration selected perhaps?

Hmm, nope - there are only 2 configurations listed.

@weshaggard
Copy link
Member

@krwq just make some configuration changes to this project so I suspect we need to regenerate the sln file. You can do that by doing https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/project-guidelines.md#updating-configurations.

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

@weshaggard - thanks, that did the trick. Had to edit build.proj though - the UpdateVSConfigurations target had a dependency on a non-existentBuildCoreFxTools target.

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Test fails, but according to this online verifier the signature is invalid, too. Perhaps it's a whitespace issue? Was the original XML supplied in a file or perhaps mangled by form / forum page?

@karelz
Copy link
Member Author

karelz commented May 2, 2017

I copied it from web page. There was also file ... digging ...

@karelz
Copy link
Member Author

karelz commented May 2, 2017

Here are the files from the repro:

Code file: verify.cs

  XmlDocument xmlDocument = new XmlDocument();

  // Format using white spaces.
  xmlDocument.PreserveWhitespace = true;

  // Load the passed XML file into the document. 
  try
  {
      xmlDocument.Load("signedNOT_OK_xdsNS_inSignatureNode.xml");
  }
  catch (Exception e)
  {
      throw new CryptographicException("Unable to load XML document. Error: " + e.Message);
  }
  // Create a new SignedXml object and pass it 
  // the XML document class.
  SignedXml signedXml = new SignedXml(xmlDocument);

  // Find the "Signature" node and create a new 
  // XmlNodeList object.
  //XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl);

  // Throw an exception if no signature was found.
  if (nodeList.Count <= 0)
  {
      throw new CryptographicException("No Signature was found in the document.");
  }

  // This example only supports one signature for
  // the entire XML document.  Throw an exception
  // if more than one signature was found.
  if (nodeList.Count >= 2)
  {
      throw new CryptographicException("More that one signature was found for the document.");
  }

  // Load the signature node.
  signedXml.LoadXml((XmlElement)nodeList[0]);

  var x509data = signedXml.Signature.KeyInfo.OfType<KeyInfoX509Data>().First();
  if (x509data != null)
  {
       signatureCert = x509data.Certificates[0] as X509Certificate2;
  }
  if (signatureCert == null)
  {
      ErrorMessage = "Signing certficate not present in the XML.";
      return false;
  }
  AsymmetricAlgorithm key = signatureCert.PublicKey.Key;
  string keyName = GetKeyName(key);

  // Check the signature and return the result. 
  bool ok = signedXml.CheckSignature(signatureCert, true);

@tintoy
Copy link
Contributor

tintoy commented May 2, 2017

Ok, tried that, too - the online validator says it's still not valid:

func=xmlSecOpenSSLX509StoreVerify:file=x509vfy.c:line=360:obj=x509-store:subj=X509_verify_cert:error=4:crypto library function failed:subj=/CN=Prvi Uporabnik/OU=i4 user/O=Test d.o.o./L=Tolmin/C=SI;err=20;msg=unable to get local issuer certificate
func=xmlSecOpenSSLX509StoreVerify:file=x509vfy.c:line=408:obj=x509-store:subj=unknown:error=71:certificate verification failed:err=20;msg=unable to get local issuer certificate
func=xmlSecOpenSSLEvpSignatureVerify:file=signatures.c:line=493:obj=rsa-sha1:subj=EVP_VerifyFinal:error=18:data do not match:signature do not match
RESULT: Signature is INVALID
---------------------------------------------------
= VERIFICATION CONTEXT
== Status: invalid
== flags: 0x00000000
== flags2: 0x00000000
== Key Info Read Ctx:
= KEY INFO READ CONTEXT
== flags: 0x00000000
== flags2: 0x00000000
== enabled key data: all
== RetrievalMethod level (cur/max): 0/1
== TRANSFORMS CTX (status=0)
== flags: 0x00000000
== flags2: 0x00000000
== enabled transforms: all
=== uri: NULL
=== uri xpointer expr: NULL
== EncryptedKey level (cur/max): 0/1
=== KeyReq:
==== keyId: rsa
==== keyType: 0x00000001
==== keyUsage: 0x00000002
==== keyBitsSize: 0
=== list size: 0
== Key Info Write Ctx:
= KEY INFO WRITE CONTEXT
== flags: 0x00000000
== flags2: 0x00000000
== enabled key data: all
== RetrievalMethod level (cur/max): 0/1
== TRANSFORMS CTX (status=0)
== flags: 0x00000000
== flags2: 0x00000000
== enabled transforms: all
=== uri: NULL
=== uri xpointer expr: NULL
== EncryptedKey level (cur/max): 0/1
=== KeyReq:
==== keyId: NULL
==== keyType: 0x00000001
==== keyUsage: 0xffffffff
==== keyBitsSize: 0
=== list size: 0
== Signature Transform Ctx:
== TRANSFORMS CTX (status=2)
== flags: 0x00000000
== flags2: 0x00000000
== enabled transforms: all
=== uri: NULL
=== uri xpointer expr: NULL
=== Transform: c14n (href=http://www.w3.org/TR/2001/REC-xml-c14n-20010315)
=== Transform: rsa-sha1 (href=http://www.w3.org/2000/09/xmldsig#rsa-sha1)
=== Transform: membuf-transform (href=NULL)
== Signature Method:
=== Transform: rsa-sha1 (href=http://www.w3.org/2000/09/xmldsig#rsa-sha1)
== Signature Key:
== KEY
=== method: RSAKeyValue
=== key type: Private
=== key name: test-rsa
=== key usage: -1
=== rsa key: size = 1024
== SignedInfo References List:
=== list size: 1
= REFERENCE VERIFICATION CONTEXT
== Status: succeeded
== URI: ""
== Reference Transform Ctx:
== TRANSFORMS CTX (status=2)
== flags: 0x00000000
== flags2: 0x00000000
== enabled transforms: all
=== uri: NULL
=== uri xpointer expr: NULL
=== Transform: enveloped-signature (href=http://www.w3.org/2000/09/xmldsig#enveloped-signature)
=== Transform: c14n (href=http://www.w3.org/TR/2001/REC-xml-c14n-20010315)
=== Transform: sha1 (href=http://www.w3.org/2000/09/xmldsig#sha1)
=== Transform: membuf-transform (href=NULL)
== Digest Method:
=== Transform: sha1 (href=http://www.w3.org/2000/09/xmldsig#sha1)
== Manifest References List:
=== list size: 0
'''

@tintoy
Copy link
Contributor

tintoy commented May 4, 2017

They claim other XMLDSIG implementations can successfully validate that XML document, and the only online one I've found claims that the certificate is not valid (which may indicate that the signature is valid but the signing key fails X.509 certificate validation).

@tintoy
Copy link
Contributor

tintoy commented May 4, 2017

This online validator claims that the document is valid, BTW (but also that the cert is not). So yes it does seem like there might be a bug here.

@tintoy
Copy link
Contributor

tintoy commented May 4, 2017

@anthonylangsworth - do you have an opinion here about what constitutes correct behaviour?

@krwq
Copy link
Member

krwq commented May 4, 2017

@tintoy thank you a lot for investigating this! I've tried the document with the validator you provided and modified a doc a little bit (outside of the signature) and can confirm that the online validator claims that unmodified doc is ok and modified is not ok. Given that - looks like a product bug and likely related to propagating namespaces.

@karelz @bartonjs - despite this being a product bug I do believe this won't meet the bar for 2.0 considering following:

  • we never claim bad document is good
  • we have shipped like that many years ago and got only two customers complaining and they are both satisfied with the workaround they found - @tintoy I do not believe their workaround validates nothing considering the hash somehow matched (too unlikely)

IMO: I believe this should be fixed for the next release though (regular priority). If anyone sends a patch we should approve it for 2.0 (assuming no API addition required).

@karelz
Copy link
Member Author

karelz commented May 4, 2017

@krwq agreed, thanks for summary and thanks @tintoy for your great analysis!

@leopignataro
Copy link

leopignataro commented May 16, 2017

Hello, @karelz @tintoy and @krwq. First of all, great effort in porting the XmlDSig feature and also in trying to tackle this long-standing issue.

I am on the same team as Bruno C Dias Ribeiro (@brunocapu), and we've found the workaround mentioned on the Connect report together. We'd like to offer our help with this issue.

we never claim bad document is good

Unfortunately, this is not the case. Consider the following document:

https://files.lacunasoftware.com/temp/19198/false-positive.xml

Notice: please let us know if you'd like a slightly different false positive case, for instance with a X509Certificate element instead of KeyValue.

This document is reported as NOT VALID by the validator at aleksey.com. Validating with Java also gives NOT VALID (please let us know if you'd like our validation code in Java). However, validating with the SignedXml class may yield VALID, depending on whether the workaround is used. If it is used, the result is NOT VALID (correct). However, if it is not used, i.e. if the SignedXml constructor is called with the XmlDocument, validation yields VALID.

I can't think of any way how this might be exploited in practice, but generally if there is a false positive in a signature validation it's only a matter of creativity and time to come up with a practical exploit.

From memory there are 2 or 3 kinds of XML-DSIG formats (attached, detached, etc). I wonder if this represents an edge case?

The issue is with the canonicalization of the SignedInfo element. AFAIK it affects all kinds of signatures. We've been able to reproduce it with both enveloped signatures (signature over the entire XML document, i.e. Reference with URI="") and detached signatures (signature over an element of the XML document, i.e. Reference with URI="#id").

So yes, the workaround does show that it's possible to validate the signature but only in isolation of the thing that was signed (which seems kinda pointless).

Not really, the references are taken into account. If they weren't, the validation of a correct signature would certainly fail, since the references were taken into account when producing the signature.

We'd like to help with this issue in any way we can, since we believe it is a bug.

@leopignataro
Copy link

leopignataro commented May 17, 2017

The plain reasoning behind the workaround is to change the behavior of the following line:

private byte[] GetC14NDigest (HashAlgorithm hash) {
	// ...
	CanonicalXmlNodeList namespaces = (m_context == null ? null : Utils.GetPropagatedAttributes(m_context));
	// ...
}

https://referencesource.microsoft.com/#System.Security/system/security/cryptography/xml/signedxml.cs,767

Calling the constructor of the SignedXml class passing a XmlDocument causes the Utils.GetPropagatedAttributes() method to be called with the document root element, while calling the constructor passing the signature element causes the Utils.GetPropagatedAttributes() method to be called with the signature element itself.

This yields different results if namespace prefixes are added on elements between (but excluding) the root document and (including) the signature element:

A
+- B
+- C*
   +- D
   +- E*
      +- F
      +- Signature*

Any namespace prefixes added on the elements marked above would cause differences on the behavior of the GetC14NDigest() method

The Canonical XML 1.0 algorithm definition clearly states that the correct behavior in this case is to take into account the prefixes inherited by the Signature element.

Please notice that this is not the case for the exclusive canonicalization algorithm, which is currently not implemented on the .NET Framework

Unfortunately this is not uncommon. A quite common case is adding the namespace prefix "ds" on the signature element itself, which is what the external tool that GregorM was using (from the Connect report) does. However, there are infinite other cases.

We believe this issue comes from a poor documentation of the constructors in the SignedXml class. To make things worse, these constructors may be called both when creating and when validating signatures.

We think that the desired effects for each case would be:

  1. During signature creation
    a) Calling SignedXml(XmlDocument) means the signature will be inserted on the given document. Only "global" namespace prefixes (declared on the root element) will be taken into account on the canonicalization of the SignedInfo. This works well if the signature element will be inserted as a child of the root document, which is arguably the most common case.
    b) Calling SignedXml(XmlElement) means the signature will be inserted on the document that owns the given element, not as a child of the root element but as a child of the given element. All namespace prefixes inherited on that "context" will be taken into account on the canonicalization of the SignedInfo.
  2. During signature validation
    a) Calling SignedXml(XmlDocument) means the signature is on the given document, and will later be given on the LoadXml() element
    b) Calling SignedXml(XmlElement) means the signature is the given element (and perhaps calling LoadXml() is a bit redundant, but that's another matter)

Again, none of that is stated on the documentation of the class, that's simply what we think is reasonable to imply from the API.

We believe the problem lies on implementation of case 2a, when the code assumes that the context of the validation is the root element -- especially given that the caller will inevitably pass the actual signature element later on the LoadXml() method.

Therefore, we'd like to propose a fix consisting of changing the LoadXml() method by removing the lines commented out below:

public void LoadXml(XmlElement value) {
	if (value == null)
		throw new ArgumentNullException("value");

	m_signature.LoadXml(value);

	//if (m_context == null) {
	m_context = value;
	//}

	bCacheValid = false;
}

In other words, when the LoadXml() method is called, override what may be initially have been considered as the "context" when the class was instantiated.

@leopignataro
Copy link

@karelz , we talked on #15603 yesterday about this thread. I'm not sure what it is that you need us to do.

You yourself created this thread because of a connect report of a supposed bug on the XmlDSig implementation on .NET Framework. On the course of this thread, it seems the collaborators have come to a conclusion that it is perhaps not a bug, and that it can be addressed later since it was thought that the implementation never says an invalid document is valid (a "false positive").

We then provided evidence that that is in fact not true, that the current implementation does say invalid documents are valid, depending on the way the API is used ("not using the workaround"). We then proposed a fix.

I'm sorry, this is the first time my team has tried to collaborate with .NET efforts, we are a bit lost regarding the contribution process. What do you need us to do next?

@karelz
Copy link
Member Author

karelz commented May 25, 2017

@leopignataro thanks for the details, it's helpful.
If you guys have a fix, did you consider submitting a PR? Our main page docs refer to documents how to do it (although the docs are under construction at this moment, so they are not super easy to use).

The fix of course has to be reviewed and thought through from all angles - incl. impact on compatibility and performance of all scenarios hitting the code path. And the fix has to have good code coverage.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the Future milestone Jan 31, 2020
@maryamariyan maryamariyan added the untriaged New issue has not been triaged by the area owner label Feb 23, 2020
@bartonjs bartonjs removed the untriaged New issue has not been triaged by the area owner label Jul 7, 2020
@phax
Copy link

phax commented May 7, 2021

This issue still exists in .NET 5.0 - any update would be appreciated...

Edit, okay it seems like it was too late when I wrote the original version of the Post :(

Can anyone please help me, why that signature does not work when validating in .NET but it works in Java as well as with xmlsec1:
working-in-java-failing-in-net.xml.txt

@krwq
Copy link
Member

krwq commented Jun 7, 2021

@phax, we do not currently plan to fix this as it has relatively low priority comparing to other issues we have but we're open for contribution.

@da9l
Copy link

da9l commented Aug 24, 2022

I'm having this problem with .NET 6 and the failure of validating the SignedInfo element.

Either I'm doing something wrong or this is still a live bug.

These links suggest that the .NET implementation fails to propagate the namespaces correctly to the SignedInfo element during the Inclusive C14N Canonicalization process.
https://www.di-mgt.com.au/docs/ia_071912.pdf
https://www.aleksey.com/pipermail/xmlsec/2012/009463.html

I've also managed to enable the Debug logging of the SignedXml.CheckSignature method and it clearly shows that it is the SignedInfo validation that fails.

// Enable debug logging of System.Security.Cryptography.Xml
let traceSource = 
    Assembly.Load("System.Security.Cryptography.Xml")
        .GetType("System.Security.Cryptography.Xml.SignedXmlDebugLog")
        .GetField("s_traceSource", (BindingFlags.Static ||| BindingFlags.NonPublic))
        .GetValue(null) :?> TraceSource

let added = traceSource.Listeners.Add(new TextWriterTraceListener(System.Console.Out))
traceSource.Switch.Level <- SourceLevels.Verbose

Given time I'll see if it's possible to create a proper reproduction repo.

However, when inspecting the Canonicalized SignedInfo i see that it does propagate the namespaces as specified. I'll just have to see if it can be replicated in a repo.

@da9l
Copy link

da9l commented Sep 2, 2022

I've recreated @karelz reproduction as a public reproduction repo in dotnet 6.0.203 with the debug info enabled.
https://github.com/da9l/SignedXmlCheckSignatureBug

@karelz
Copy link
Member Author

karelz commented Sep 3, 2022

Tagging @bartonjs @GrabYourPitchforks who own the area now.

@willisd2
Copy link

willisd2 commented Oct 21, 2022

I ran into an issue similar to this which I think others in this thread might be running into as well (@da9l and @phax). It looks like what we're running into is described pretty well in this issue #27500

From what I can tell it looks like the fix should be a part of .NET 7.

@ThiemeNL
Copy link

ThiemeNL commented Feb 2, 2023

The issue still exist in .NET 7, is there any update?

@willisd2
Copy link

willisd2 commented Feb 2, 2023

The issue still exist in .NET 7, is there any update?

Not sure exactly what your situation is, and I'm going to completely hack this explanation cause I'm going from memory...

My situation was validating a SAML token. The token had the overall Document signed and the Assertion Element signed. The overall Document signature checked out fine prior to .NET 7, but the Assertion signature was failing due to this issue. After the 'fix' was in, my Assertion signature still wasn't passing. I eventually figured out it was because the Assertion Element was isolated from the main XmlDocument, and a new XmlDocument was created out of it. This was the way it was done on about every example you would find on the internet. When I checked the Assertion signature based off of referencing the element in the original XmlDocument, then the signature checked out.

@ThiemeNL
Copy link

ThiemeNL commented Feb 2, 2023

@willisd2 my issues is exactly the sames as @phax mentioned. Maybe there are two seperated issues discussed in this thread. Mine is also the same as: https://stackoverflow.com/questions/47694541/wrong-canonicalization-c14n-made-by-signedxml-it-removes-line-breaks-13

@yelvert
Copy link

yelvert commented Oct 31, 2023

so, year 6 of this same issue, completely prevents anyone from making a secure saml implementation in dotnet

@totszwai
Copy link

I just happened to stumble the need of this too. Welcome to 2024. Subscribed.

@bartonjs
Copy link
Member

The specific problems described in this issue are varied. Some of them look like a known issue of .NET SignedXml not playing nicely with carriage returns (&#xD; or &#13;).

I've commented on that at #99856 and is where future discussion for the carriage return problem should happen.

Any issues that aren't specifically related to carriage returns should open a new issue; but be aware the .NET team is unlikely to fix any problems that aren't "SignedXml can't read a signature produced by an earlier version of the package (or .NET Framework)"

@bartonjs bartonjs closed this as not planned Won't fix, can't repro, duplicate, stale Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Security bug help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

No branches or pull requests