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

Digitally Signing an XML Document and Verifying the Signature


:P
On this page:

Recently I've been involved in some work that deals with signed XML documents outside of a WCF Web service environment. Ironically the service that is being accessed accepts SOAP data but the service does not expose any WSDL even though it uses some WS-* specification features - namely digital signature of the XML body of the message.

Since I didn't have much luck finding the information I needed in one place I thought I'd write it up here, so it hopefully helps out some of you (and myself) in the future.

[Updated: 2/23/08 - added code updates and section for installing live certs]
[Updated: 3/12/08 - added notes about PreserveWhitespace matching]

Certificates for Testing

The most frustrating part of the process for me was getting the certificates set up correctly for testing. Certificate logic - private and public keys - is something that's always been rather fuzzy in my mind and getting the keys set up properly in Windows is really quite a pain in the ass because there are a bunch of different options to do it.

For quick review: When signing a document you'll use a Private key to sign a portion of the the document and embed a signature into the document. You then use a Public key to validate the signature when reading the document. Both signing and and validation can be done with the SignedXml class in combination with the X509Certificate and X509Certifcate2 classes.

For testing it's a good idea to create self signed certificate that you can use both for signing and validating.

When you create a key with Windows using the MakeCert.exe utility you can create a key pair that includes both public and private key and automatically install these keys into the Windows certificate store.

