XML Digital Signature API Examples

The following sections describe two examples that show how to use the XML Digital Signature API:

To run the sample applications using the supplied Ant build.xml files, issue the following commands after you installed Java WSDP:

For Solaris/Linux:

  1. % export JWSDP_HOME=<your Java WSDP installation directory>
  2. % export ANT_HOME=$JWSDP_HOME/apache-ant
  3. % export PATH=$ANT_HOME/bin:$PATH
  4. % cd $JWSDP_HOME/xmldsig/samples/<sample-name>

For Windows 2000/XP:

  1. > set JWSDP_HOME=<your Java WSDP installation directory>
  2. > set ANT_HOME=%JWSDP_HOME%\apache-ant
  3. > set PATH=%ANT_HOME%\bin;%PATH%
  4. > cd %JWSDP_HOME%\xmldsig\samples\<sample-name>

validate Example

You can find the code shown in this section in the Validate.java file in the <JWSDP_HOME>/xmldsig/samples/validate directory. The file on which it operates, envelopedSignature.xml, is in the same directory.

To run the example, execute the following command from the <JWSDP_HOME>/xmldsig/samples/validate directory:

$ ant 

The sample program will validate the signature in the file envelopedSignature.xml in the current working directory. To validate a different signature, run the following command:

$ ant -Dsample.args="signature.xml"  

where "signature.xml" is the pathname of the file.

Validating an XML Signature

This example shows you how to validate an XML Signature using the JSR 105 API. The example uses DOM (the Document Object Model) to parse an XML document containing a Signature element and a JSR 105 DOM implementation to validate the signature.

Instantiating the Document that Contains the Signature

First we use a JAXP DocumentBuilderFactory to parse the XML document containing the Signature. An application obtains the default implementation for DocumentBuilderFactory by calling the following line of code:

DocumentBuilderFactory dbf = 
  DocumentBuilderFactory.newInstance(); 

We must also make the factory namespace-aware:

dbf.setNamespaceAware(true); 

Next, we use the factory to get an instance of a DocumentBuilder, which is used to parse the document:

DocumentBuilder builder = dbf.newDocumentBuilder();  
Document doc = builder.parse(new FileInputStream(argv[0])); 

Specifying the Signature Element to be Validated

We need to specify the Signature element that we want to validate, since there could be more than one in the document. We use the DOM method Document.getElementsByTagNameNS, passing it the XML Signature namespace URI and the tag name of the Signature element, as shown:

NodeList nl = doc.getElementsByTagNameNS
  (XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
  throw new Exception("Cannot find Signature element");
} 

This returns a list of all Signature elements in the document. In this example, there is only one Signature element.

Creating a Validation Context

We create an XMLValidateContext instance containing input parameters for validating the signature. Since we are using DOM, we instantiate a DOMValidateContext instance (a subclass of XMLValidateContext), and pass it two parameters, a KeyValueKeySelector object and a reference to the Signature element to be validated (which is the first entry of the NodeList we generated earlier):

DOMValidateContext valContext = new DOMValidateContext
  (new KeyValueKeySelector(), nl.item(0)); 

The KeyValueKeySelector is explained in greater detail in Using KeySelectors.

Unmarshaling the XML Signature

We extract the contents of the Signature element into an XMLSignature object. This process is called unmarshalling. The Signature element is unmarshalled using an XMLSignatureFactory object. An application can obtain a DOM implementation of XMLSignatureFactory by calling the following line of code:

XMLSignatureFactory factory = 
  XMLSignatureFactory.getInstance("DOM"); 

We then invoke the unmarshalXMLSignature method of the factory to unmarshal an XMLSignature object, and pass it the validation context we created earlier:

XMLSignature signature = 
  factory.unmarshalXMLSignature(valContext); 

Validating the XML Signature

Now we are ready to validate the signature. We do this by invoking the validate method on the XMLSignature object, and pass it the validation context as follows:

boolean coreValidity = signature.validate(valContext); 

The validate method returns "true" if the signature validates successfully according to the core validation rules in the W3C XML Signature Recommendation, and false otherwise.

What If the XML Signature Fails to Validate?

If the XMLSignature.validate method returns false, we can try to narrow down the cause of the failure. There are two phases in core XML Signature validation:

Each phase must be successful for the signature to be valid. To check if the signature failed to cryptographically validate, we can check the status, as follows:

boolean sv = 
  signature.getSignatureValue().validate(valContext);
System.out.println("signature validation status: " + sv); 

We can also iterate over the references and check the validation status of each one, as follows:

Iterator i =
  signature.getSignedInfo().getReferences().iterator();
for (int j=0; i.hasNext(); j++) {
  boolean refValid = ((Reference) 
    i.next()).validate(valContext);
  System.out.println("ref["+j+"] validity status: " + 
    refValid);
} 

Using KeySelectors

KeySelectors are used to find and select keys that are needed to validate an XMLSignature. Earlier, when we created a DOMValidateContext object, we passed a KeySelector object as the first argument:

DOMValidateContext valContext = new DOMValidateContext
  (new KeyValueKeySelector(), nl.item(0)); 

Alternatively, we could have passed a PublicKey as the first argument if we already knew what key is needed to validate the signature. However, we often don't know.

The KeyValueKeySelector is a concrete implementation of the abstract KeySelector class. The KeyValueKeySelector implementation tries to find an appropriate validation key using the data contained in KeyValue elements of the KeyInfo element of an XMLSignature. It does not determine if the key is trusted. This is a very simple KeySelector implementation, designed for illustration rather than real-world usage. A more practical example of a KeySelector is one that searches a KeyStore for trusted keys that match X509Data information (for example, X509SubjectName, X509IssuerSerial, X509SKI, or X509Certificate elements) contained in a KeyInfo.

