Creating XMLDSIG-Compliant Signatures Using the .NET Framework

for RuBoard

Creating XMLDSIG-Compliant Signatures Using the .NET Framework

The .NET Framework includes a comprehensive set of object classes and methods for creating and verifying XMLDSIG digital signatures. All of these classes are located in the System.Security.Cryptography.Xml namespace. In general, there is a .NET Framework class for each defined XMLDSIG element plus the utility class SignedXml that provides high-level signature generation and verification functions. Table 32.7 provides a summary of the classes in the System.Security.Cryptography.Xml namespace. This section demonstrates how SignedXml and the other XMLDSIG classes can be used to construct XMLDSIG signatures.

NOTE

The classes in the System.Security.Cryptography.Xml namespace are contained in the System.Security.dll assembly, so to use these classes in your own programs, you will need to explicitly reference this assembly when compiling your programs. These classes also depend on the .NET Framework XML classes contained within the System.XML.dll assembly. For Visual Studio .NET, perform the following steps to add a reference to the System.Security assembly to your project:

  1. In the Solution Explorer, select References, right-click, and select Add Reference.

  2. Select System.Security from the selection list.

  3. Either double-click the System.Security or highlight System.Security and click the Select button.

  4. Select OK to complete the addition of System.Security to your Visual Studio .NET project.

(A reference to System.XML.dll is already included by default when you create a Visual Studio .NET project.)

If you are compiling your programs by invoking the compiler from the command line, you will need to explicitly reference both System.Security.dll and System.XML.dll . For example, when using the Visual C# .NET or Visual Basic .NET compiler, you will need to add the command-line arguments /r:System.Security.dll and /r:System.XML.dll .


Table 32.7. Classes in the System.Security.Cryptography.Xml Namespace
System.Security.dll Object Description
SignedXml Utility class for creating and verifying XMLDSIG signatures
Signature Represents the ds:Signature element
SignedInfo Represents the ds:SignedInfo element
Reference Represents the ds:Reference element
TransformChain Represents the content of a single ds:Transforms element, which is an ordered sequence of ds:Transform elements
Transform Represents the ds:Transform element
KeyInfo Represents the ds:KeyInfo element
RSAKeyValue Represents the ds:RSAKeyValue element
DSAKeyValue Represents the ds:DSAKeyValue element
KeyInfoName Represents the ds:KeyName element
KeyInfoRetrievalMethod Represents the ds:RetrievalMethod element
KeyInfoX509Data Represents the ds:X509Data element
KeyInfoNode A generic handler for child elements of ds:KeyInfo that do not have specific object representations in the .NET Framework
DataObject Represents the ds:Object element

The .NET Framework object model for XMLDSIG mirrors the layout of XML elements in the standard. In general, there is a class present in the System.Security.Cryptography.Xml namespace for each complex type (for example, ds:SignedInfo ) defined in XMLDSIG. Simple types, such as the Algorithm attribute on ds:SignatureMethod , are represented as typed properties on the classes representing their parent elements. The top-level SignedXml class provides high-level functions for generating and verifying signature (the ComputeSignature and CheckSignature methods, respectively) and for combining together Reference , KeyInfo , and DataObject objects to represent an overall ds:Signature object. Table 32.8 provides a summary of the important properties and methods available on a SignedXml object.

Table 32.8. Properties and Methods of the SignedXml Class
Property/Method Name Description
SigningKey (property) Gets/sets the key or key pair used to generate and/or verify the digital signature.
SignatureMethod (property) Gets the URI identifying the digital signature algorithm.
SignatureValue Gets the binary value of the digital signature.
KeyInfo Gets/sets the KeyInfo object associated with this XMLDSIG signature, representing the entire contents of the ds:KeyInfo element.
Signature Gets the Signature object that represents the contents of the ds:Signature element.
SignedInfo Gets the SignedInfo object that represents the contents of the ds:SignedInfo element within the ds:Signature element.
AddObject Adds an Object to the list of Objects to be embedded within the signature.
AddReference Adds a Reference object to the list of References in the SignedInfo portion of the signature.
ComputeSignature Calculates the digital signature value using the SigningKey .
CheckSignature Attempts to verify the digital signature.
CheckSignatureReturningKey Attempts to verify the digital signature. Returns the verification key in its out parameter if successful.

Our first set of examples demonstrates how to construct detached, wrapped, and enveloped XMLDSIG signatures. The procedures for creating XMLDSIG signatures all follow the same general set of steps:

  1. Create a new SignedXml object to hold the signature information and associate it with a signing key.

  2. Associate the signing key with the SignedXml object by setting its SigningKey property. This key will be either an AsymmetricAlgorithm or a KeyedHashAlgorithm .

  3. Create a Reference object for each piece of content to be signed by the signature and add the Reference to the SignedXml object (via the AddReference method on SignedXml ). Creating the Reference will generally require specifying the value of its Uri property and may also require defining a TransformChain for the Reference .

  4. Optionally, add a KeyInfo object to the SignedXml object to communicate key- related information to the recipient of the signature.

  5. Compute the XMLDSIG signature by calling one of the overloads of the ComputeSignature method on SignedXml .

  6. Retrieve the completed ds:Signature object by calling the SignedXml.GetXml() method.

Individual scenarios may require slight modifications to these steps, but the general outline is the basis for all signature computation in the .NET Framework.

To see these steps in action, consider our first signature construction scenarioa detached signature construction method for URI-addressable content using RSA or DSA key pairs. Our goal is to create a subroutine that takes two input argumentsan AsymmetricAlgorithm signing key and a String URIand returns a detached signature for the contents of that URI using the specified key. Listing 32.21 shows the code for such a subroutine.

NOTE

Source code in electronic form for all code listings in this chapter can be downloaded from the publisher's Web site for this book.