To do this you need to:

  • Create the certificate:
    makecert.exe -r -pe -n "CN=TestSignCert"  -ss my -sr currentuser -sky exchange -sy  12 "c:\TestSignCert.cer"

    You can review the options for makecert here. The above creates a key that is self signed (-r), includes a private and exportable key (-pe), the name of the signer (-n in x509 convention), that the location is going to be My or Personal (-ss my)  and in the currentuser store (-sr), that the key will be used for message exchange (-sky exchange) and the crypto type as RSA (-sy 12). The public key automatica
           
  • Run mmc.exe and Add the Certificates Add-in
    Go to the Current User Certificates | Personal and find your certificate (you'll need to use the Local Machine Store later in order to work with IIS so keep that in mind). Make sure the cert has both the cert and key showing in the icon (ie. key and cert seal). The key indicates a Private key while the cert seal alone indicates a public key.
  • Right click and select Properties and add a Friendly Name (TestSignCert here) - it's blank by default which is not very useful. In code it's easiest to retrieve the friendly name because it's a property of the certificate. All other certificate 'values' (like Issued to or by) are embedded as part of the Subject string and have to be manually parsed out which is more work. Using FriendlyName is easiest.

    certDisplay

The latter step is optional but I prefer to assign a friendly key name so its easier to reference the key. The code below uses Friendly Names to retrieve the key. But you can use any of the other entries.  Subject (which is the same value as Issued To) is what key command line tools generally use to look up keys.

At this point you should have a working certificate that you can use for digital signatures.

Signing an XML Document

The next step is to sign an XML document. There are a number of ways to do this and the process will vary based on how your original document is set up. Signing basically involves picking one or more sections in the document that are marked for signing, getting them signed and then embedding the signature into the XML document.

In my scenario I have an XML SOAP Envelope document with just a SOAP:Envelope and SOAP:Body with the body content being the content that is to be signed. The document looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP:Envelope xmlns:r1="http://www.routeone.com/namespace.messaging.diag#"
               xmlns:star="http://www.starstandards.org/STAR"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:oa="http://www.openapplications.org/oagis">
  <SOAP:Body>
    <!-- data to be signed here -->
  </SOAP:Body>
</SOAP:Envelope>

Once the document is signed it should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP:Envelope xmlns:r1="http://www.routeone.com/namespace.messaging.diag#" xmlns:star="http://www.starstandards.org/STAR" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oa="http://www.openapplications.org/oagis">
  <SOAP:Header>
    <SOAP-SEC:Signature MustUnderstand="1" xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12">
      <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
          <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
          <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
          <Reference URI="#Body">
            <Transforms>
              <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>6hpmccmjxQmAI143OhQfIWpkryw=</DigestValue>
          </Reference>
        </SignedInfo>
        <SignatureValue>sv8n4h0rV4Xmbl+M+w+MLl7lVA8KFsoWRx5DqSKkwSie32jOFoJt0WvH6UWRQInW5XpAL3OtNZcw8pbCdTtl8KSo7UIkl182zLFkdLh3o3EbiFqMw0iD0NM6L7UslaQ0uf8twnMFWlCA612icy76UfHtO6j4GcPeiBEwj5Y0YUg=</SignatureValue>
        <KeyInfo>
          <X509Data>
            <X509IssuerSerial>
              <X509IssuerName>CN=TestSignCert</X509IssuerName>
              <X509SerialNumber>75496503122422458150193540449068096025</X509SerialNumber>
            </X509IssuerSerial>
          </X509Data>
        </KeyInfo>
      </Signature>
    </SOAP-SEC:Signature>
  </SOAP:Header>
  <SOAP:Body id="Body">
    <!-- data to be signed here -->
  </SOAP:Body>
</SOAP:Envelope>

Note that the SOAP:Body tag gets an id attribute appended - the id is referenced with <Reference URI="#Body"> in the signature section. This points at the section (or more than one) that is to be signed.

The code to encode the document is shown below. Please note that the code is somewhat generic and should work with most SOAP envelope documents:

 
/// <summary>
/// Signs the SOAP document and adds a digital signature to it.
/// 
/// Note a lot of optional settings are applied against
/// key and certificate info to match the required XML document
/// structure the server requests.
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="certFriendlyName">Friendly Name of Cert installed in the Certificate Store under CurrentUser | Personal</param>
/// <returns></returns>
public XmlDocument SignSoapBody(XmlDocument xmlDoc, X509Certificate2 cert)
{
    // *** Add search Namespaces references to ensure we can reliably work 
    // *** against any SOAP docs regardless of tag naming
    XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
    ns.AddNamespace("SOAP", STR_SOAP_NS);
    ns.AddNamespace("SOAP-SEC", STR_SOAPSEC_NS);
 
    // *** Grab the body element - this is what we create the signature from
    XmlElement body = xmlDoc.DocumentElement.SelectSingleNode(@"//SOAP:Body", ns) as XmlElement;
    if (body == null)
        throw new ApplicationException("No body tag found");
 
    // *** We'll only encode the <SOAP:Body> - add id: Reference as #Body
    body.SetAttribute("id", "Body");
 
    // *** Signed XML will create Xml Signature - Xml fragment
    SignedXml signedXml = new SignedXml(xmlDoc);
 
    // *** Create a KeyInfo structure
    KeyInfo keyInfo = new KeyInfo();
 
    // *** The actual key for signing - MAKE SURE THIS ISN'T NULL!
    signedXml.SigningKey = cert.PrivateKey;
 
    // *** Specifically use the issuer and serial number for the data rather than the default
    KeyInfoX509Data keyInfoData = new KeyInfoX509Data();
    keyInfoData.AddIssuerSerial(cert.Issuer, cert.GetSerialNumberString());
    keyInfo.AddClause(keyInfoData);
 
 
    // *** provide the certficate info that gets embedded - note this is only
    // *** for specific formatting of the message to provide the cert info
    signedXml.KeyInfo = keyInfo;
 
 
    // *** Again unusual - meant to make the document match template
    signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
 
    // *** Now create reference to sign: Point at the Body element
    Reference reference = new Reference();
    reference.Uri = "#Body"// reference id=body section in same doc
    reference.AddTransform(new XmlDsigExcC14NTransform());  // required to match doc
    signedXml.AddReference(reference);
 
    // *** Finally create the signature
    signedXml.ComputeSignature();
 
    // *** Result is an XML node with the signature detail below it
    // *** Now let's add the sucker into the SOAP-HEADER
    XmlElement signedElement = signedXml.GetXml();
 
    // *** Create SOAP-SEC:Signature element
    XmlElement soapSignature = xmlDoc.CreateElement("Signature", STR_SOAPSEC_NS);
    soapSignature.Prefix = "SOAP-SEC";
    soapSignature.SetAttribute("MustUnderstand", "", "1");
 
    // *** And add our signature as content
    soapSignature.AppendChild(signedElement);
 
    // *** Now add the signature header into the master header
    XmlElement soapHeader = xmlDoc.DocumentElement.SelectSingleNode("//SOAP:Header", ns) as XmlElement;
    if (soapHeader == null)
    {
        soapHeader = xmlDoc.CreateElement("Header", STR_SOAP_NS);
        soapHeader.Prefix = "SOAP";
        xmlDoc.DocumentElement.InsertBefore(soapHeader, xmlDoc.DocumentElement.ChildNodes[0]);
    }
    soapHeader.AppendChild(soapSignature);
 
    return xmlDoc;
}
 

The signing code is a little more complex than it needs to be, mainly because I had to match the signature settings exactly to what the server expects. In simpler scenarios you may not have specify things like CanonicalizationMethod or the security provider or transform - especially if you're using .NET on both ends of the connection.

Note also that to write you're using the Private key so the key is being read out of the Certificate store. You can also read a certificate from a file, but this is not recommended given that the private key should be private and safely stored away. The easiest way to keep it isolated is in the certificate store. .NET 3.0 makes this very easy with the various X509 related certificate classes which are pretty easy to use.

To then validate the signature on the resulting XmlDocument you can use code like the following:

/// <summary>
/// Validates the Xml Signature in a document.
/// 
/// This routine is significantly simpler because the key parameters
/// are embedded into the signature itself. All that's needed is a
/// certificate to provide the key - the rest can be read from the
/// Signature itself.
/// </summary>
/// <param name="doc"></param>
/// <param name="publicCertFileName"></param>
/// <returns></returns>
public bool ValidateSoapBodySignature(XmlDocument doc, X509Certificate2 cert)
{
    // *** Load the doc this time
    SignedXml sdoc = new SignedXml(doc);
 
    // *** Find the signature and load it into SignedXml
    XmlNodeList nodeList = doc.GetElementsByTagName("Signature");
    sdoc.LoadXml((XmlElement)nodeList[0]);
 
    // *** Now read the actual signature and validate
    bool result = sdoc.CheckSignature(cert, true);
 
    return result;
}
 

The validation process is a heck of a lot easier because all of the information regarding keys and certificate information is all provided as part of the signature that's embedded in the document. You simply provide the cert and call CheckSignature() and off you go. Compared to signing this is a walk in the park.

Getting the Certificate

One thing that the code above doesn't really show is retrieving the certificate from the Certificate Store. The following methods (on the same class above) demonstrate how to retrieve a certificate from the store by its 'subject name' (ie. Issued To) value  and from a file.

/// <summary>
/// Retrieve a Certificate from the Windows Certificate store
/// by its Friendly name.
/// </summary>
/// <param name="subject">The friendly name of the certificate</param>
/// <param name="storeName">The store name type ( for example: Storename.My )</param>
/// <param name="storeLocation">Top level Location (CurrentUser,LocalMachine)</param>
/// <returns></returns>
public X509Certificate2 GetCertificateBySubject(string subject, StoreName storeName, StoreLocation storeLocation)
{
    X509Store xstore = new X509Store(storeName, storeLocation);
    xstore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
 
    X509Certificate2 cert = null;
    foreach (X509Certificate2 cert2 in xstore.Certificates)
    {
        string sub = wwUtils.ExtractString(cert2.Subject, "CN=", ",", true, true);
 
        if (subject == sub)
        {
            cert = cert2;
            break;
        }
    }
    return cert;
}
 
/// <summary>
/// Retrieve a Certificate from the Windows Certificate store
/// by its Friendly name.
/// 
/// This code pulls from CurrentUser - Personal cert store
/// </summary>
/// <param name="subject"></param>
/// <returns></returns>
public X509Certificate2 GetCertificateBySubject(string subject)
{
    return this.GetCertificateBySubject(subject, StoreName.My, StoreLocation.CurrentUser);
}
 
/// <summary>
/// Creates a Certificate from a file
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public X509Certificate2 GetCertificateFromFile(string fileName)
{
    X509Certificate cert = X509Certificate.CreateFromCertFile(fileName);
    return new X509Certificate2(cert);
}

As I mentioned earlier the Subject (or Issued To) value requires parsing and I use a library routine to extract a string the string. If you can ensure you have a friendly name on the key you can directly compare the FriendlyName property of the cert instead. The last method is there just for completelness - it doesn't add much value since it simply reads the cert from a file.

Watch out for PreserveWhiteSpace

One thing you'll want to be very careful of when signing documents is the PreserveWhiteSpace property on the XmlDocument object. This property must be set the same way for signing and validation. By default .NET has PreserveWhiteSpace off and in my experience most other solutions that use signatures expect PreserveWhiteSpace on. If you mismatch the white space preservation between encoding and validation, the validation will fail even if everything else is correct. You'd figure that the PreserveWhiteSpace setting would be part of the signature, but this must be done explicitly.

In your .NET code this basically means that when you load your XmlDocument you should immediately set the PreserveWhitespace property (if you need it set to true that is). This ensures that your encoding will respect the setting and also ensures documents get saved properly if you write them to disk. Make sure to set the property BEFORE loading any content into XmlDocument with Load() or LoadXml(). If your application will need to use the setting (or any other settings on XmlDocument) consistently you should create a factory method and always use it to create instances. For example on our processor object these three methods exist to load a document:

/// Gets a properly formatted instance of an Xml document
/// </summary>
/// <returns></returns>
public XmlDocument GetXmlDocument()
{
    XmlDocument doc = new XmlDocument();
    doc.PreserveWhitespace = true;
    return doc;
}
 
/// <summary>
/// Gets a properly formatted Xml Document from an input stream
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public XmlDocument GetXmlDocument(Stream stream)
{
    XmlDocument doc = this.GetXmlDocument();
    doc.Load(stream);
    return doc;
}
 
/// <summary>
/// Gets a properly formatted Xml document from a file name
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public XmlDocument GetXmlDocument(string fileName)
{
    XmlDocument doc = this.GetXmlDocument();            
    doc.Load(fileName);
    return doc;
}

Installing Actual Keys from Thawte

Once your testing is all done you'll probably want to install a real key from a certifcate authority. In my case we were using Thawte as our CA to provide the key.

When you receive your final keys from the Thawte you end up with a .PVK and .SPC file. The .pvk is the private key that was generated as part of the certificate request. The .spc contains the merged key files. As it is these two files aren't useful until you merge them and import them into the Windows Certificate Store.

You'll need to use a special tool to do this: PVK2PFX.EXE from the Windows SDK (my version is 6.0). The SDK installs with Visual Studio (if you didn't diable the option) and you can find the file in C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin.

pvk2pfx.exe -pvk Westwind.pvk -spc Westwind.spc -pfx Westwind.pfx

This will popup and prompt for your private key password and give you options on how to import the key into the certificate store.

pfxFromCA

Make sure that you mark the key as exportable which ensures that you can more easily move the key around without importing it first again. Don't check the first option - if you do a Windows dialog will pop up every time you try to use the private key even if you access it programmatically.

The next form prompts you were to store the key. If you accept automatic installation the key is stored in your CurentUser Personal store (StoreName.My, StoreLocation.CurrentUser). Alternately you can select the store to store your key in, but keep in mind that the store selection is limited to the current user.

When you're done you end up with .PFX file that is ready to be installed on other machines and the key installed in the current user Personal store.

IIS and Certificates

If you plan on using your key from a Web application, you'll have to copy the key from your Current User Store to the LocalMachine and then use StoreLocation.LocalMachine when specifying the store to look for the key.

The most reliable way to get the key installed into the Local Machine Certificate store. Open the certificate console and add the Local Machine Certificate snap-in. When you're there go to the personal location and then select Import Certificates from the context menu and import the key from the PFX you generated in the last step.

You should end with something like this:

LocalMachineStore

This will now let you access the store using StoreName.My and StoreLocation.LocalMachine which means this key is globally accessible. Remember to set the Friendly Name  after import so the code above can find the key.

