Welcome to MSDN Blogs Sign in | Join | Help

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.

Published Wednesday, November 12, 2003 5:21 PM by shawnfa
Filed under: , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: XML Digital Signatures in .Net

Tuesday, May 04, 2004 5:02 PM by Benjamin Mitchell
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.

# re: XML Digital Signatures in .Net

Tuesday, May 04, 2004 5:40 PM by Shawn
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

# Virtual Thought &raquo; Nice article on SignedXML

Thursday, July 22, 2004 3:53 PM by TrackBack
Virtual Thought &raquo; Nice article on SignedXML

# re: XML Digital Signatures in .Net

Wednesday, December 21, 2005 11:37 AM by Kianoush
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?

# re: XML Digital Signatures in .Net

Wednesday, April 26, 2006 3:34 PM by Edith
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.

# re: XML Digital Signatures in .Net

Tuesday, June 27, 2006 8:35 AM by Anthony Wieser
What's to stop someone else signing the code again with a different key pair?

# re: XML Digital Signatures in .Net

Tuesday, June 27, 2006 8:35 PM by shawnfa
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

# re: XML Digital Signatures in .Net

Friday, February 13, 2009 4:51 PM by Volen

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

# re: XML Digital Signatures in .Net

Monday, February 23, 2009 2:37 PM by shawnfa

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

# re: XML Digital Signatures in .Net

Wednesday, March 04, 2009 10:46 AM by preguntoncojonero

and using xades ?? any sample code ???

thanks

# re: XML Digital Signatures in .Net

Monday, April 06, 2009 1:06 PM by Jake

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?

# re: XML Digital Signatures in .Net

Thursday, May 21, 2009 5:27 PM by shawnfa

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

-Shawn

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker