XML signatures define a powerful new technology that is used for digitally signing data that is represented in an XML format. XML has become such an important standard that it is now used in practically every conceivable programming scenario where the exchange of data takes place between applications. In many of these scenarios where data must be exchanged there may also be requirements for ensuring data integrity, authentication, and/or nonrepudiation. Recall from previous chapters that in public key encryption, the original data is encrypted with the public key and decrypted with the private key. In digital signatures, only a message digest of the original data is encrypted with the private key and decrypted with the public key. Then, the message digest is recalculated and checked to see if the data was tampered with. To make use of this new technology, you must learn two distinct aspects of XML signatures. The first is the XML signature syntax and processing rules as defined in the specification. The second is how to program with XML signatures on the .NET platform using the classes defined in the System.Security.Cryptography.Xmlnamespace . The XML signature specification is sometimes referred to as XMLDSIG and is defined by W3C. The specification provides the syntax and semantics of XML signature in the form of an XML schema [11] and Document Type Definition (DTD) as well as the associated processing rules.
The XML Signature SpecificationFor all of the nitty-gritty details, you may want to read through the official W3C XML Signature Syntax and Processing specification found at www.w3.org/TR/xmldsig- core /. You can also learn about this technology in the request for comment found at www.ietf.org/rfc/rfc3275.txt. The status of the XML signature specification is now at the Recommendation stage, meaning that it is stable and may now be used as an implementation reference for widespread deployment. What XML Signatures ProvideXML signatures can be applied to arbitrary data that may be located within or external to a given XML document. XML signatures support the following capabilities for data:
XML Signature SyntaxData objects are hashed and encrypted into a digest, which is then placed into an element, together with related information, which is then cryptographically signed. XML digital signatures are represented with the Signature element, which has the following layout. Again, the character ? denotes zero or one occurrence, + denotes one or more occurrences, and * denotes zero or more occurrences. <Signature ID?> <SignedInfo> <CanonicalizationMethod/> <SignatureMethod/> (<Reference URI? > (<Transforms>)? <DigestMethod> <DigestValue> </Reference>)+ </SignedInfo> <SignatureValue> (<KeyInfo>)? (<Object ID?>)* </Signature> THE XML SIGNATURE ELEMENTSThe Signature element is the main syntactic component used in XML signatures. All other XML signature elements are children of the Signature element. This section provides a brief overview of the XML signature elements, including the Signature element and each of its children.
It might be helpful to look again at a before and after picture to help make this syntax more concrete. The following invoice document is the same one that we saw in the XML encryption example earlier in this chapter. <invoice> <items> <item> <desc>Deluxe corncob pipe</desc> <unitprice>14.95</unitprice> <quantity>1</quantity> </item> </items> <creditinfo> <cardnumber>0123456789</cardnumber> <expiration>01/06/2005</expiration> <lastname>Finn</lastname> <firstname>Huckleberry</firstname> </creditinfo> </invoice> Now, after the signature is applied to this XML document, we obtain the following. The entire invoice element, along with its purchased items and credit information, is all wrapped up in a rather complex Signature element, which contains all the details needed to verify the RSA signature that is contained. < Signature xmlns="http://.../xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="..." /> <SignatureMethod Algorithm="http://... /xmldsig#rsa-sha1" /> <Reference URI="#MyDataObjectID"> <DigestMethod Algorithm="http://... /xmldsig#sha1" /> <DigestValue>fFyEpmWrhIwMjnrBZOOGmATvvG8= </DigestValue> </Reference> </SignedInfo> <SignatureValue>V2tW6tmZnOnuvKi8cZBXp... </SignatureValue> <KeyInfo> <KeyValue xmlns="http://www.w3.org/2000/09/ xmldsig#"> <RSAKeyValue> <Modulus>rJzbWtkPyhq+eBMhRdimd...</Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue> </KeyValue> </KeyInfo> <Object Id="MyDataObjectID"> <invoice xmlns=""> <items> <item> <desc>Deluxe corncob pipe</desc> <unitprice>14.95</unitprice> <quantity>1</quantity> </item> </items> <creditinfo> <cardnumber>0123456789</cardnumber> <expiration>01/06/2005</expiration> <lastname>Finn</lastname> <firstname>Huckleberry</firstname> </creditinfo> </invoice> </Object> </Signature> DETACHED, ENVELOPING, AND ENVELOPED SIGNATURESThere are three ways in which a signature may be defined within an XML document.
Classes Used in XML SignaturesLet's take a brief look at the classes that we will be working with in the upcoming XML signature example program. These classes are defined in the System.Security.Cryptography.Xml namespace. We will not have to do as much work as we did in the previous section on XML encryption, because the .NET library provides more complete support for XML signatures. THE DATAOBJECT CLASSThe DataObject class encapsulates an XML element that holds the data to be signed. We will use the Data property, which gets or sets the data of the DataObject , and the ID property, which gets or sets the ID of the DataObject . public XmlNodeList Data {get; set;} public string Id {get; set;} THE SIGNEDXML CLASSThe SignedXml class encapsulates the core XML signature object for a signed XML document. This class has several methods and properties that we will use in the upcoming program example. The SigningKey property gets or sets the asymmetric algorithm key used for signing the XML element. public AsymmetricAlgorithm SigningKey {get; set;} The AddObject method adds a new DataObject to the list of objects to be signed. public void AddObject( DataObject dataObject ); The AddReference method adds a Reference element to the list of references that are to be hashed and signed. public void AddReference( Reference reference ); The KeyInfo property gets or sets the KeyInfo element of the SignedXml object. public KeyInfo KeyInfo {get; set;} The ComputeSignature method performs the actual signature operation on the XML element. There are two overloaded versions of this method. We will use the one with no parameter. public void ComputeSignature(); public void ComputeSignature( KeyedHashAlgorithm macAlg ); The EnvelopingXmlSignature ExampleThe EnvelopingXmlSignature example program demonstrates how to sign and validate an XML signature. The entire source code for this simple program is shown in the following code listing. The first thing that the program does in the Main method is call the CreateXMLDocument method, which creates an XML file that contains a simple invoice document named OriginalInvoice.xml . Next, the program calls the PerformXMLSignature method that computes an XML signature for the invoice and then writes the resulting signed invoice to a file named SignedInvoice.xml . Then, the VerifyXMLSignature method is called, which verifies that the signature in SignedInvoice.xml is valid, and it displays the fact that it is indeed valid in the console window. Next, in order to show the effect of tampering with a signed XML document, the TamperSignedXMLDocument method is called, which takes the valid SignedInvoice.xml file and modifies the credit-card number in the invoice, and then writes the modified result to the file named TamperedInvoice.xml . This tampered file contains the original invoice data but with a forged credit-card number and the original signature, which is no longer valid due to the tampered data. Finally, the VerifyXMLSignature method is called once again, but, this time, it attempts to verify the signature in TamperedInvoice.xml , and the negative result is then displayed in the console window. Take a look through the following source code listing to understand how these operations work, and then study the console output and the three resulting XML files that follow. Here is the source code. //EnvelopingXMLSignature.cs //NOTE: must add a project reference to System.Security using System; using System.IO; using System.Xml; using System.Security.Cryptography; using System.Security.Cryptography.Xml; class EnvelopingXMLSignature { static void Main(string[] args) { //create participants Sender sender = new Sender(); Receiver receiver = new Receiver(); Tamperer tamperer = new Tamperer(); //show the effects of signing and tampering sender.CreateXmlDocument("OriginalInvoice.xml"); sender.PerformXmlSignature( "OriginalInvoice.xml", "SignedInvoice.xml"); receiver.VerifyXmlSignature("SignedInvoice.xml"); tamperer.TamperSignedXmlDocument( "SignedInvoice.xml", "TamperedInvoice.xml"); receiver.VerifyXmlSignature("TamperedInvoice.xml"); } } class Sender { public void CreateXmlDocument(String originalFilename) { //establish the original XML document XmlDocument xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; xmlDoc.LoadXml( "<invoice>\n" + " <items>\n" + " <item>\n" + " <desc>Deluxe corncob pipe</desc>\n" + " <unitprice>14.95</unitprice>\n" + " <quantity>1</quantity>\n" + " </item>\n" + " </items>\n" + " <creditinfo>\n" + " <cardnumber>0123456789</cardnumber>\n" + " <expiration>01/06/2005</expiration>\n" + " <lastname>Finn</lastname>\n" + " <firstname>Huckleberry</firstname>\n" + " </creditinfo>\n" + "</invoice>\n"); //write original XML document to file StreamWriter file = new StreamWriter(originalFilename); file.Write(xmlDoc.OuterXml); file.Close(); //let the user know what happened Console.WriteLine( "Original XML document written to\n\t:" + originalFilename); } public void PerformXmlSignature( String originalFilename, String signedFilename) { //load the XML document XmlDocument xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; xmlDoc.Load(originalFilename); //create signature wrapper with default RSA key RSA key = RSA.Create(); SignedXml signedXml = new SignedXml(); signedXml.SigningKey = key; //create data object to hold the data to be signed DataObject dataObject = new DataObject(); dataObject.Data = xmlDoc.ChildNodes; //set data object id for URI ref from elsewhere dataObject.Id = "MyDataObjectID"; //add data object to be signed to signature wrapper signedXml.AddObject(dataObject); //create reference object to ref data object Reference reference = new Reference(); reference.Uri = "#MyDataObjectID"; //add reference object to signature wrapper signedXml.AddReference(reference); //add key information to signature wrapper KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(new RSAKeyValue(key)); signedXml.KeyInfo = keyInfo; //generate the XML signature signedXml.ComputeSignature(); //apply XML signature to XML document XmlElement xmlSignature = signedXml.GetXml(); xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; XmlNode xmlNode = xmlDoc.ImportNode(xmlSignature, true); xmlDoc.AppendChild(xmlNode); xmlDoc.Save(signedFilename); //let the user know what happened Console.WriteLine( "Signed XML document written to\n\t:" + signedFilename); } } class Receiver { public void VerifyXmlSignature(String signedFilename) { //load signed XML document XmlDocument xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; xmlDoc.Load(signedFilename); //create signature wrapper from signed XML file SignedXml signedXml = new SignedXml(xmlDoc); //get <Signature> node (assume only one exists) XmlNodeList nodeList = xmlDoc.GetElementsByTagName( "Signature", "http://www.w3.org/2000/09/xmldsig#"); signedXml.LoadXml((XmlElement)nodeList[0]); //let the user know what happened if (signedXml.CheckSignature()) Console.WriteLine( signedFilename + " signature is VALID"); else Console.WriteLine( signedFilename + " signature is NOT VALID"); } } class Tamperer { public void TamperSignedXmlDocument( String signedFilename, String tamperedFilename) { //load signed XML document XmlDocument xmlDoc = new XmlDocument(); xmlDoc.PreserveWhitespace = true; xmlDoc.Load(signedFilename); //tamper signed XML document and write to file XmlNodeList nodeList = xmlDoc.GetElementsByTagName("cardnumber"); XmlNode xmlOldNode = (XmlElement)nodeList[0]; XmlNode xmlNewNode = xmlOldNode.Clone(); xmlNewNode.InnerText = "9876543210"; xmlOldNode.ParentNode.ReplaceChild( xmlNewNode, xmlOldNode); xmlDoc.Save(tamperedFilename); //let the user know what happened Console.WriteLine( "Tampered signed XML document written to\n\t" + tamperedFilename); } } If you run the preceding program, you will see the following console window output. In particular, notice that the file named SignedInvoice.xml contains a valid XML signature, but the file named TamperedInvoice.xml does not contain a valid XML signature. These two files are identical except that the credit-card information was modified after the signature was generated. Since only the rightful owner of the private key used to generate the signature knows the value of that key, it is impossible for nonauthorized individuals to modify the document in an undetectable manner. This is because nonauthorized individuals do not know the correct value of this private key. Of course, in this simple example the code that does the tampering happens to be the same program that does the signing, which is a bit unrealistic if not downright silly. Nobody would intentionally tamper with data that they themselves have produced. This example is only intended to prove the concept that, by signing a document, you can detect if it has been modified by someone else after signing has taken place. Original XML document written to OriginalInvoice.xml Signed XML document written to SignedInvoice.xml SignedInvoice.xml signature is VALID Tampered signed XML document written to TamperedInvoice.xml TamperedInvoice.xml signature is NOT VALID Press any key to continue Now, let's look at the three XML files that this program has generated: OriginalInvoice.xml, SignedInvoice.xml , and TamperedInvoice.xml . First, here is OriginalInvoice.xml . Notice that the credit-card number is set to 0123456789. We will see what happens if we try tampering with this card number shortly. <invoice> <items> <item> <desc>Deluxe corncob pipe</desc> <unitprice>14.95</unitprice> <quantity>1</quantity> </item> </items> <creditinfo> <cardnumber> 0123456789 </cardnumber> <expiration>01/06/2005</expiration> <lastname>Finn</lastname> <firstname>Huckleberry</firstname> </creditinfo> </invoice> Now, let's look at the SignedInvoice.xml file. Several parts of the file have been shortened using ellipses, and a few carriage returns have been added to make it more legible and to fit it within the confines of this printed page. As you can see, the signed version of this file contains information on both the signature and the public aspects of the key that corresponds with the private key used to used sign the file. <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="..." /> <SignatureMethod Algorithm="..." /> <Reference URI=" #MyDataObjectID "> <DigestMethod Algorithm="..." /> <DigestValue>...</DigestValue> </Reference> </SignedInfo> <SignatureValue>...</SignatureValue> <KeyInfo> <KeyValue xmlns="..."> <RSAKeyValue> <Modulus>...</Modulus> <Exponent>...</Exponent> </RSAKeyValue> </KeyValue> </KeyInfo> <Object Id=" MyDataObjectID "> <invoice xmlns=""> <items> <item> <desc>Deluxe corncob pipe</desc> <unitprice>14.95</unitprice> <quantity>1</quantity> </item> </items> <creditinfo> <cardnumber> 0123456789 </cardnumber> <expiration>01/06/2005</expiration> <lastname>Finn</lastname> <firstname>Huckleberry</firstname> </creditinfo> </invoice> </Object> </Signature> We will not bother showing the entire contents of the TamperedInvoice.xml , since it is identical to the SignedInvoice.xml file in every way except that the credit-card number is changed from 0123456789 to 9876543210, as shown in the following snippet. You can verify that this is the only difference by comparing these two files using a tool such as Windiff.exe . <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> ... <creditinfo> <cardnumber> 9876543210 </cardnumber> <expiration>01/06/2005</expiration> <lastname>Finn</lastname> <firstname>Huckleberry</firstname> </creditinfo> </invoice> </Object> </Signature> |