IIS Permission Requirements - Adding NETWORK SERVICE

But you're not quite done yet if you need to access the key with IIS. I spent about an hour trying to figure out why the the Private Key kept failing under IIS but was working fine in my tests. It turns out the problem is that IIS (ie. NETWORK SERVICE) doesn't have rights to read the private key data from the store and so although the certificate loads, the private key fails when assigned.

Unfortunately unless you're running your Web application under SYSTEM or an Admin you will need to perform yet another configuration step to allow access to the key information to the Web user account. The process for this is outlined in a blog post here and it's mighty ugly. It essentially boils down to setting permissions on the Windows internal key files either manually or using a tool. Many thanks to Christian Weyer, who when pinged spotted the problem immediately.

In a nutshell you need to download WinHttpCertCfg.exe (download) and then add permissions to your installed key in the Certficate Store. The command line to do so is:

C:\>winhttpcertcfg -c LOCAL_MACHINE\my -s "West Wind Code Signing" -g -a NETWORK_SERVICE

-g is grant and -a is account, -s the subject and -c the store.The subject is the same as the Issued to value in the Console view.

And that should hopefully do the trick getting your key registered and usable with IIS. I say hopefully because unless you follow the steps exactly there's a good chance that something can go wrong as it did for me, mainly because I didn't find this information all in one place, but rather scattered over about 20 different sources.

