Signing Xml Documents

If you want to check if an XML is not changed, you can sign an XML file. Following Code show you how to sign a XML  document.

 

X509Certificate2 cert = loadCertifate();

XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load(@"c:\Temp\Unsigned.xml");

SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = cert.PrivateKey;

//
// Add a signing reference, the uri is empty and so the whole document
// is signed.
Reference reference = new Reference();
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.Uri = "";
signedXml.AddReference(reference);

//
// Add the certificate as key info, because of this the certificate
// with the public key will be added in the signature part.
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(cert));
signedXml.KeyInfo = keyInfo;

// Generate the signature.
signedXml.ComputeSignature();

// Appends the sigbature at the end of the xml document.
doc.DocumentElement.AppendChild(signedXml.GetXml());


The result XML looks like that.

 

<test>
  <
data Id="test1">
    <
description>bla bla bla</description>
  </
data>
  <
data Id="test2">
    <
description>bla bla bla</description>
  </
data>
  <
data Id="test3">
    <
description>bla bla bla</description>
  </
data>
  <
Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <
SignedInfo>
      <
CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <
SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
      <
Reference URI="">
        <
Transforms>
          <
Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
         
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
       
</Transforms>
        <
DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <
DigestValue>viYAUV9bBBo1nYzq0+8ehB8kdjc=</DigestValue>
      </
Reference>
    </
SignedInfo>
    <
SignatureValue>fTD4lMunhlYLWBCPDVxLL9JVXc1WWOSx+evx/Sca/3LSvEnH1ez9WA==</SignatureValue>
    <
KeyInfo>
      <
X509Data>
        <
X509Certificate>.....</X509Certificate>
      </
X509Data>
   
</KeyInfo>
  </
Signature>
</
test>

 


In some cases it is necessary that only some parts of an XML are signed and not the whole document.  For this you must change the URI so it references to a part of the XML, something like this.

 

//
// Add a signing reference, the uri refernces to an element with the
// ID "test1"
Reference reference = new Reference();
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.Uri = "#test1";
signedXml.AddReference(reference);

 


This works in this case fine, because the SignedXml has only one implementation, if the Uri starts with ‘#’, the class searches in the document for an attribute with the name “Id” and the value of the Uri without the ‘#’ character. If you have an Xml which as a different Id attribute name or you want specified any other path string like XPath the Signed Xml does not work it throws the Exception with the message “Malformed reference element.  

 

If you want to sign such XML documents than you must create a new class and inherit from SignedXml, in this class you must override the “GetIdElement” method. This method gets the XML document and the Uri Valuewithout the ‘#’ character. The Method must return the element which you want to sign.  A very simple solution is:

 

public class MySignedXml : SignedXml
{
    public MySignedXml(XmlDocument document) : base(document) { }
    public override XmlElement GetIdElement(
        XmlDocument document, string idValue)
    {
        return (XmlElement)
            document.SelectSingleNode("//*[@MyId='" + idValue + "']");
   
}
}

 




Posted May 13 2009, 04:35 PM by Rolf Nebhuth
Filed under:
developers.de is a .Net Community Blog powered by daenet GmbH.