Verifying an XMLDSIG Signature

for RuBoard

We turn our attention now to the process of verifying an XMLDSIG digital signature using the .NET Framework. Signature verification is much easier in general than signature generation, because the only variable in the verification process is finding the correct verification key. The general procedure for verifying an XMLDSIG signature consists of the following steps:

  1. Obtain the XMLDSIG ds:Signature you want to verify as an XmlElement object by using the XML- related classes in the System.Xml namespace. Depending on the method in which your application receives the XMLDSIG signature, you may have to scan an XmlDocument for the ds:Signature element, read the document containing the signature from an on-disk file, or parse a network stream for the relevant data. Ultimately, you need to have the signature available as an XmlElement .

  2. Create a new SignedXml object. If the signature you want to verify is an enveloped signature, you will need to use one of the one-argument constructors for the SignedXml class to pass the document context for the signature.

  3. Load the contents of the signature into the SignedXml object by using the LoadXml method.

  4. Use one of the CheckSignature or CheckSignatureReturningKey methods available on the SignedXml object to validate the digital signature. All of these methods have Boolean return values and return true if and only if the signature validates correctly.

Depending on your particular situation, you may need to explicitly specify the verification key in step 4. If your application is handed the ds:Signature element as an XmlElement and includes sufficient KeyInfo to find the public verification key, the entire verification procedure is as simple, as shown in the following method:

 public static bool VerifySignature(XmlElement signatureElement) {     SignedXml signedXml = new SignedXml();     signedXml.LoadXml(signatureElement);     return signedXml.CheckSignature(); } 

Of course, this method performs no error handling; various exceptions (usually of the CryptographicException class) will be thrown by the XMLDSIG classes if the signatureElement does not contain a well- formed XMLDSIG signature, or the verification key cannot be located.

Listing 32.37 contains sample code for a program that will attempt to find and verify an XMLDSIG signature in an on-disk file. The basic steps used to verify the signature are the same as in the previous code snippet, but the program demonstrates some additional routines that you may find useful in your own programs. Given the name of a file on-disk that contains XML content in args[0] (the first command-line argument to the program), we can read the contents of the file into an XmlDocument object as follows :

 // Create a new XmlDocument object XmlDocument xmlDocument = new XmlDocument(); // Tell the XmlDocument to preserve white space in the input xmlDocument.PreserveWhitespace = true; // Load the contents of the file into the XmlDocument xmlDocument.Load(new XmlTextReader(args[0])); 

CAUTION

It is very important that you configure the XmlDocument to preserve whitespace before loading signed XML content and XMLDSIG objects. Normally, the XML classes in the .NET Framework ignore "insignificant whitespace," such as the whitespace occurring between two XML tags not in mixed content. However, the Canonical XML algorithm used by XMLDSIG cannot distinguish between significant and insignificant whitespace, and therefore assumes that all whitespace is significant. If you do not tell the XML classes to preserve whitespace, they will automatically omit insignificant whitespace, which will likely lead to a different canonicalized form, different hash values, and ultimately break the digital signature.


After we have the XML containing the signature in an XmlDocument , the next step is to locate the top-level ds:Signature element. One convenient way to find the signature element is to perform an XPath query for it from the root element of the document. The following is a code snippet that locates the first ds:Signature element located anywhere within the xmlDocument :

 // Our XPath query is going to look for a Signature tag in the // XMLDSIG namespace, so we have to initialize an XmlNamespaceManager // and tell it about the URI that identifies the XMLDSIG namespace // The next two lines of code do that. XmlNamespaceManager nsm = new XmlNamespaceManager(xmlDocument.NameTable); nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); // Now call the XPath query method on XmlDocument // This will return the first XmlNode that matches the // "//ds:Signature" query. That query will search the entire XML // tree underneath the document root looking for an element with // tag name "Signature" in the namespace bound to "ds" in the // XmlNamespaceManager. XmlNode node = xmlDocument.SelectSingleNode("//ds:Signature", nsm); 

This code fragment works by searching the XmlDocument for elements that have the tag name Signature in the XMLDSIG namespace. (The static String variable SignedXml.XmlDsigNamespaceUrl holds the namespace URI defined by the XMLDSIG standard.) If multiple ds:Signature elements exist in the document, this routine finds the first such element in XPath document order; if you want to find all ds:Signature nodes, use the SelectNodes method to return them as an XmlNodeList .