Exporting the Public Key to .CER File

Once you have the PFX file, you can use the same procedure to install the key on other machines as needed. Remember that the key contains both the public and private keys, so you'll want to minimize exposure of this key and limit it to machines that really need to use it - you don't want to compromise the private key in sensitive applications.

Once the key is working for you locally you should probably create a .CER file of the public key that you can send to folks that need to validate the content you are signing or encrypting - in my case the host service provider. To export the key select it in the certificate manager and select All Tasks | Export.

Here's what you should see:

Export1

Don't export the private key (even if the option is available). You'll want to create a base 64 .CER file since that's the most widely understood certificate format:

Export2 

Pick a filename and you've got your public key that you can send along with signed messages.

Once you've exported the public key to a .cer file you should be able to sign the document from the certificate store and validate either with the the same certificate certificate from the certificate store (since it contains both the private and public keys) or the .cer file just exported.

There you have it - full round tripping of digital signatures. Not a trivial task to say the least. Hopefully having this information all in one place will be useful to some of you.

A few thoughts

<rant>

Man, was this a piece of freaking work! It really is no wonder that security for signatures and encryption with certificates is so lightly used on Windows. It's a royal pain in the ass, horribly documented and requires a host of tools. Why isn't this stuff all provided through the Management Console Snap In? Adding permissions would certainly be nice...

WinHttpCertCfg looks promising to do everything from importing to the appropriate store, and assigning rights but unfortunately I couldn't get it to work to import my PFX keys into the Local_Machine store. No error, no message it just didn't work...

The following should have worked but didn't:

C:\>winhttpcertcfg -c LOCAL_MACHINE\My  -a NETWORK_SERVICE -i WestWindCodeSigning.pfx

No error, but also no imported key which is bogus to the max. At least tell me something - what failed but no it just gives a blank report. I searched both Local_Machine and Current_User stores but no luck - they weren't imported.

It really bugs me that these tools are scattered all over the place and not available on the system in the first place and have to be downloaded and then worst of all seem to fail to work. Talk about non-discoverable and obtuse.

This is security by worthless and most likely unintended obfuscation... and in this scenario I'd argue it definitely hurts adoption of good practices in terms of security.

</rant>

Posted in .NET  Web Services  XML  Security  

The Voices of Reason


 

Doug Hennig
February 19, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

Hey Rick. When you apply for the cert, you're prompted to download the private key file. Once you're approved, you download the cert. If you miss the step for getting the private key (I did the first time), contact their tech support and they'll take care of it.

Rick Strahl
February 19, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

Thanks Doug. I didn't set this up actually, but the customer has a .PVK file. Problem is I don't know what to do with this <s>. The field won't work as a cert directly and it won't import into the Windows Cert Store.

With self signed cert, it generates a private public key pair that can be imported into the cert store - this way you get both keys into the store and you can use the cert both for signing and validating.

