34

I have a response from a 3-rd party web service. I load an XmlDocument with that response.

  string txt = readStream.ReadToEnd();
  response = new XmlDocument();
  response.PreserveWhitespace = true;
  response.LoadXml(txt);   
  return response;

Now I would like to verify that the respones is signed using the certificate. I have a VerifyXmlDoc(XmlDocument xmlDoc) method which I have found on msdn.

I know that the message is correct.

    public bool VerifyXmlDoc(XmlDocument xmlDoc)
    {

        SignedXml signed = new SignedXml(xmlDoc);

        XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");


        signed.LoadXml((XmlElement)signatureNodeList[0]);

        X509Certificate2 serviceCertificate = null;
        foreach (KeyInfoClause clause in signed.KeyInfo)
        {
            if (clause is KeyInfoX509Data)
            {
                if (((KeyInfoX509Data)clause).Certificates.Count > 0)
                {
                    serviceCertificate = (X509Certificate2)((KeyInfoX509Data)clause).Certificates[0];
                }
            }
        }


        bool result = signed.CheckSignature(serviceCertificate, true);
        return result;

    }

If I set target framework of my project to .NET 3.5 or .NET 3, or .NET 2 it works great. Result is true. But if I change target framework to .NET 4 result is false. (And I have to use .NET 4)

Any ideas on how to solve this problem?

Adel Khayata
  • 2,717
  • 10
  • 28
  • 46