The final step to perform before attempting signature validation is to populate a SignedXml object with the contents of the ds:Signature element:

 SignedXml signedXml = new SignedXml(); signedXml.LoadXml((XmlElement)node); 

Now we are ready to try to validate the signature. The SignedXml class contains four methods for checking the cryptographic validity of the signature information contained within it; three of the methods are overloads on CheckSignature and the fourth method is CheckSignatureReturningKey . The following is a detailed description of each of the methods; all return Boolean values:

  • CheckSignature(AsymmetricAlgorithm key) This is the most basic method available for validating an XMLDSIG signature. Each Reference in the SignedInfo is validated by independently obtaining the input content, applying the Reference Processing Model, and computing the hash of output of the chain of transforms. If the computed hash value of any Reference differs from the specified hash value, the signature is immediately declared invalid and CheckSignature returns false . After the References have been processed , the SignedInfo element is canonicalized, hashed , and compared to the result of validating the SignatureValue using the input key argument. The signature validates (and the method returns true ) only if the computed hash of SignedInfo matches the hash value inside the SignatureValue .

  • CheckSignature(KeyedHashAlgorithm macAlg) This method works identically to CheckSignature(AsymmetricAlgorithm) except that it validates XMLDSIG signatures protected with a message authentication code, such as HMAC-SHA1.

  • CheckSignature() This method uses the information contained within the KeyInfo section of the signature to discover the validation key. The GetPublicKey() method on the SignedXml object is called repeatedly to produce candidate public keys, and each candidate is tried in turn to validate the signature. The GetPublicKey() method on SignedXml recognizes only the RSAKeyValue and DSAKeyValue types of KeyValue within KeyInfo , but, as described in the "Extending System.Security.Cryptography.Xml for Custom Processing" section later in this chapter, you can subclass SignedXml to define your own GetPublicKey() method to look at application-specific data or other types of KeyInfo to find the validation key.

  • CheckSignatureReturningKey(out AsymmetricAlgorithm signingKey) This method works exactly like CheckSignature() except that when successful, it assigns the actual key used to validate the signature to the signingKey argument.

Our task is complete when we have the result of checking the signature. In the program shown in Listing 32.37, we use the fourth method, CheckSignatureReturningKey , so that we can print out the actual key used to verify the signature. If we were verifying an enveloped signature, we would have to modify only the initial construction of the SignedXml object, passing the XmlDocument as an argument to set the proper verification context.

Listing 32.37 Verifying an XMLDSIG Signature Stored in an On-Disk File
 using System; using System.Security.Cryptography; using System.Security.Cryptography.Xml; using System.IO; using System.Xml; using System.Text; public class VerifySignature {   static void Main(String[] args) {     // Create a new XmlDocument object     XmlDocument xmlDocument = new XmlDocument();     // Tell the XmlDocument to preserve white space in the input     xmlDocument.PreserveWhitespace = true;     // Load the contents of the file into the XmlDocument     xmlDocument.Load(new XmlTextReader(args[0]));     // Our XPath query is going to look for a Signature tag in the     // XMLDSIG namespace, so we have to initialize an XmlNamespaceManager     // and tell it about the URI that identifies the XMLDSIG namespace     // The next two lines of code do that.     XmlNamespaceManager nsm = new XmlNamespaceManager(xmlDocument.NameTable);     nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl);     // Now call the XPath query method on XmlDocument     // This will return the first XmlNode that matches the     // "//ds:Signature" query. That query will search the entire XML     // tree underneath the document root looking for an element with     // tag name "Signature" in the namespace bound to "ds" in the     // XmlNamespaceManager.     XmlNode node = xmlDocument.SelectSingleNode("//ds:Signature", nsm);     // Create a new SignedXml object     SignedXml signedXml = new SignedXml();     // Load in the Signature element     signedXml.LoadXml((XmlElement)node);     AsymmetricAlgorithm key;     // Use CheckSignatureReturningKey so we get a handle     // back on the key object that was discovered to     // be the validation key     if (signedXml.CheckSignatureReturningKey(out key)) {       Console.WriteLine("Signature check OK");       Console.WriteLine(key.ToXmlString(false));     }  else {       Console.WriteLine("Signature check FAILED");     }     return;   } } 
for RuBoard


. NET Framework Security
.NET Framework Security
ISBN: 067232184X
EAN: 2147483647
Year: 2000
Pages: 235

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net