I haven't been able to do this with Thawtee cert. It's possible though that something needs to happen on the actual sending machine. Looking over the docs at Thawtee it seems to indicate that you have to import it first (with password prompt) then export it to a .PFX file (which is also what I did with my self signed cert for moving between machines here in the offiice). So something is missing from what I have.

Also, looking at the Thawtee site at least I'm not even quite sure what cert was used. Looks like a signature cert is the same as an Authenticode Cert? Those things aren't cheap...

As a side note - Thawtee has gone quite downhill from when I used them for SSL certs. Bad, slow site and horrible marketing stuff on their site instead of the no-nonsense approach of the old days along with the commensurate price increases. One more reason to hate Verisign...

Duncan Smart
February 19, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

Rick, the private key is on the machine where the certificate request was generated, when hopefully it was marked as exportable. So once you've got the cert (*.cer a BASE-64 bit of text) back from Thawte and re-married it up with its private key, you can then export it into a PKCS #12 (*.pfx) file - which will contain the key pair and cert which can then be transported to another server.

C
February 19, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

You should have received two files for the certificate. An .spc and .pvk file. Use the microsoft 'PVKIMPRT.EXE' tool to create the .pfx file.

Rick Strahl
February 20, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

Looks like pvk2pfx.exe is the tool to use on Vista or XP. This file comes with the latest (6.0 currently) Windows SDK. It looks like this will do the trick although I lack the password for extracting the full private/public keypair into a PFX.

There's more information about the tool here:
http://msdn2.microsoft.com/en-us/library/aa906332.aspx

For me the path of the file is here (which is different what the link above indicates):
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin

The 6.0 SDK installed with Visual Studio assuming you install the Windows SDKs (which is generally a good idea <g>).

Oleg
February 22, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

I wish you published this a couple days ago :) I just spent the last two days figuring the same thing out on my own. BTW, I answered your post on MSDN forum, but I looks like you figured it out about the same time. Funny, seem like we are working almost at the same time on integrating with the same automotive vendor system.

Anyway, good post. There is very little info on .NET implementation on DSIG.

Rick Strahl
February 23, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

@Oleg - I wish I had gotten it done a lot sooner too <g>. In the end I ended up spending two full days on getting everything to work properly which is kind of pathetic. Hopefully this post will help others to avoid this sort of pain.

Yup, automotive industry and in this case Route One is the service that's being called...

Oleg
February 27, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

Rick,
here is something I came across today that may save you some headaches. The code was working when I signed an XML file and then validated the same file. When RouteOne sent me a sample and their cert, it failed validation in the code. Apparently PreserveWhitespace property of the XmlDocument that you load signed file into can affect the results. For RouteOne files, it has to be set to true. Otherwise, stripping off the whitespaces invalidates the document integrity. So, set the PreserveWhiteSpace property to true before loading the XmlDocument.
Oleg

Nayan Hajratwala
February 29, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

Funny -- I'm working on an integration w/ the same vendor as well. I'm having quite a headache with the validation side of the house. I think my doc is getting corrupted somewhere. I'm a java guy though, so your examples didn't work for me ;-)

Rick Strahl
March 01, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

@Oleg - ah yes, just ran into that issue once we started exchanging messages with RouteOne. It appears both inbound and outbound messages have to a created with PreserveWhiteSpace=true although I haven't yet heard back whether this fixes the signature validation problem that RouteOne claims to have just yet. I'll find out Monday I guess.

Dinesh
October 15, 2008

# re: Digitally Signing an XML Document and Verifying the Signature

Rick,
Your article was very informative and provided a lot of help in my current integration. But I am still having issue validating the partners signature using their cert. The checksignature is always returning false. Would you guide me what I could be doing wrong? And yes, I am also working on a integration with RouteOne. :-)

Thanks,

John
January 15, 2009

# re: Digitally Signing an XML Document and Verifying the Signature

Your tip about setting PreserveWhitespace property got my code going, thanks for posting this! As others have said, I had to set this property to True before the CheckSignature method would return true.

tbtrieu
March 20, 2009

