Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Watch out for XmlDocument.PreserveWhitespace when dealing with Digital Signatures


:P
On this page:

Last week I posted a detailed entry in regards to digitally signing an XML document and then also validating the document, using certficiates both internally generated and using a public CA signed key certificate. I've been working with a customer on a project where sending signed documents back and forth as part of a custom messaging interface is necessary and getting the signature creation and validation was the first and presumably most important step.

Unfortunately as soon as we started exchanging messages with the server we found out that even though my local test code checked messages for validity before sending that the remote machine was not able to validate the signature. Conversely sample documents sent from the service vendor for us to validate with their public certificate also failed for validate for us - the signatures failed to validate.

This has been a frustrating experience, since our end of the code validates just fine and round trips from signing through validation, why would the end consumer fail with that very same data? We had a bunch of back and forth with the vendor that was more or less grasping at straws (both ends <g>). One of the comments in the last post from Oleg pointed me in the right direction by chance.

It turns out the problem is related to XmlDocument parser settings that are differing between our implementation and the service provider's. Specifically the problem was the PreserveWhitespace property of XmlDocument which determines how XML treats white space in the XML document. Usually this setting is not really critical because XML internally doesn't consider white space as part of the payload content, but when it comes to the signature, this property setting makes all the the difference in the world, because it determines whether the signature includes white space in the document.

Essentially, we had to ensure that all XML documents that are involved with signatures are loaded and saved with PreserveWhiteSpace=true explicitly to force the white space treatment correctly to the same format the vendor was using. FWIW, this has bitten me before in other situations and it's quite common when dealing with non-.NET XML solutions for PreserveWhitespace to be the default.

Here's what the signature and validation routines look like now:

[TestMethod()]
public void GetSignatureTest()
{
    RouteOneProcessor sign = new RouteOneProcessor();
 
    string certName = App.Configuration.CertificateName;    
 
    // *** Retrieve a certificate by Subject - Tidewater Finance Company
    X509Certificate2 cert = sign.GetCertificateBySubject(App.Configuration.CertificateName,
                                                        StoreName.My, StoreLocation.LocalMachine);
 
    // *** XmlDoc is input - this would be our input from POST data - here from file
    XmlDocument doc = new XmlDocument();
    doc.PreserveWhitespace = true;
    doc.Load(STR_PROJECT_PATH + STR_InputDocument);
 
    // *** Sign the XmlDocument and return a new XmlDocument
    XmlDocument xmlDoc = sign.SignSoapBody(doc, cert);
 
    Assert.IsNotNull(xmlDoc, "XmlDocument null when returned.");
 
    // *** Check if the signature got added
    XmlElement element = xmlDoc.GetElementsByTagName("Signature")[0] as XmlElement;
 
    // *** Write out the document for testing so we can see structure
    xmlDoc.Save(STR_PROJECT_PATH + STR_OutputDocument);
 
    Assert.IsNotNull(element, "Signature element missing in result.");
}
 
/// <summary>
///A test for ValidateXmlSignature and verifying with a certificate store certificate
///</summary>
[TestMethod()]
public void ValidateXmlSignatureTest()
{
    RouteOneProcessor sign = new RouteOneProcessor(); 
 
    // *** Load signed output document from file
    XmlDocument doc = new XmlDocument();
    doc.PreserveWhitespace = true;
    doc.Load(STR_PROJECT_PATH + STR_OutputDocument);
 
    // *** Get cert by subject again
    X509Certificate2 cert = sign.GetCertificateBySubject(App.Configuration.CertificateName, 
                                                          StoreName.My, StoreLocation.LocalMachine);
 
    // *** And validate the signature
    Assert.IsTrue(sign.ValidateSoapBodySignature(doc, cert), "Xml Signature failed.");
 
}

It's important to note that the flag has to be set everywhere in the process - before loading any XML using Load or LoadXml and before saving. If you skip the step somewhere along the way and write out the document without the flag, then read it with the flag again the signature will fail to validate. IOW, the PreserveWhitespace setting must match when writing the signature and validating it.

This is one of those simple, but subtle things that are not easily discoverable and that can trip you up for a day or two. Hopefully this helps someone out and saves you that time...

Posted in .NET  Security  Web Services  XML  

The Voices of Reason


 

Amil
March 11, 2008

# re: Watch out for XmlDocument.PreserveWhitespace when dealing with Digital Signatures

So, using WCF, how can this be done via the config file? It seems that, by default, WCF does not preserve whitespace and validation fails if the original signature included the whitespace in the hash.

Bernie
October 09, 2009

# re: Watch out for XmlDocument.PreserveWhitespace when dealing with Digital Signatures

Thanks for this post! The PreserveWhitespace property seems a bit backwards to me, but nonetheless, your post saved me hours of head scratching.

Michael Vinther
February 03, 2017

# re: Watch out for XmlDocument.PreserveWhitespace when dealing with Digital Signatures

Unfortunately this doens't handle all whitespace issues. A tag like will still be changed to


BerndK
May 08, 2020

# re: Watch out for XmlDocument.PreserveWhitespace when dealing with Digital Signatures

PreserveWhitespace - very good point. But as far as I understood, the signature's info is also part of the hash (which makes perfect sense) so it is also critical, that the signature's whitespaces won't change. If you handle xml via XDocument and .ToString, this will also add unwanted whitespaces to the signature. Better not touch the file any more after signed.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024