The implementation of the KeyValueKeySelector is as follows:

private static class KeyValueKeySelector extends KeySelector {

  public KeySelectorResult select(KeyInfo keyInfo,
      KeySelector.Purpose purpose,
      AlgorithmMethod method,
      XMLCryptoContext context)
    throws KeySelectorException {

    if (keyInfo == null) {
      throw new KeySelectorException("Null KeyInfo object!");
    }
    SignatureMethod sm = (SignatureMethod) method;
    List list = keyInfo.getContent();

    for (int i = 0; i < list.size(); i++) {
      XMLStructure xmlStructure = (XMLStructure) list.get(i);
      if (xmlStructure instanceof KeyValue) {
        PublicKey pk = null;
        try {
          pk = ((KeyValue)xmlStructure).getPublicKey();
        } catch (KeyException ke) {
          throw new KeySelectorException(ke);
        }
        // make sure algorithm is compatible with method
        if (algEquals(sm.getAlgorithm(), 
            pk.getAlgorithm())) {
          return new SimpleKeySelectorResult(pk);
        }
      }
    }
    throw new KeySelectorException("No KeyValue element 
found!");
  }

  static boolean algEquals(String algURI, String algName) {
    if (algName.equalsIgnoreCase("DSA") &&
        algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
      return true;
    } else if (algName.equalsIgnoreCase("RSA") &&
        algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
      return true;
    } else {
      return false;
    }
  }
} 

genenveloped Example

The code discussed in this section is in the GenEnveloped.java file in the <JWSDP_HOME>/xmldsig/samples/genenveloped directory. The file on which it operates, envelope.xml, is in the same directory. It generates the file envelopedSignature.xml.

To compile and run this sample, execute the following command from the <JWSDP_HOME>/xmldsig/samples/genenveloped directory:

$ ant  

The sample program will generate an enveloped signature of the document in the file envelope.xml and store it in the file envelopedSignature.xml in the current working directory.

Generating an XML Signature

This example shows you how to generate an XML Signature using the XML Digital Signature API. More specifically, the example generates an enveloped XML Signature of an XML document. An enveloped signature is a signature that is contained inside the content that it is signing. The example uses DOM (the Document Object Model) to parse the XML document to be signed and a JSR 105 DOM implementation to generate the resulting signature.

A basic knowledge of XML Signatures and their different components is helpful for understanding this section. See http://www.w3.org/TR/xmldsig-core/ for more information.

Instantiating the Document to be Signed

First, we use a JAXP DocumentBuilderFactory to parse the XML document that we want to sign. An application obtains the default implementation for DocumentBuilderFactory by calling the following line of code:

DocumentBuilderFactory dbf =
  DocumentBuilderFactory.newInstance(); 

We must also make the factory namespace-aware:

dbf.setNamespaceAware(true); 

Next, we use the factory to get an instance of a DocumentBuilder, which is used to parse the document:

DocumentBuilder builder = dbf.newDocumentBuilder();  
Document doc = builder.parse(new FileInputStream(argv[0])); 

Creating a Public Key Pair

We generate a public key pair. Later in the example, we will use the private key to generate the signature. We create the key pair with a KeyPairGenerator. In this example, we will create a DSA KeyPair with a length of 512 bytes :

KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(512);
KeyPair kp = kpg.generateKeyPair(); 

In practice, the private key is usually previously generated and stored in a KeyStore file with an associated public key certificate.

Creating a Signing Context

We create an XML Digital Signature XMLSignContext containing input parameters for generating the signature. Since we are using DOM, we instantiate a DOMSignContext (a subclass of XMLSignContext), and pass it two parameters, the private key that will be used to sign the document and the root of the document to be signed:

DOMSignContext dsc = new DOMSignContext
  (kp.getPrivate(), doc.getDocumentElement()); 

Assembling the XML Signature

We assemble the different parts of the Signature element into an XMLSignature object. These objects are all created and assembled using an XMLSignatureFactory object. An application obtains a DOM implementation of XMLSignatureFactory by calling the following line of code:

XMLSignatureFactory fac = 
  XMLSignatureFactory.getInstance("DOM"); 

We then invoke various factory methods to create the different parts of the XMLSignature object as shown below. We create a Reference object, passing to it the following:

Next, we create the SignedInfo object, which is the object that is actually signed, as shown below. When creating the SignedInfo, we pass as parameters:

Next, we create the optional KeyInfo object, which contains information that enables the recipient to find the key needed to validate the signature. In this example, we add a KeyValue object containing the public key. To create KeyInfo and its various subtypes, we use a KeyInfoFactory object, which can be obtained by invoking the getKeyInfoFactory method of the XMLSignatureFactory, as follows:

KeyInfoFactory kif = fac.getKeyInfoFactory(); 

We then use the KeyInfoFactory to create the KeyValue object and add it to a KeyInfo object:

KeyValue kv = kif.newKeyValue(kp.getPublic());
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv)); 

Finally, we create the XMLSignature object, passing as parameters the SignedInfo and KeyInfo objects that we created earlier:

XMLSignature signature = fac.newXMLSignature(si, ki); 

Notice that we haven't actually generated the signature yet; we'll do that in the next step.

Generating the XML Signature

Now we are ready to generate the signature, which we do by invoking the sign method on the XMLSignature object, and pass it the signing context as follows:

signature.sign(dsc); 

The resulting document now contains a signature, which has been inserted as the last child element of the root element.

Printing or Displaying the Resulting Document

You can use the following code to print the resulting signed document to a file or standard output:

OutputStream os;
if (args.length > 1) {
  os = new FileOutputStream(args[1]);
} else {
  os = System.out;
} 
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));