# re: Digitally Signing an XML Document and Verifying the Signature

I'm having trouble validating the signature against the cer file with CheckSignature, always return false. For those who have encountered this issue, what did you do to resolve it?

Thanks for your help.

Matt
April 03, 2009

# re: Digitally Signing an XML Document and Verifying the Signature

Dealing with this stuff on Windows is a disaster. I am doing digitally signed SAML Responses and the .NET API is completely half-baked. Definitely advise people to look at a commercial third-party component (ComponentSource has them) to deal with this.

Dustin Clark
June 23, 2009

# re: Digitally Signing an XML Document and Verifying the Signature

I wish I had found this 3 days ago. I chased my tail trying to figure out why the heck this worked on Vista/Windows 7 (IIS 7), but not Win Server 2003 (IIS 6). What a beating.

Dustin

John McKenna
June 29, 2009

# re: Digitally Signing an XML Document and Verifying the Signature

Thanks All-

This article was tremendously helpful. The site below was helpful for me solving the Private Key Access Problems.

http://www.enterprisedt.com/products/edtftpnetpro/doc/manual/privatekeyaccessproblems.html

Cheers!

vaibhav
May 14, 2010

# re: Digitally Signing an XML Document and Verifying the Signature

Hi,

Me too working on RouteOne component. Along with working with Signing & verifying i need to work on setting up HTTP Header fields( as i am working with WCF basichttp binding).
i.e I need to stuff information like Senderidentificatonnumber, Targetidentficationnumber, timestamp......etc.



most of the time we will use soap over http protocol.


now my input a message from message box
custom send pipeline a) create a soap envelope ( header +body) sign it
b) create HTTP HEADER
wHY? BECZ.. if some reason if soap envelope is corrupted while transmission it will retrieve the necessary information from the HTTP HEADER and creates a soap fault message & sends back the error message.

so that The HTTP adapter in the send port will write the headers to the message, and send the message over HTTP.


adpater: basichttpbinding
sendport

Please let me know if anyone used "Biztalk" to implement "RouteOne".

any type of help would be apprciated.

crokusek
October 25, 2011

# re: Digitally Signing an XML Document and Verifying the Signature

Enjoyed the rant. Posted a stackoverflow question after reading your article. Interestingly it was removed because the question itself was off topic. Really?! Any chance of an article on "ranting" to get some feedback? I hope you don't mind the rejected question here:

------------------------------------------------------------------------------------------------------------------------
Q: Are rants appreciated by popular opinion?

I am currently appreciative of rants because I find myself in various giant snake pits from hell every so often. Sure, religions/backgrounds encourage us to present our happy selves but what if by not ranting we're:

- Levitating ourselves up from a true sense of reality.
- Missing out on personal bonding through shared pain.
- Passively over-bonding with an oppressor.

Perhaps there is a ratio above which it becomes a depressing environment, thus a religious/background vote is just "forget about it".

So ranting, Yeah or nay?

[Example at end of Rick Strahl's article][1]
[stackoverflow etiquette][2]

[1]: http://www.west-wind.com/weblog/posts/2008/Feb/23/Digitally-Signing-an-XML-Document-and-Verifying-the-Signature
[2]: http://stackoverflow.com/faq#etiquette

Neil
February 04, 2013

# re: Digitally Signing an XML Document and Verifying the Signature

This has saved me so much time. Thanks ever so much.
Liked the rant about Winhttpcertcfg. That caused me a real headache in a previous project (I was getting errors when using it initially, so when the errors stopped I assumed it had worked. Not so!).

Scott Fletcher
August 03, 2017

# re: Digitally Signing an XML Document and Verifying the Signature

Thanks for the article. I just start working the RouteOne project 3 days ago and not yet gotten to the certificate part.

Currently stuck at this moment cuz xsd.exe doesn't exists in Dot Net Core, so tried several 3rd party software that successfuly generate C# source code from RouteOne XSD files. But kept running into exception error when serializing the C# source code cuz of badly build C# source code from RouteOne XSD files.

Which software or tool did you use to build RouteOne C#/VB/whatever source code that works successfuly?

Thanks.


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