XML Digital Signatures in .Net


The .Net framework has built in support for signing XML files with an XML digital signature.  Here’s a sample of how to create and verify an enveloped digital signature using these classes.


There are three types of XML digital signatures:



  1. Enveloped – The signature is contained within the document it is signing
  2. Enveloping – The signed XML is contained within the signature element
  3. Detached – The signature is in a seperate document from the signed data

In this sample, I will create an enveloped signature over a order record recieved from a ficticous online store.  The XML for that order, saved in a file order.xml is:


<order>
   <purchase>
     <item quantity=”1″>Def Leppard: Pyromania</item>
     <item quantity=”1″>Ozzy Osbourne: Goodbye to Romance</item>
   </purchase>
   <shipping>
     <to>Shawn Farkas</to>
     <street>5 21st Street</street>
     <city>Seattle</city>
     <state>WA</state>
     <zip>98000</zip>
   </shipping>
   <payment>
     <card type=”visa”>0000-0000-0000-0000</card>
     <address sameAsShipping=”yes”/>
   </payment>
</order>

Creating the Signature


The first step in signing this document, is loading it into an XmlDocument object, and creating a SignedXml object for that XmlDocument:


// setup the document to sign
XmlDocument doc = new XmlDocument();
doc.Load(“order.xml”);
SignedXml signer = new SignedXml(doc);

Next, the key that will be used to sign the document must be setup.  In this sample, I will just generate a random RSA key, but in reality, the website would probably have an RSA key that they would always use to sign the documents with.


// setup the key used to sign
RSA key = new RSACryptoServiceProvider();
signer.KeyInfo = new KeyInfo();
signer.KeyInfo.AddClause(new RSAKeyValue(key));
signer.SigningKey = key;

The key must be set as the signing key, as well as placed in an RSAKeyValue clause.  The RSAKeyValue clause puts the public portion of the keypair into the signature itself, allowing anyone who retrieves the document to validate the signature, without having to know what key was used to sign it with.  The next step is to create a reference to the data being signed.


// create a reference to the root of the document
Reference orderRef = new Reference(“”);
orderRef.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signer.AddReference(orderRef);

A reference with a URI that is the empty string refers to the entire containing document.  However, since this is going to be an enveloped signature, validating the entire document would result in an invalid signature, since the signature value itself will be a part of the document.  Therefore, we must add an XmlDsigEnvelopedSignatureTransform, which prevents the signature validator from looking at the actual signature itself when validating the document.  The last step is to compute the signature, and add it to the document:


// add transforms that only select the order items, type, and
// compute the signature, and add it to the document
signer.ComputeSignature();
doc.DocumentElement.AppendChild(signer.GetXml());
doc.Save(“order-signed.xml”);

The resulting signed order looks like this:


<?xml version=”1.0″ standalone=”yes”?>
<order>
  <purchase>
    <item quantity=”1″>Def Leppard: Pyromania</item>
    <item quantity=”1″>Ozzy Osbourne: Goodbye to Romance</item>
  </purchase>
  <shipping>
    <to>Shawn Farkas</to>
    <street>5 21st Street</street>
    <city>Seattle</city>
    <state>WA</state>
    <zip>98000</zip>
  </shipping>
  <payment>
    <card type=”visa”>0000-0000-0000-0000</card>
    <address sameAsShipping=”yes” />
  </payment>
  <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#rsa-sha1″ />
      <Reference URI=””>
        <Transforms>
          <Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature” />
        </Transforms>
        <DigestMethod Algorithm=”http://www.w3.org/2000/09/xmldsig#sha1″ />
        <DigestValue>BPoz+CmKZyTATOhskqke3iOXmvA=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>gkw197s1e N60Og+U=</SignatureValue>
    <KeyInfo>
      <KeyValue>
        <RSAKeyValue>
          <Modulus>xC4bfXcL fUV5phs=</Modulus>
          <Exponent>AQAB</Exponent>
        </RSAKeyValue>
      </KeyValue>
    </KeyInfo>
  </Signature>