Listing 32.21 Compute a Detached Signature Using an AsymmetricAlgorithm
 public static XmlElement CreateDetachedSignature (AsymmetricAlgorithm signingKey, String graphics/ccc.gif uri) {   // Create the SignedXml message   SignedXml signedXml = new SignedXml();   signedXml.SigningKey = signingKey;   // Create a Reference to point to the to-be-signed content   Reference reference = new Reference(uri);   // Add the Reference to the SignedXml message   signedXml.AddReference(reference);   // Compute the XML Digital Signature   signedXml.ComputeSignature();   // Return the XmlElement containing the XMLDSIG signature   return signedXml.GetXml(); } 

The sample code in Listing 32.21 directly follows steps 16 outlined earlier. Input arguments to the CreateDetachedSignature method are the signingKey (an AsymmetricAlgorithm ) and a uri (a String) identifying the contents to be signed. The first thing the method does is create a new SignedXml object using its null constructor; the null constructor form can be used when creating detached or wrapped XMLDSIG signatures. After the SignedXml object is created, its SigningKey property must be set to the AsymmetricAlgorithm object that is to be used to perform the signature function. The third step is to create a Reference object identifying the to-be-signed content. In this particular case, no Transforms are required, so we can create the necessary Reference object using the one-argument constructor that accepts a String containing the URI of the to-be-signed content. We then add the Reference to the SignedXml object using the AddReference method; this adds the Reference to the list of References maintained within a SignedInfo object inside a Signature object, which is itself stored inside the SignedXml class. Finally, the call to the ComputeSignature() method calculates the hashes of all referenced content, canonicalizes the XML element representation of the SignedInfo object, hashes the resulting XML element, signs this hash, and constructs the overall XMLDSIG ds:Signature element. The actual XML element is obtained by calling SignedXml's GetXml() method.

NOTE

By default, the .NET Framework XMLDSIG classes use SHA1 wherever a hash algorithm is needed, so the DigestMethod of a Reference is initialized to the URI identifier for SHA1. Another hash algorithm can be used by setting the DigestMethod property on the Reference appropriately.


The XMLDSIG signature generated by the CreateDetachedSignature method in Listing 32.21 is generally the most compact signature possible because it contains only the URI of the content, the content's hash, and the digital signature value itself. Listing 32.22 contains a sample XMLDSIG signature created using a random RSA key for the content located at the URI http://www.farcaster.com/index.htm .

Listing 32.22 Sample Detached XMLDSIG Signature Using RSA
 <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="http://www.farcaster.com/index.htm">       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />       <DigestValue>XoaHIm+jLKnPocR7FX0678DUOqs=</DigestValue>     </Reference>   </SignedInfo>   <SignatureValue>     M5BhlrxPaOEYcCwSZ3WEDR6dfK5id/ef1JWK6OO5PEGHp9/JxrdA2xT5TYr5egArZGdVURpM     VGUeViWoeHcGAyMNG9Cmc/I56sYd/TSV/MjLgb/mxq+6Fh/HWtVhjHIG+AdL4lA+ZxxEi147     QVVzgCl4+dvIZaGo7oAFneDKv0I=   </SignatureValue> </Signature> 

CAUTION

The XMLDSIG example signatures presented in this section have been "pretty-printed" ( nicely formatted with line breaks and indentation) for easy reading by humans . Unfortunately, the Canonical XML algorithm does not properly handle pretty-printing, so if you want to verify any of these signatures yourself, you will need to remove all the line breaks and insignificant whitespace from the XML before verifying.


Detached signatures work well when the to-be-signed content can be easily addressed and retrieved by URI, but for signing small or dynamically generated XML fragments forcing the content to be Web addressable is overkill. For these scenarios, wrapped signatures work particularly well because the to-be-signed content can be easily embedded within the overall structure of a ds:Signature element. Creating a wrapped signature is very similar to creating a detached signature; the main difference is that the to-be-signed content must be added to the SignedXml object by wrapping the content within a DataObject , and the DataObject 's Id property must have a value that can be referenced from a Reference object. As a concrete example, consider the following XML element, which is a simple element with text content:

 <MyElement xmlns="">MyElement's Text Value</MyElement> 

This MyElement element contains a single child node holding the text content MyElement's Text Value . An XML element corresponding to this structure could be constructed using the classes in the System.Xml namespace as follows:

 XmlDocument doc = new XmlDocument(); XmlElement elem = doc.CreateElement("MyElement"); XmlNode child = (XmlNode) doc.CreateTextNode("MyElement's Text Value"); elem.AppendChild(child); 

Listing 32.23 contains source code for an exemplary CreateWrappedSignature subroutine that can be used to sign our XmlElement . CreateWrappedSignature requires two argumentsa signing key and an XmlElement containing the to-be-signed data. The method embeds and then generates a wrapped XMLDSIG signature around the provided XmlElement .

Listing 32.23 Compute a Wrapped Signature Using an AsymmetricAlgorithm
 static XmlElement CreateWrappedSignature (AsymmetricAlgorithm signingKey, XmlElement graphics/ccc.gif contentToSign) {     // Create the SignedXml message     SignedXml signedXml = new SignedXml();     signedXml.SigningKey = signingKey;     // Create a DataObject to encapsulate the to-be-signed content     // We use the string "ID1" as the internal reference ID for the object     // Second and third arguments are null because we don't wish to specify     // the optional MIME-Type and Encoding attributes.     DataObject dataObject = new DataObject("ID1", null, null, contentToSign);     // Add the new DataObject to the SignedXml object so it'll be included     // in the output signature     signedXml.AddObject(dataObject);     // Create a Reference to point to the to-be-signed content     Reference reference = new Reference();     // Use a URI fragment to point to the same Id we used in the DataObject     reference.Uri = "#ID1";     // Add the Reference to the SignedXml message     signedXml.AddReference(reference);     // Compute the XML Digital Signature     signedXml.ComputeSignature();     // Return the XmlElement containing the XMLDSIG signature     return signedXml.GetXml(); } 

As expected, the code in Listing 32.23 is similar to that for a detached signature (Listing 32.21); the only differences are in the creating and inclusion of a DataObject and the link between that DataObject and a Reference . After assigning the signing key, a DataObject is created to encapsulate the to-be-signed content. If the to-be-signed content consists of a single XmlElement , it can be specified by providing the element as an argument to the DataObject 's constructor, as shown here. Alternatively, if the to-be-signed content is a collection of independent XmlNodes held in an XmlNodeList , the embedded content can be added by using the DataObject 's Data property. In either case, a value for the DataObject 's Id attribute must be specified so that that ds:Object element can be referenced in a ds:SignedInfo element. In this example, the string "ID1" is used to identify the embedded object. After being created, the DataObject is added to the overall signature structure via the AddObject method on the SignedXml class. The AddObject method adds the DataObject to an internal list of DataObjects that must be included in the resulting XML represention of the signature. Next , a Reference object is created that points to the DataObject via a URI fragment. Because the DataObject 's Id attribute was assigned a value of "ID1" , the corresponding URI fragment value for the Reference 's Uri property is "#ID1" . The Reference object is then added as before, and signature calculation is performed by a call to ComputeSignature() .

NOTE

There are a few points to be aware of when constructing wrapped signatures in your programs. First, notice that the XMLDSIG classes will not automatically generate Id values for DataObjects and corresponding fragment URIs for References ; you must generate these values yourself and guarantee that there are no conflicts (multiple Id attributes with the same value) within the entire SignedXml object. If you are only including a single DataObject within your signature, this generally isn't a problem, but if you are combining data from multiple sources, you need to resolve any Id conflicts before computing signatures. This is especially true if you ever merge signed content from multiple documents; two signatures may be valid in independent document contexts but become invalid when combined into a single document because of Id conflicts.

Another important point is to remember to add DataObjects to your signatures using the AddObject method on the SignedXml class. The AddObject method not only adds a DataObject to the list of ds:Object elements that must be created at signature serialization time, but also adds the DataObject to the list of potential targets for fragment URIs found in References . When computing the DigestValue of a Reference that uses a fragment URI , the XMLDSIG classes know to look for corresponding embedded objects in a few places, including the list of DataObjects . If the DataObject is not added via AddObject , it will not be possible to resolve the fragment URI in the Reference and compute the corresponding hash value.


An example wrapped signature for our test element is shown in Listing 32.24. Notice how the output XML differs from that in Listing 32.22 only in the inclusion of the <Object>...</Object> element and the use of a URI fragment in the Reference to indicate that the element with Id attribute value "ID1" will be found in the same document as the document containing the <Signature>...</Signature> element.

Listing 32.24 Sample Wrapped XMLDSIG Signature Using RSA
 <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="#ID1">       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />       <DigestValue>zrMsnr3MPXc/fFzYXYqrQtHDTH4=</DigestValue>     </Reference>   </SignedInfo>   <SignatureValue>BNqo9l2cOjHMdgvcLz0y893JJlrO5zzPDeNIltUcExCjfhNoc8hH5Rs1Q       gKi4WC9iIfStzBPu3wi213nU6JNRD/oZ80VICal6OSKpZT39sMvCkcqV5OMcfhOjupZ5k       aTVDGVWrm1sPfDNgcEB+jTeeiTOysfLZHmbE8I4bJdZrY=   </SignatureValue>   <Object Id="ID1">     <MyElement xmlns="">MyElement's Text Value</MyElement>   </Object> </Signature> 

These two examples contain the minimum amount of information necessary to convey a digital signature from signer to verifier. In particular, there is no information or other indication in either example as to what key was actually used to sign the content. Thus, a party attempting to verify either of these signatures would have to have a priori knowledge of the verification key to use to validate the signature (or perhaps simply knowledge that one of a small set of keys was used). Generally, though, we cannot assume that a priori information exists between two communicating parties; we need to add information to the signature that will help verifiers find and use appropriate verification keys. In XMLDSIG terms, we need to add a ds:KeyInfo element to our signatures that contain one or more "hints" to help out verifiers receiving our signatures. The most basic form of the ds:KeyInfo element contains just a ds:KeyValue sub-element holding the public key that corresponds to the private signing key used to create the signature. In Listing 32.25, we have modified the wrapped signature code from Listing 32.23 to include a ds:KeyInfo element with an appropriate ds:KeyValue clause.

Listing 32.25 Compute a Wrapped Signature Including a KeyInfo Element
 static XmlElement CreateWrappedSignature (AsymmetricAlgorithm signingKey, XmlElement graphics/ccc.gif contentToSign) {     // Create the SignedXml message     SignedXml signedXml = new SignedXml();     signedXml.SigningKey = signingKey;     // Create a DataObject to encapsulate the to-be-signed content     // We use the string "ID1" as the internal reference ID for the object     // Second and third arguments are null because we don't wish to specify     // the optional MIME-Type and Encoding attributes.     DataObject dataObject = new DataObject("ID1", null, null, contentToSign);     // Add the new DataObject to the SignedXml object so it'll be included     // in the output signature     signedXml.AddObject(dataObject);     // Create a Reference to point to the to-be-signed content     Reference reference = new Reference();     // Use a URI fragment to point to the same Id we used in the DataObject     reference.Uri = "#ID1";     // Add the Reference to the SignedXml message     signedXml.AddReference(reference);     // Add a KeyInfo element to the SignedXml message     KeyInfo keyInfo = new KeyInfo();     if (signingKey is RSA) {         keyInfo.AddClause(new RSAKeyValue((RSA) signingKey));     }  else if (signingKey is DSA) {         keyInfo.AddClause(new DSAKeyValue((DSA) signingKey));     }     signedXml.KeyInfo = keyInfo;     // Compute the XML Digital Signature     signedXml.ComputeSignature();     // Return the XmlElement containing the XMLDSIG signature     return signedXml.GetXml(); } 

As you can see, adding KeyInfo -related information to an XMLDSIG signature is easy; all we had to do was create an empty KeyInfo object, add the KeyInfo clauses (sub-elements) we desired, and assign the object to the KeyInfo property on our SignedXml object. When the XML representation of the signature is created by the call to GetXml() at the end of the subroutine, the contents of the KeyInfo object are automatically serialized and added as a sub-element of the overall ds:Signature element. The .NET Framework natively understands both the ds:RSAKeyValue and ds:DSAKeyValue elements (via the RSAKeyValue and DSAKeyValue classes), so the seven lines of code we added to create Listing 32.25 suffice for all RSA and DSA keys. Listings 32.26 and 32.27 show two example wrapped signatures using RSA and DSA keys, respectively, that include corresponding KeyInfo elements with KeyValue sub-elements.

Listing 32.26 Sample Wrapped XMLDSIG Signatures Using RSA and a KeyInfo Element
 <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="#ID1">       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />       <DigestValue>zrMsnr3MPXc/fFzYXYqrQtHDTH4=</DigestValue>     </Reference>   </SignedInfo>   <SignatureValue>     DtFNTsE7o4IJkVxB2/xNsQvkftRg02WM2mH/nxqNtbaFhlxJ9hCbOjvakM87QxbIRdZpr7Lnm     N1H0IWTOxZiuYItLAyeXMLVwir9PHaPMFBp4QmSGrG983uZleGbnQ3oz536BGSYn4UVV7xhoY     bjV/+lxXha++W9XzRN1KKaN6w=   </SignatureValue>   <KeyInfo>     <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">       <RSAKeyValue>         <Modulus>           s0cVjgVj7rzRulP1148kHBaeEafbhmPrMbpkFT854HHu9tYp2DEP2f/VPxNtv6Hlk8g           dDXHey6w4x5GBHUotQWix7XJC9xwa3O8duCCUnO1fV08+PchAfTaP7rNTZWXqJLiecO           D0TPcvnxzK1NbQmYb9H4hY+XRu5yVFynoN8WM=         </Modulus>         <Exponent>AQAB</Exponent>       </RSAKeyValue>     </KeyValue>   </KeyInfo>   <Object Id="ID1">     <MyElement xmlns="">MyElement's Text Value</MyElement>   </Object> </Signature> 
Listing 32.27 Sample Wrapped XMLDSIG Signatures Using DSA
 <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="#ID1">       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />       <DigestValue>zrMsnr3MPXc/fFzYXYqrQtHDTH4=</DigestValue>     </Reference>   </SignedInfo>   <SignatureValue>     64YrMQdC2wOAd7X/HwbJpC/fVM4J+ce01eguwYSmro8H82/mAsPdJA==   </SignatureValue>   <KeyInfo>     <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">       <DSAKeyValue>         <P>           mJxJ7mx2sweRCvD0LJXraxE1oHPfh9A/qvdFoptX3NYIK7+Q6zsPKjmC+3xVA4F/nbx           pjzjcxe8NAU6kIcALlbb+Are9bGAn1T6iX1vSX5yuag2uY9eetUxPZ+zBag7St7WbxE           cJ2CnbFJy7MfKh1o1h5Lp44i9lY85nVAH1z6s=         </P>         <Q>7BSubUVUSrfwphIe28k1rCIaxbU=</Q>         <G>           S3AXH1iY2eKZc/J6qDmZZ9s8iPsiLFi4VuU8d4NaHkfvoVMMH2+0RQnpvLJisHBVNR5           Rc9lumKJAwO6+bEqJxqWS2i6b+wJC9rAnm7MQzjc0tTOB5i0Ta1jDZjfHixDzNhvqe7           mj93O6GiNkEIbRWNq2Gr3sKERqo4VidJjqCcc=         </G>         <Y>           b05XXi7Pom5IYmSCgEFyq6+p89nnC/YNFq75FIxZZf7poVbSw1SeAQa1fvylPv3jqJ9           ZQH/BKXCS/EIgcKai2RXVk82K5qpq1LyhmJRIZ7zXr38sL0NpPuGdAAyka30jlOQJf5           jhDAKfpB9PRmXL243ENn8eT4964d7yDGVHOFs=         </Y>         <J>           pXyonBRdcalnwbGtF6CY1PkLrvDxmtNJe4pYtYn2/4lsgVdrkvN/mKCJ0MbH+84889Z           g3P51eDLwelF0+5pY+E4EBxMPYnhvOJMIy3e20RmPVqQncfHZo6wO/oU9WI9LmrgC2I           4tEFXEWdtC         </J>         <Seed>cIUUPuDB7etAzIVGC3woIWnE/14=</Seed>         <PgenCounter>oQ==</PgenCounter>       </DSAKeyValue>     </KeyValue>   </KeyInfo>   <Object Id="ID1">     <MyElement xmlns="">MyElement's Text Value</MyElement>   </Object> </Signature> 

NOTE

Notice that the structure of the two examples differ not only in KeyInfo components but also in the size of the SignatureValue . This is due to the way the RSA and DSA signing algorithms operate . The output of the RSA signature function is comparable in size to the size of the RSA modulus (1,024 bits in these examples, or 128 bytes). A DSA signature, in contrast, always consists of two 20-byte values; the SignatureValue for DSA is simply the Base64 encoding of a 40-byte array formed by concatenating the two signature values.


Multiple types of KeyInfo sub-elements can be added to a signature by simply calling the AddClause method repeatedly for each additional sub-element. The KeyInfoClause abstract class is the parent class for RSAKeyValue , DSAKeyValue , and other KeyInfo sub-elements, and any number of KeyInfoClause elements can be added to a single KeyInfo object. For example, we can easily change the six lines of code in Listing 32.25 that create the KeyInfo object to also add a KeyName sub-element. Listing 32.28 shows the resulting code that adds a KeyName sub-element that says the key belongs to Alice. A sample RSA signature generated with this subroutine is shown in Listing 32.29.

Listing 32.28 Using a KeyInfo Element with Multiple Sub-Elements
 static XmlElement CreateWrappedSignature  (AsymmetricAlgorithm signingKey, XmlElement graphics/ccc.gif contentToSign) {     // Create the SignedXml message     SignedXml signedXml = new SignedXml();     signedXml.SigningKey = signingKey;     // Create a DataObject to encapsulate the to-be-signed content     // We use the string "ID1" as the internal reference ID for the object     // Second and third arguments are null because we don't wish to specify     // the optional MIME-Type and Encoding attributes.     DataObject dataObject = new DataObject("ID1", null, null, contentToSign);     // Add the new DataObject to the SignedXml object so it'll be included     // in the output signature     signedXml.AddObject(dataObject);     // Create a Reference to point to the to-be-signed content     Reference reference = new Reference();     // Use a URI fragment to point to the same Id we used in the DataObject     reference.Uri = "#ID1";     // Add the Reference to the SignedXml message     signedXml.AddReference(reference);     // Create a KeyInfo element to hold key-related information     KeyInfo keyInfo = new KeyInfo();     // Add an appropriate KeyValue sub-element     if (signingKey is RSA) {         keyInfo.AddClause(new RSAKeyValue((RSA) signingKey));     }  else if (signingKey is DSA) {         keyInfo.AddClause(new DSAKeyValue((DSA) signingKey));     }     // Create a KeyInfoName (representing a KeyName sub-element)     KeyInfoName keyName = new KeyInfoName();     // Set the text value for the KeyName element     keyName.Value = "Alice's Signing Key";     // Add the KeyInfoName object to the KeyInfo     keyInfo.AddClause(keyName);     // Assign the KeyInfo object     signedXml.KeyInfo = keyInfo;     // Compute the XML Digital Signature     signedXml.ComputeSignature();     // Return the XmlElement containing the XMLDSIG signature     return signedXml.GetXml(); } 
Listing 32.29 Sample Wrapped XMLDSIG Signatures Using RSA
 <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="#ID1">       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />       <DigestValue>zrMsnr3MPXc/fFzYXYqrQtHDTH4=</DigestValue>     </Reference>   </SignedInfo>   <SignatureValue>     tgSS9Zjd+/fLMx5CEKvzxPcMwWMvH1Jld331Ci3zmVfO6l2DXI5lYuiehBUtvuBKaRaqB+Y     8bAb9zKkyqaT0Yc9Fj6ftiFnrqj0pXCjc8x/1X/5OvRzVcIANfgjHOzFnLNmA0+4t7eBdIi     Ei9CQ0ktCXJ1kS0SE5ZyjwFEdNzho=   </SignatureValue>   <KeyInfo>     <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">       <RSAKeyValue>         <Modulus>           tmsw2NKSFGgeJ2JBWM4I3OgFA7Q0O7A0aYugtdGWf60smOGNWegocQjQEzy1qmXM3           UPxMTQZV4F5O28VLmTKtDn3F1QUa42XP0Oi7Rnvm0UApiNW39stauhg9/XwBH/myw           I4SGiU1HjN8voYJe6xIZfUGanRXORIjIYMCGXpkgE=         </Modulus>         <Exponent>AQAB</Exponent>       </RSAKeyValue>     </KeyValue>     <KeyName>Alice's Signing Key</KeyName>   </KeyInfo>   <Object Id="ID1">     <MyElement xmlns="">MyElement's Text Value</MyElement>   </Object> </Signature> 

CAUTION

When constructing ds:KeyInfo elements, you must make sure that you abide by XMLDSIG restrictions concerning allowed combinations of sub-elements. In particular, when creating X509Data sub-elements, you must adhere to the restrictions detailed at the end of the last section, such as the requirement that all instances ds:X509IssuerSerial , ds:X509SKI , and ds:X509SubjetName that refer to the same single certificate be grouped together within a single ds:X509Data element. These semantic restrictions cannot be captured within the XMLDSIG schema, and the .NET Framework classes will allow you to construct KeyInfo elements that do not adhere to them.


So far, all of our examples have used simple references to identify to-be-signed contentcontent has always been completely identified by a full URI or fragment, same-document URI. In the next two examples, we show how to use the full power of the Reference Processing Model to sign just a portion of an XML document. We will add Transform objects to Reference objects to create chains of transformations to apply to the input content identified by a URI. As a result, we will be able to selectively sign any desired portion of the input content.

Creating Reference objects that include a chain of transformations is easy. All we need to do is create the Transform objects that represent each stage of the transformation chain and add them, in order, to the Reference object by calling that object's AddTransform method. Listing 32.30 shows a sample detached signature that includes a Base64 transformation in its Reference element; the content signed by this signature is not the content directly obtained from the URI http://www.farcaster.com/index.b64 , but rather is the result of Base64-decoding that content. Listing 32.31 contains sample code for the method that created the exemplary signature; notice that the only difference between Listing 32.31 and Listing 32.21 is the explicit addition of an XmlDsigBase64Transform object to the Reference .

Listing 32.30 Sample Detached XMLDSIG Signature with a Base64 Transform
 <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="http://www.farcaster.com/index.b64">       <Transforms>         <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64" />       </Transforms>       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />       <DigestValue>XoaHIm+jLKnPocR7FX0678DUOqs=</DigestValue>     </Reference>   </SignedInfo>   <SignatureValue>     M9tyhbB05xUY0oB+EhFRt2IxdrY15mKdbKJphYA5RGSrU/7/oqEPWo0q02uig5lp9CBHeb5     iIT6JOlLNaUgmUTF5gIcnB9NT62EWVBAIlr2N5o4NCpY/Q44kYH1Oq7wgYkmCvJpYDumpjJ     DFao4cj7vDi/YouS0iDW9B/EcTKzk=   </SignatureValue>   <KeyInfo>     <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">       <RSAKeyValue>         <Modulus>           othk0XJTRpeIsD4u4qGAcVebbAe9NCNCsTMBCcZhoniWm4AJeWIWAHgL0xCxoK5df           iQhTk8YWkWt+lJcTmKUdx0/duDMpGLt6o7anfVs8xmeX3o5zwGg5EEZs8FKED8pvc           bWsTorVGUpG2HgpqhNzbsFfrsgZsC10CGNe0G7qKE=         </Modulus>         <Exponent>AQAB</Exponent>       </RSAKeyValue>     </KeyValue>   </KeyInfo> </Signature> 
Listing 32.31 Creating a Detached Signature That Uses a Base64 Transform
 static XmlElement CreateDetachedSignature (AsymmetricAlgorithm signingKey, String uri) {     // Create the SignedXml message     SignedXml signedXml = new SignedXml();     signedXml.SigningKey = signingKey;     // Create a Reference to point to the to-be-signed content     Reference reference = new Reference(uri);     // Create a Base64 transform: the input content retrieved from the     // uri should be Base64-decoded before hashing     Transform base64 = new XmlDsigBase64Transform();     // Add the transform to the Reference object     reference.AddTransform(base64);     // Add the Reference to the SignedXml message     signedXml.AddReference(reference);     // Create a KeyInfo element to hold key-related information     KeyInfo keyInfo = new KeyInfo();     // Add an appropriate KeyValue sub-element     if (signingKey is RSA) {         keyInfo.AddClause(new RSAKeyValue((RSA) signingKey));     }  else if (signingKey is DSA) {         keyInfo.AddClause(new DSAKeyValue((DSA) signingKey));     }     // Assign the KeyInfo object     signedXml.KeyInfo = keyInfo;     // Compute the XML Digital Signature     signedXml.ComputeSignature();     // Return the XmlElement containing the XMLDSIG signature     return signedXml.GetXml(); } 

Compare the Reference element in Listing 32.30 to that in Listing 32.22. The two References are obviously different in that the one in Listing 32.22 specifies the direct hash of content obtained from a URI, whereas the one in Listing 32.30 specifies the hash of Base64-decoded content. Notice, however, that the DigestValues in the two elements are identical! They are equivalent because the content stored at http://www.farcaster.com/index.b64 is the Base64 encoding of the content stored at http://www.farcaster.com/index.htm , so the to-be-signed content in Listing 32.30, the Base64-decoded content, is identical to the to-be-signed content in Listing 32.22. Identical to-be-signed content yields identical hashes (using the same hash algorithm), which is why the DigestValues are equal. Of course, the hash values of the canonicalized SignedInfo elements are not equal (they are structurally different) and so even if both signatures were created with the same signing key, the signature values would be different.

Perhaps the most powerful feature of Reference transformations is their ability to select for signing a subset of the input content. In particular, the XPath transformation allows a signer to precisely select a subset of input XML content and sign just that subset. Consider the XML fragment shown in Listing 32.32. This fragment represents a simple element, MyElement , with two child elements, MyFirstChild and MySecondChild . Each child element further contains some text content.

Listing 32.32 Sample XML Fragment for XPath Transform Example
 <MyElement xmlns="">   <MyFirstChild>First Child's Text</MyFirstChild>   <MySecondChild>Second Child's Text</MySecondChild> </MyElement> 

We saw in Listings 32.21 and 32.25 how we could create a wrapped signature for an entire XML object like this one. Suppose, however, that instead we want to sign only the content of the MyFirstChild sub-element; we want to commit to exactly the XML structure

 <MyFirstChild>First Child's Text</MyFirstChild> 

while still sending the entire content of the MyElement element. We can select just the MyFirstChild element (and all its descendants) by using the XPath expression:

 ancestor-or-self::MyFirstChild 

This XPath expression matches every node in an XML document that either a MyFirstChild element or has a MyFirstChild element as an ancestor node. For the fragment shown in Listing 32.32, the XPath expression ancestor-or-self::MyFirstChild will match exactly the content we want to sign. Thus, by modifying the sample code in Listing 32.25 to include an XPath transformation of ancestor-or-self::MyFirstChild , we can create a wrapped signature around the entire MyElement with a signature that only protects the MyFirstChild element.

The .NET Framework include a class, XmlDsigXPathTransform , that represents an XPath transformation in the Reference Processing Model. Unfortunately, the Framework neglected to include a simple constructor for building XmlDsigXpathTransform objects from a String XPath expression; the only way to set the internal state of an XmlDsigXPathTransform is to construct the object from the internal XML state of a ds:Transform node. Listing 32.33 contains the source code for a simple utility class that creates an XmlDsigXPathTransform from a String argument containing a valid XPath expression; you'll want to cut-and-paste this utility class into your own programs if you ever need to construct XPath transformations.

Listing 32.33 Source Code for the CreateXPathTransformFromString Method
 // This utility function creates an XmlDsigXPathTransform // from an XPath string like "self::text()". It's necessary // because XmlDsigXPathTransform doesn't have a constructor that // takes a String argument, so we have to build it by importing // an appropriate XmlNodeList! static Transform CreateXPathTransformFromString(String xpathString) {     XmlDsigXPathTransform xpathTransform = new XmlDsigXPathTransform();     // We need a document to create nodes     XmlDocument document = new XmlDocument();     document.LoadXml("<XPath xmlns:dsig=\ "http://www.w3.org/2000/09/xmldsig#\">" graphics/ccc.gif +xpathString+"</XPath>");     xpathTransform.LoadInnerXml(document.ChildNodes);     return xpathTransform; } 

With the CreateXPathTransformFromString method in our toolbox of useful functions, we can now easily construct a Reference with an embedded Transform that selects just the content we want to sign. Because we know that the XPath expression for selecting the to-be-signed content is ancestor-or-self::MyFirstChild , the only change we need to make to the code in Listing 32.25 is to change the construction of the Reference object from

 // Create a Reference to point to the to-be-signed content Reference reference = new Reference(); // Use a URI fragment to point to the same Id we used in the DataObject reference.Uri = "#ID1"; // Add the Reference to the SignedXml message signedXml.AddReference(reference); 

to

 // Create a Reference to point to the to-be-signed content Reference reference = new Reference(); // Use a URI fragment to point to the same Id we used in the DataObject reference.Uri = "#ID1"; // Create an XPath transform for the portion of the content we wish to sign Transform xpath = CreateXPathTransformFromString("ancestor-or-self::MyFirstChild"); // Add the transform to the Reference object reference.AddTransform(xpath); // Add the Reference to the SignedXml message signedXml.AddReference(reference); 

The resulting signature, shown in Listing 32.34, contains the entire MyElement object embedded within an Object element. The Reference points to the entire Object via a fragment URI ( #ID1 ), and the XPath transformation selects the to-be-signed content. For reference, Table 32.9 contains a complete listing of all XMLDSIG transforms included in the .NET Framwork.

Listing 32.34 Sample Wrapped Signature Using an XPath Transform
 <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="#ID1">       <Transforms>         <Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">           <XPath>             ancestor-or-self::MyFirstChild           </XPath>         </Transform>       </Transforms>       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />       <DigestValue>Hvipq88vrQCK4zYZsnpncslOiog=</DigestValue>     </Reference>   </SignedInfo>   <SignatureValue>     nBATUMs7Fev2kyiPD5k5/JgcQ4Fbk7SPoHrzctETw6Hgw0STAWFhZ+jvUXsFzcjU6zCHQug     GEuDHh7bWIdknkmJjwnXQOU3UozBURRVu/nBJZo3UAWJAEC0FHO7FpkquGTxFHb/Nw3DrBx     HtJXPDxijyjNM9vYVDwnBsfBrFtTk=   </SignatureValue>   <KeyInfo>     <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">       <RSAKeyValue>         <Modulus>           txbfRFQ9fCxZWk4sbFZ1kSbpFt4RedOd6PS2+jOF44J5CX+DuY1mzhTY5HOW63fW7           8OoGLwrITDcpXGD3mAmHqc1BzbO0Coulvu8o6G9sYthFkYFnfRTuwOM67C42eUlTU           6E1ZjyOOxr3ONh+iu+zZiy7ff83svzstfwtR92rqE=         </Modulus>         <Exponent>AQAB</Exponent>       </RSAKeyValue>     </KeyValue>   </KeyInfo>   <Object Id="ID1">     <MyElement xmlns="">       <MyFirstChild>First Child's Text</MyFirstChild>       <MySecondChild>Second Child's Text</MySecondChild>     </MyElement>   </Object> </Signature> 
Table 32.9. Transform Classes and Algorithms Included in the .NET Framework
Class Description
XmlDsigBase64Transform The Base64 decoding transformation
XmlDsigC14NTransform The Canonical XML canonicalization transform (comment nodes are excluded from the output in this algorithm)
XmlDsigC14NWithCommentsTransform The Canonical XML canonicalization transform (comment nodes are preserved in the output in this algorithm)
XmlDsigEnvelopedSignatureTransform The enveloped signature transform
XmlDsigXPathTransform The XPath transform
XmlDsigXsltTransform The XSLT transform

NOTE

Readers familiar with the XPath standard may wonder why we chose to use the XPath expression ancestor-or-self::MyFirstChild to select the to-be-signed content rather than a more direct path selection from the document root, such as //MyFirstChild or /MyElement/MyFirstChild . The answer has to do with the way the XPath transform is defined in Section 6.6.3, "XPath Filtering," of the XMLDSIG specification. XPath transforms in XMLDSIG are always treated filters on sets of XML nodes. An XML node is included in the output of an XPath transform if the result of the XPath expression, when evaluated in the context of the node and converted to a Boolean, is true . For XPath expressions that return node sets like //MyFirstChild , the Boolean result is true if the returned node set is non-empty. In practice, what this means is that an XML node is included in the output of the XPath transform if the XPath expression matches the content of the node or any children recursively of the node. So, if we had used the XPath expression //MyFirstChild in the previous example, we would have ended up signing more than just the MyFirstChild sub-element.


Our final example in this section demonstrates how to construct an enveloped signature, which is also sometimes called an embedded signature. An enveloped signature is an XMLDSIG ds:Signature that, after construction, is embedded within the signed content. A common use of embedded signatures is to sign documents where the outer XML structure of the document must be preserved; the signature is added to the document but does not obscure the overall structure of the document. This is the reverse situation from wrapped signatures where the ds:Signature element was the "outer" XML element, and the signed content was embedded within the signature elements. Now we want to perform the operation the other way, wrapping the signed content around the signature data. As a concrete exemplary scenario, we will embed an XMLDSIG ds:Signature element for the content of the MyElement of Listing 32.32 inside the element itself as a third child element of the root node.

Creating an enveloped signature differs from other signatures in three ways. First, we must specify the XML document into which the signature will be embedded at the time we create the SignedXml object. The SignedXml object needs to know the XML context in which the signature will exist so that XML canonicalization can be carried out properly. In particular, part of the Canonical XML specification requires that XML namespace nodes be propagated from parent nodes into child nodes automatically, so we must declare the node that will be the parent of the ds:Signature node before we can calculate the canonical form of the ds:SignedInfo element. The eventual parent node of the to-be-generated ds:Signature is declared in an argument to the SignedXml constructor using one of the following two constructor overloads:

 public SignedXml(XmlElement); public SignedXml(XmlDocument); 

In the first constructor overload, the XmlElement argument is the parent element of the ds:Signature element about to be computed; the signature will be valid only when made a child of the specified XmlElement . The second constructor overload can be used when the ds:Signature is going to be a child of the top-level document element of an XmlDocument .

The other two differences between enveloped signatures and other forms of XMLDSIG signatures have to do with the construction of References . Enclosing content (that is, content that includes the Signature element) is indicated by creating a Reference object as using the empty string "" as the value of the Reference 's URI property. The URI "" has special meaning in the XMLDSIG standard; it identifies the XML resource containing the Reference (with comment nodes removed from the XML). So, to reference the enclosing document, we construct the Reference object as follows:

 // Create a Reference to point to the to-be-signed content Reference reference = new Reference(); // Use an empty-string URI to indicate that the signed content is // the document containing the Signature element reference.Uri = ""; 

In addition to specifying an empty-string URI, we also need to add a Transform to the Reference that will exclude the contents of the ds:Signature element from the other content in the document. XMLDSIG defines a special-purpose transform for this particular purpose, the "enveloped signature transform" (see Section 6.6.4, "Enveloped Signature Transform" of the XMLDSIG specification, "XML-Signature Syntax and Processing"). In the .NET Framework, the XmlDsigEnvelopedSignatureTransform class represents this transform and when constructing enveloped signatures, we need to make an instance of this transform be the first member of the Reference 's transform chain:

 // Create an enveloped signature transform to reference the entire document // minus the Signature we're about to embed XmlDsigEnvelopedSignatureTransform envSigTransform = new graphics/ccc.gif XmlDsigEnvelopedSignatureTransform(); // Add the transform to the Reference object reference.AddTransform(envSigTransform); 

Now we can perform any other transformations we want on the to-be-signed content, and signature verifiers will automatically ignore the information in the Signature element. Listing 32.35 contains a complete sample program for generating an enveloped signature around the MyElement sample text (using a randomly generated RSA key), and the resulting signature (pretty-printed) is shown in Listing 32.36.

Listing 32.35 Creating an Enveloped Signature for the MyElement XML Element
 using System; using System.Security.Cryptography; using System.Security.Cryptography.Xml; using System.IO; using System.Text; using System.Xml; // Enveloped signature example public class RSAEnvelopedSignature {     static XmlElement CreateEnvelopedSignature  (AsymmetricAlgorithm signingKey, graphics/ccc.gif XmlElement signatureParent) {         // Create the SignedXml message         SignedXml signedXml = new SignedXml(signatureParent);         signedXml.SigningKey = signingKey;         // Create a Reference to point to the to-be-signed content         Reference reference = new Reference();         // Use an empty-string URI to indicate that the signed content         // is the document containing the Signature element         reference.Uri = "";         // Create an enveloped signature transform to reference the entire         // document minus the Signature we're about to embed         XmlDsigEnvelopedSignatureTransform envSigTransform = new graphics/ccc.gif XmlDsigEnvelopedSignatureTransform();         // Add the transform to the Reference object         reference.AddTransform(envSigTransform);         // Add the Reference to the SignedXml message         signedXml.AddReference(reference);         // Create a KeyInfo element to hold key-related information         KeyInfo keyInfo = new KeyInfo();         // Add an appropriate KeyValue sub-element         if (signingKey is RSA) {             keyInfo.AddClause(new RSAKeyValue((RSA) signingKey));         }  else if (signingKey is DSA) {             keyInfo.AddClause(new DSAKeyValue((DSA) signingKey));         }         // Assign the KeyInfo object         signedXml.KeyInfo = keyInfo;         // Compute the XML Digital Signature         signedXml.ComputeSignature();         // Return the XmlElement containing the XMLDSIG signature         return signedXml.GetXml();     }     public static void Main()     {         // Create a random RSA key         RSACryptoServiceProvider rsa;         rsa = new RSACryptoServiceProvider();         // Create a new XmlDocument with the to-be-signed content         XmlDocument doc = new XmlDocument();         doc.LoadXml("<MyElement xmlns=\"\"><MyFirstChild>First Child's Text </ graphics/ccc.gif MyFirstChild><MySecondChild>Second Child's Text</MySecondChild> </MyElement>");         // Get the root document element         XmlElement elem = (XmlElement) doc.DocumentElement;         // Create an enveloped signature using the root document element as         // the parent of the signature         XmlElement xmlDigitalSignature = CreateEnvelopedSignature(rsa, elem);         // Add the computed Signature element to the root document element         elem.AppendChild(xmlDigitalSignature);         // Create an XmlTextWriter to write out the XML content to the Console         XmlTextWriter xmltw;         xmltw = new XmlTextWriter(Console.Out);         // Write out the document         doc.WriteTo(xmltw);         // Close the XmlTextWriter         xmltw.Close();     } } 
Listing 32.36 Sample Enveloped Signature
 <MyElement xmlns="">   <MyFirstChild>First Child's Text</MyFirstChild>   <MySecondChild>Second Child's Text</MySecondChild>   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">     <SignedInfo>       <CanonicalizationMethod Algorithm= "http://www.w3.org/TR/2001/ graphics/ccc.gif 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>rKs5Gxt3IxfjGnT6yGPK9RrGeac=</DigestValue>       </Reference>     </SignedInfo>     <SignatureValue>       YyyQHXVsO1YPmfK9fBGoHHTMoq+5q9O8IoGD7XE/fArldgKfdVQ9z2EWQZBr6fzUhI4Yo       LY1XHXNHIDKCNI72gcPr34BocVh/YQ5yF3pZRK3aeD32cRPcDmSfL569Yd02QgXvL67UQ       dWIuJEABeXo9QvLyPiYZ6j+XERjB/MqZ0=     </SignatureValue>     <KeyInfo>       <KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">         <RSAKeyValue>           <Modulus>             vUIbOs+9MVk/h5YdhruZzMM+aQ3T8mHKz1EkHWadJFofUC06/68Hu6YhrLIO7s2             kDLws5ZckHW0yr1FSYWvf0xc6a+DsoEin+S9TvFMQ+MahoKs4/ckQxlpi6+u/2n             yE2RpDmt5PX1eQQDK1z7hqhDMjnGtFupC8elCC5Q9gX6s=           </Modulus>           <Exponent>AQAB</Exponent>         </RSAKeyValue>       </KeyValue>     </KeyInfo>   </Signature> </MyElement> 

One closing note before concluding our section on creating XMLDSIG signatures: All of the examples in this section have used AsymmetricAlgorithm objects (RSA or DSA keys) to generate digital signatures. XMLDSIG also allows two communicating parties to authenticate information using a message authentication code (MAC); "signing" with a MAC consists simply of computing the MAC over the canonicalized form of the ds:SignedInfo using the shared secret. You can create these types of XMLDSIG signatures using the .NET Framework by calling the special overloaded form of SignedXml.ComputeSignature that takes a single KeyedHashAlgorithm input argument. Only the HMAC-SHA1 algorithm is supported for use with XMLDSIG in the first release of the .NET Framework.

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