user1207548
  • 341
  • 1
  • 3
  • 4
  • 1
    You may found help in a similar question: http://stackoverflow.com/questions/3935600/signedxml-checksignature-returns-false – gsscoder Jan 15 '13 at 21:04
  • 1
    What algorithm is being used to sign it? – Martin Ernst Jul 31 '13 at 12:50
  • 6
    Hi Can you post the exception call stack? – Bassam Alugili Aug 23 '13 at 08:21
  • 1
    If I'm reading this right, I think http://stackoverflow.com/a/19175738/12502 may have the solution to your issue if you're still looking for one. – Jonathan Rupp Oct 06 '13 at 14:23
  • Maybe debuggin through the .Net source code will give you a hint as well. You can enable it somewhere/somehow with some trouble in Visual Studio: http://stackoverflow.com/questions/8139269/how-do-you-enable-enable-net-framework-source-stepping – Mike de Klerk Oct 07 '13 at 17:05
  • It can be that enumeration order is changed and you just get another certificate to test the signature. – Alexey F Oct 08 '13 at 22:47
  • Does anyone know if there was any changes between .Net 4 and older regarding x509Cerificates and verifying them? – John Odom Jan 27 '14 at 20:39
  • It could be a change in the XML processing logic and not in the crypto logic. – dezfowler Jan 28 '14 at 10:22
  • According to [Reference Source](http://referencesource-beta.microsoft.com/#System.Security/cryptography/xml/signedxml.cs#6dd865f8431a18f8) the default test for signature format changed, maybe this is your problem, what are your xml and signature like? – Julien Ch. Mar 05 '14 at 13:30
  • I'm facing this exact issue right now. Tried explicitly setting the Canonicalization method, to no avail. Anyone find the reason for this? – Craig Vermeer Nov 20 '14 at 21:39

8 Answers8

7

This is a known issue. The Canonicalization implementation between .NET 3.5 and .NET 4.0 has changed.

I don't know if this works on all XML signatures but the following works from the testing that I've done.

Add the following C14N Transform class to your project:

public class MyXmlDsigC14NTransform: XmlDsigC14NTransform {
  static XmlDocument _document;
  public static XmlDocument document {
    set {
      _document = value;
    }
  }

  public MyXmlDsigC14NTransform() {}

  public override Object GetOutput() {
    return base.GetOutput();
  }

  public override void LoadInnerXml(XmlNodeList nodeList) {
    base.LoadInnerXml(nodeList);
  }

  protected override XmlNodeList GetInnerXml() {
    XmlNodeList nodeList = base.GetInnerXml();
    return nodeList;
  }

  public XmlElement GetXml() {
    return base.GetXml();
  }

  public override void LoadInput(Object obj) {
    int n;
    bool fDefaultNS = true;

    XmlElement element = ((XmlDocument) obj).DocumentElement;

    if (element.Name.Contains("SignedInfo")) {
      XmlNodeList DigestValue = element.GetElementsByTagName("DigestValue", element.NamespaceURI);
      string strHash = DigestValue[0].InnerText;
      XmlNodeList nodeList = _document.GetElementsByTagName(element.Name);

      for (n = 0; n < nodeList.Count; n++) {
        XmlNodeList DigestValue2 = ((XmlElement) nodeList[n]).GetElementsByTagName("DigestValue", ((XmlElement) nodeList[n]).NamespaceURI);
        string strHash2 = DigestValue2[0].InnerText;
        if (strHash == strHash2) break;
      }

      XmlNode node = nodeList[n];

      while (node.ParentNode != null) {
        XmlAttributeCollection attrColl = node.ParentNode.Attributes;
        if (attrColl != null) {
          for (n = 0; n < attrColl.Count; n++) {
            XmlAttribute attr = attrColl[n];
            if (attr.Prefix == "xmlns") {
              element.SetAttribute(attr.Name, attr.Value);
            } else if (attr.Name == "xmlns") {
              if (fDefaultNS) {
                element.SetAttribute(attr.Name, attr.Value);
                fDefaultNS = false;
              }
            }
          }
        }

        node = node.ParentNode;
      }
    }

    base.LoadInput(obj);
  }
}

Register the class using the CryptoConfig.AddAlgorithm method.

CryptoConfig.AddAlgorithm(typeof(MyXmlDsigC14NTransform), "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); 

var message = new XmlDocument();
message.PreserveWhitespace = true;
message.Load("XmlSig.xml");

MyXmlDsigC14NTransform.document = message; // The transform class needs the xml document

// Validate signature as normal.  

That should do it.

2

I had the same problem but none of those answers helped me. In this case it works or not depending on the operative system I used, not on the .Net version.

I've enabled the SignedXML log by adding this code in the app.config to see what happened behind:

<system.diagnostics>
        <sources>
            <source name="System.Security.Cryptography.Xml.SignedXml" switchName="XmlDsigLogSwitch">
                <listeners>
                    <add name="logFile" />
                </listeners>
            </source>
        </sources>
        <switches>
            <add name="XmlDsigLogSwitch" value="Verbose" />
        </switches>
        <sharedListeners>
            <add name="logFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="XmlDsigLog.txt"/>
        </sharedListeners>
        <trace autoflush="true">
            <listeners>
                <add name="logFile" />
            </listeners>
        </trace>
    </system.diagnostics>

It wrote this particular line:

System.Security.Cryptography.Xml.SignedXml Information: 17 : [SignedXml#033ec00f, UnsafeTransformMethod] Canonicalization method "http://www.w3.org/TR/1999/REC-xpath-19991116" is not on the safe list. Safe canonicalization methods are: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", "http://www.w3.org/2001/10/xml-exc-c14n#", "http://www.w3.org/2001/10/xml-exc-c14n#WithComments", "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2000/09/xmldsig#base64", "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform", "http://www.w3.org/2002/07/decrypt#XML".

I found this Microsoft Support article which tries to fix an error introduced by the security update 3141780: https://support.microsoft.com/en-us/kb/3148821

Into that article, in Scenario 2 section, there are 2 solutions, I fixed the problem applying the registry key related to the XPath Transform Method: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\Security\SafeTransformMethods@XmlDsigXPathTransform=http://www.w3.org/TR/1999/REC-xpath-19991116

NahuelGQ
  • 178
  • 1
  • 1
  • 10
1

Try explicitly setting the Canonicalization method for the SignedInfo property of the SignedXml class.. It appears there has been a change in default behaviour here between .Net 2.0 and .Net 4.0

signed.SignedInfo.CanonicalizationMethod = Signed.XmlDsigExcC14NTransformUrl;

Reference:

This answer

Community
  • 1
  • 1
Onots
  • 2,118
  • 21
  • 28
0
// Assume the data to sign is in the data.xml file, load it, and

// set up the signature object.
XmlDocument doc = new XmlDocument();

doc.Load(@"D:\Example.xml");
SignedXml sig = new SignedXml(doc);

// Make a random RSA key, and set it on the signature for signing.
RSA key = new RSACryptoServiceProvider();

sig.SigningKey = key;

// Create a Reference to the containing document, add the enveloped
// transform, and then add the Reference to the signature
Reference refr = new Reference("");refr.AddTransform(new XmlDsigEnvelopedSignatureTransform());

sig.AddReference(refr);

// Compute the signature, add it to the XML document, and save
sig.ComputeSignature();

doc.DocumentElement.AppendChild(sig.GetXml());
doc.Save("data-signed.xml");

// Load the signed data

//XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;

doc.Load("data-signed.xml");

// Find the Signature element in the document
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());

nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
XmlElement sigElt = (XmlElement)doc.SelectSingleNode("//dsig:Signature", nsm);

// Load the signature for verification

//SignedXml sig = new SignedXml(doc);

sig.LoadXml(sigElt);

// Verify the signature, assume the public key part of the

// signing key is in the key variable
if (sig.CheckSignature(key))
    Console.WriteLine("Signature verified");
else
    Console.WriteLine("Signature not valid");
Peter Lang
  • 54,264
  • 27
  • 148
  • 161
0

From .NET framework 4/4.5 the classes to work with x509 certificates and other security features are located in System.IdentityModel.dll. Search the respective classes in the namespace mentioned.

Dijkgraaf
  • 11,049
  • 17
  • 42
  • 54
Eduardo Vélez
  • 193
  • 1
  • 4
0

I solved this problem adding same namespace from Signature tag to SignedInfo. Like this:

Before: enter image description here

After: enter image description here

Jean
  • 93
  • 2
  • 9
0

In order to check the signature on NET 4.0+ you have to change the context of your CanonicalizationMethod, therefore you have to initialize your signedXml object in the following way:

XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");

SignedXml signedXml = new SignedXml((XmlElement)signatureNodeList[0]);

signedXml.LoadXml((XmlElement)signatureNodeList[0]);

//Then you proceed your check as usual
fvasquezc23
  • 864
  • 12
  • 14
-2
public static Boolean VerifyDetachedSignature(string XmlSigFileName)
{   
    // Create a new XML document.
    XmlDocument xmlDocument = new XmlDocument();

    // Load the passed XML file into the document.
    xmlDocument.Load(XmlSigFileName);

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

    // Create a new SignedXMl object.
    SignedXml signedXml = new SignedXml();

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

    // Check the signature and return the result. 
    return signedXml.CheckSignature();
}
ricksmt
  • 888
  • 2
  • 13
  • 34
biswapm
  • 31
  • 4