</order>

Verifying the Signature


Verifying the signature produced above is a very easy process, with the help of the SignedXml class.  It involves only three steps:



  1. Load the XML containing the signature
  2. Load the signature itself
  3. Call CheckSignature

The first step, loading the XML containing the signature is very similar to loading the unsigned XML above.


XmlDocument doc = new XmlDocument();
doc.Load(“order-signed.xml”);SignedXml verifier = new SignedXml(doc);

Next, the SignedXml class must be given the value of the signature it is to validate.  This can be done by looking for elements with the tag name of Signature:


verifier.LoadXml(doc.GetElementsByTagName(“Signature”)[0] as XmlElement);

Finally, the signature needs to be checked for validity:


if(verifier.CheckSignature())
    Console.WriteLine(“Signature verified”);
else
    Console.WriteLine(“Signature not valid”);

Finishing Up


The above example shows how to create a signature that prevents a malicious person from modifying the contents of a CD order.  However, nothing above prevents that person from reading the order and stealing the address or even credit card number of the person who placed it.  In a future post, I’ll show an example of using a new feature being added to Whidbey, XML Encryption, to prevent unwanted eyes from viewing this sensitive information.

Comments (12)

  1. In the code sample:

    // setup the key used to sign

    RSA key = new RSACryptoServiceProvider();

    signer.KeyInfo.AddClause(new RSAKeyValue(key));

    You need to add:

    signer.KeyInfo = new KeyInfo();

    before the AddClause line, otherwise the KeyInfo is null and throws an exception.

  2. Shawn says:

    Good catch Benjamin. I’d actually pulled this code out of a bigger sample app, and had forgotten to grab that line too. I’ll update the sample now. Thanks.

    I’d also recommend checking out my other post, which doesn’t embed the key into the signature (making it more secure). You can find it here:

    http://blogs.msdn.com/shawnfa/archive/2004/01/22/61779.aspx

    -Shawn

  3. Anonymous says:

    Virtual Thought &raquo; Nice article on SignedXML

  4. Kianoush says:

    Hi,

    Ich want to sign and encryt a particular portion of a XML-Fragment. supposing that an application should sign and encrypt the XML-Fragment, which should be secure. Another application should verifies the attached Signature and decrypt the encrypted XML-Fragment. Could you point me to the respective implementation for the purpose?

  5. Edith says:

    Hello,

    I make this example, but when I run this, marck an error en this line:

    doc.Load("order-signed.xml");SignedXml verifier = new SignedXml(doc);

    an de message is: Invalid character in a Base-64 string.

    I hope, someone can help me.

  6. Anthony Wieser says:

    What’s to stop someone else signing the code again with a different key pair?

  7. Nothing — in this example, you have to know the public key of hte expected signer, and filter on that.  You might also want to check out http://blogs.msdn.com/shawnfa/archive/2004/01/22/61779.aspx  for an alternative which does not have that requirement.

    -Shawn

  8. Volen says:

    Hi this all is very interesting, but what I really want to know is the process of the validation.

    The important step in signing an XML is pretty obvious:

    1. Compute the hash code (based on the xml body)

    2. Encrypt the hash code with the private key.

    3. Write the result as the signature value.

    My question is, what does the public key holder to do verify that the signature value is correct as in what is the actual process that takes place???

    – Volen

  9. If you’re interested in how this process takes place, it’s all documented by a publicly available standard: http://www.w3.org/TR/xmldsig-core/

    If the public key holder wants to verify that we did a correct job, they could implement an alternate version of the standard to verify the results.

    -Shawn

  10. preguntoncojonero says:

    and using xades ?? any sample code ???

    thanks

  11. Jake says:

    As simple as this seems, I have not been able to get CheckSignature() to return anything other than false…

    The signature tag has a "ds" namespace (I’m not having any trouble extracting the signature though), do you think this has anything to do with it?

  12. Nope – the ds namespace is normal in an XML digital signature.

    -Shawn