Yes, you can secure your Web services documents, Part 2

XML Signature ensures your XML documents' integrity

Security is surely one of the most talked-about subjects this year. Many books about writing secure code have surfaced over the past several months (for Windows and Unix too!). Early in the year, Microsoft announced its Trustworthy Computing initiative. In addition, the company's partnership with IBM and Verisign has resulted in WS-Security (Web Services Security), a platform- and language-neutral security standard for Web services.

WS-Security enhances SOAP (Simple Object Access Protocol) messaging to provide confidentiality and integrity, which form the basis for higher-level security aspects, such as trust, authorization, or policy. In Part 1, I discussed XML Encryption, which covers WS-Security confidentiality. In this article, we look closer at the integrity part of the equation: XML Signature, which turns out to be a bit more complicated than its confidentiality counterpart.

Read the whole series on XML security, "Yes, You Can Secure Your Web Services Documents:"

A new, improved approach to digital signature

The main difference between XML Signature and other digital signature standards such as PKCS#7 (Public-Key Cryptography Standard #7) is that it is—surprise, surprise—XML-aware. That is, with XML Signature, not only can you sign arbitrary digital content as a whole, but you can also sign just specific parts of XML documents. This proves useful when parts of XML documents can be moved, copied, encrypted, signed, and verified in any order, which is quite likely to happen as we move more towards shuffling around XML messages instead of binary payload over the wire. However, as you will see later, XML awareness doesn't come free.

Compared to XML Signature, XML Encryption is relatively straightforward. You select some nodes to encrypt, and that's basically it. You decrypt the ciphertext with the correct key, and you retrieve the plaintext—as simple as that. Not so with XML Signature! XML Signature is more complicated because signatures need to be verified correctly. Now, what can be so difficult about verification? Read on.

It's the information content, not the physical representation

Generating a signature over a document usually means calculating that document's digest and encrypting it with your private key. When you need to verify it on the other side of the wire, simply recalculate the document's digest and compare it with the digest you get from decrypting the encrypted digest with the signer's public key. That way, you can ensure the document has not changed, not even slightly.

But XML Signature is XML-aware, which introduces a new challenge. Look at the snippets below:

<blah meaning="also nothing" purpose='nothing'     >
   <blah2 />
</blah>

and

<blah purpose='nothing'    meaning = 'also nothing'>
   <blah2></blah2>
</blah>

Though both snippets have identical information content, because of their permissible differences, their digests will definitely differ. That's what you get when mixing XML with digital signature: digital signature checks whether a message has changed along the way, but an XML document's physical representation can—and quite likely will—change during processing, even though its information content remains the same.

Exclusive XML Canonicalization solves this problem. This W3C (World Wide Web Consortium) specification dictates how to generate the canonical form of an XML document that accounts for permissible changes such as white space or attribute ordering. As an example, if we canonicalize the two snippets above, both will yield the same canonical form:

<blah meaning="also nothing" purpose="nothing">
   <blah2></blah2>
</blah>

By calculating the digest over a document's canonical form, we can ensure that, as long as the document's information content stays the same, its signature will still verify, even if the physical representation has somehow changed during processing.

Yet sometimes, ordinary (also referred to as inclusive) canonicalization is not enough for XML Signature. Because of digital signature's fundamental nature, XML Signature could serve as the building block for many protocols in the future. However, in such protocols, extracting and inserting parts of XML messages into new messages is quite common. Consider the following snippet, already in canonical form:

<doh:inner xmlns:doh="http://doh"></doh:inner>

Suppose someone signs and submits that message to the system for further processing, during which it is inserted into an envelope:

<foo:outer xmlns:foo="http://foo">
   <doh:inner xmlns:doh="http://doh"></doh:inner>
</foo:outer>

You get the snippet above at the other end. To verify the signature over doh:inner, you must calculate the canonical form, which now, unfortunately, looks like the snippet below, thanks to the enveloping element's namespace:

<doh:inner xmlns:doh="http://doh" xmlns:foo="http://foo"></doh:inner>

Exclusive canonicalization handles this namespace mix-up (which will break signature verification) by excluding the envelope's context from doh:inner's canonical form. This way, you can still verify the signature over doh:inner even if it is removed, copied, and inserted out of and into other elements from different namespaces.

Some nodes are not meant to be decrypted

XML Signature and Encryption allow you to work on just a certain part of a document, which is good; you can sign/encrypt just the part you need while letting the system make sense of the document. This approach also works well in workflow systems, where several people may approve (by signing) or encrypt different parts of a document during its lifetime. But the fact that you can sign and encrypt a certain part of the document in any order raises an interesting problem.

Suppose your workflow engine handles rules such as "This type of transaction must be approved by a senior manager, followed by a manager." Suppose a transaction looks like the snippet below, after a senior manager named Sheridan has approved it:

<FundsTransfer>
   <Id>8293742</Id>
   <FromAccount>008-8-036982</FromAccount>
   <ToAccount>001-2-165921</ToAccount>
   <Money>
      <Amount>1000</Amount>
      <Currency>702</Currency>
      <Exponent>-2</Exponent>
   </Money>
   <Approvals>
      <Approval>
         <Username>sheridan</Username>
         <Designation>Senior Manager</Designation>
         <AuthCode>Gamma-six-six-Z-niner</AuthCode>
      </Approval>
   </Approvals>
</FundsTransfer>

The software generates Sheridan's signature over the snippet above and then encrypts his authorization code. Next, the document goes to a manager, Ivanova. After Ivanova signs the transaction, it now looks like the snippet below. Note that her authorization code has been encrypted as well:

<FundsTransfer>
   <!-- omitted for brevity -->
   <Approvals>
      <Approval Id="sheridan_approval">
         <Username>sheridan</Username>
         <Designation>Senior Manager</Designation>
         <EncryptedData xmlns=".../xmlenc#" Id="sheridan_authcode">
            <!-- omitted for brevity -->
         </EncryptedData>
         <Signature Id="sheridan_signature">
            <!-- omitted for brevity -->
         </Signature>
      </Approval>
      <Approval Id="ivanova_approval">
         <Username>ivanova</Username>
         <Designation>Manager</Designation>
         <EncryptedData xmlns=".../xmlenc#" Id="ivanova_authcode">
            <!-- omitted for brevity -->
         </EncryptedData>
         <Signature Id="ivanova_signature">
            <!-- omitted for brevity -->
         </Signature>
      </Approval>
   </Approvals>
</FundsTransfer>

At this step, the approval cycle has completed, and the document should process further. But think about it: to verify Ivanova's signature, your application must decrypt her AuthCode field, not Sheridan's. How is your application supposed to know that it should decrypt one but not the other? That transaction only needs two approvals. What about those that need the signatures of Delenn, Marcus, and Franklin as well?

Decryption Transform for XML Signature comes to the rescue! With Decryption Transform, you can tell the recipient the proper order of decryption and signature verification; not only will signatures be verified properly, but also people don't have to sign what they can't see. That is, Sheridan can sign the document with his authorization code in the clear and then encrypt it, instead of the other way around.

The example above actually needs more than just Decryption Transform. Notice that the signatures above are enveloped signatures. That is, the signed XML element itself envelopes each signature. XML Signature also offers an enveloping signature, which envelops the signed element and a detached signature, which is completely separate from the signed data object. Now, enveloped signature is tricky, isn't it? We're supposed to calculate the signed element's digest and then put the result inside the signature. But doing so will change the Signature element, thereby invalidating the digest! An enveloped signature transform solves this problem by excluding the Signature element itself (along with all its descendants) from digest calculation.

Next we'll see what an XML Signature looks like.

XML Signature: The core element

The Signature element represents an XML Signature and has the following structure (Note: This code is from the W3C's XML Signature page. ? 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>

In the structure above, SignedInfo is a mandatory element since it is the element that is actually signed. That is, if you sign a document in XML Signature, the signature is not generated over the document's digest directly, but over the SignedInfo element (which contains the digest).

Reference refers to the data object being signed. It includes the Transform elements, which process in their textual order. The last transform's output becomes the input to DigestMethod, which will produce the DigestValue element's content.

SignatureValue is quite obvious: it contains the signature value. KeyInfo is interesting because it is used in XML Encryption as well—to tell the processing application where it can obtain the keys to process the signatures. Object is an optional element that can contain any data, so you can place the equivalent of PKCS#7 authenticated attributes, such as signingTime, there. Object also plays an important role in enveloping signatures, as it contains the signed data.

With all this talk and no coding, you're probably growing bored. From this point on, I discuss writing code for XML Signature. Shall we begin?

Which library to use?

I thought long and hard before deciding on a library for this article. In Part 1, I chose XML Security Suite from IBM mainly because of its übercool feature: the template. But I also chose it because I couldn't find an open source Java library for XML Encryption then (this is no longer true—Apache's XML Security package now supports both XML Encryption and XML Signature). The situation differs for XML Signature; many libraries are available—you can even accomplish XML Signature with J#, Microsoft's Java development tool for its .Net Framework.

At the time of this writing, two open source libraries are available: Apache's XML Security package, and Gapxse. Like IBM's XML Security Suite, Gapxse also boasts the template feature, so you don't have to study the whole API and write too much code before testing it.

In any case, I decided to use IBM XML Security Suite (XSS) again. You'll like the fact that for XML Signature, building a template programmatically proves almost as easy as using an XML file. Let's see how we can write code to generate all three types of digital signatures—enveloping, enveloped, and detached.

Template, template, template all around

XML Signature with XML Security Suite is easy once you have the hang of it. In Part 1, I wrote most examples using XML files for templates. For XML Signature, the examples are a bit more programmatic, but fret not: generating signatures programmatically is actually simpler in XSS. But before going into the code, we'll need to do some setup.

Prepare the necessary files

First, you need at least a pair of keys to generate signatures and verify them. Doing that is very easy—just use the good old keytool:

keytool -genkey -alias sheridan -keyalg RSA -keysize 1024 -keystore jw2store -storepass password -storetype jceks

When you execute the command above, keytool will ask you several questions. It will use your answers to generate your certificate. Answer all the questions and you're set.

Now for the example, we use a simplified version of FundsTransfer. I start with this template:

FundsTransferTemplate.xml

<?xml version="1.0" encoding="utf-8"?>
<FundsTransfer>
   <Id>8293742</Id>
   <FromAccount>008-8-036982</FromAccount>
   <ToAccount>001-2-165921</ToAccount>
   <Money>
      <Amount>1000</Amount>
      <Currency>702</Currency>
      <Exponent>-2</Exponent>
   </Money>
</FundsTransfer>

Now we must see how to generate and verify all three signature types. This is getting interesting...keep on reading.

XSS's TemplateGenerator class allows you to manipulate and then generate a Signature element. The constructor takes four parameters:

TemplateGenerator(org.w3c.dom.Document factory, 
   java.lang.String digestMethod, 
   java.lang.String C14NMethod,
   java.lang.String signatureMethod)

These parameters basically set up the parameters inside the SignedInfo structure (see the "XML Signature: The Core Element" section above). After setting up the parameters, you have an empty Signature element. Next, you must add References, which point to the signed data.

Create an enveloped signature

I wrote a helper class (which you can download with this article's source code) that slightly simplifies signature generation, but for illustration purposes, I'll take snippets of the methods inside my XMLSignature class and paste those snippets as we go.

First, let's convert the template for FundsTransfer to a Document and create a TemplateGenerator instance:

Document doc = XMLUtil.getDocumentFromFile("FundsTransferTemplate.xml");
TemplateGenerator templGen = new TemplateGenerator(doc,
   XSignature.SHA1,
   Canonicalizer.W3C2,
   SignatureMethod.RSA);

Next, we create the reference, which points to the resource we want to sign. Mind you, although the signature is enveloped within the signed element, we still must tell XSS which part of the envelope it should sign later. In this example, we want to sign the whole enveloping element, so we use an empty URI (Uniform Resource Identifier) ("") to create the reference:

   Reference ref = templGen.createReference(uri);
   
   // This transform is necessary so that
   // the signature is excluded from digest
   // calculation
   ref.addTransform(Transform.ENVELOPED);
   // We also want to canonicalize the 
   // thing first before getting its digest
   ref.addTransform(Transform.W3CC14N2);
    
   templGen.addReference(ref);
    
   Element sig = _templGen.getSignatureElement();

Now that we have the element, we need to put the signature in the envelope. Let's add the signature as a child of the root element:

   // Put signature inside the envelope   
   Node nodeSig = envelope.getOwnerDocument().importNode(sig, true);
   envelope.appendChild(nodeSig);

At this point, we can generate the signature. Recall that Signature has an optional KeyInfo element, which can be used in verification later. In this example, we use a certificate and put it in the KeyInfo element (in the sample code, note the underscore in front of the variables; they represent instance variables):

   // Now get the keys, the certs, and everything
   String alias = "sheridan";
   char[] password = "password".toCharArray();
   // This is your regular KeyStore code 
   KeyStore keystore = KeyStore.getInstance("jceks");
   keystore.load(new FileInputStream("jw2store"), password);
   X509Certificate _verifyingCert = 
     (X509Certificate)keystore.getCertificate(alias);
   _signingKey = keystore.getKey(alias, password); // a private key
   // Fill in the KeyInfo with the cert that
   // we use to verify the signature later      
   _verifyingKeyInfo = new KeyInfo();
   KeyInfo.X509Data x5datum = new KeyInfo.X509Data();
   x5datum.setCertificate(_verifyingCert);
   x5datum.setParameters(_verifyingCert, true, true, true);
   _verifyingKeyInfo.setX509Data(new KeyInfo.X509Data[] { x5datum });
   // Now insert the KeyInfo into the Signature element.
   // First get the signature element from 
   // the envelope...
   Element signatureElement =  
     (Element)parentElementInEnvelope.getElementsByTagNameNS(
     XSignature.XMLDSIG_NAMESPACE, "Signature").item(0);
   _verifyingKeyInfo.insertTo(signatureElement);
   // And finally, the signing itself!   
   SignatureContext sigContext = new SignatureContext();
   sigContext.setIDResolver(
     new AdHocIDResolver(signatureElement.getOwnerDocument()));
   sigContext.sign(signatureElement, _signingKey);

Below is the resulting enveloped signature, with the KeyInfo and all:

<?xml version="1.0" encoding="UTF-8" ?>
<FundsTransfer>
   <Id>8293742</Id>
   <FromAccount>008-8-036982</FromAccount>
   <ToAccount>001-2-165921</ToAccount>
   <Money>
      <Amount>1000</Amount>
      <Currency>702</Currency>
      <Exponent>-2</Exponent>
   </Money>
   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
         <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
         <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
         <Reference URI="">
            <Transforms>
               <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
               <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>oGpbCNJdgBu/+ReuBFsjEIAHTBo=</DigestValue>
         </Reference>
      </SignedInfo>
      <SignatureValue>
    nw7C6IuQwGMe9POTPvfY0XLOd6nTbTN+kJnEZVF19LtEGWFpL/cN76Eg0L7MNyIzLGAMGPsz
    IkAmKgrx0GfWmY6mwpeSHH7RV6xOPCrQ+n/Bx3iQ8Hi9h2P8FpGpIohTdhByBA_u88 ?Daq0iEBW
    uqrzLWYbUMnWwYDPoCCqRD7RwmI=
  </SignatureValue>
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
         <X509Data>
            <X509IssuerSerial>
               <X509IssuerName>CN=John Sheridan,OU=Babylon 5,O=Interstellar Alliance,L=Babylon,ST=Babylon,C=B5</X509IssuerName>
               <X509SerialNumber>1031403982</X509SerialNumber>
            </X509IssuerSerial>
            <X509SubjectName>CN=John Sheridan,OU=Babylon 5,O=Interstellar Alliance,L=Babylon,ST=Babylon,C=B5</X509SubjectName>
            <X509Certificate>
MIICbDCCAdUCBD15+c4wDQYJKoZIhvcNAQEEBQAwfTELMAkGA1UEBhMCQjUxEDAOBgNVBAgTB0Jh
Ynlsb24xEDAOBgNVBAcTB0JhYnlsb24xHjAcBgNVBAoTFUludGVyc3RlbGxhciBBbGxpYW5jZTES
MBAGA1UECxMJQmFieWxvbiA1MRYwFAYDVQQDEw1Kb2huIFNoZXJpZGFuMB4XDTAyMDkwNzEzMDYy
MloXDTAyMTIwNjEzMDYyMlowfTELMAkGA1UEBhMCQjUxEDAOBgNVBAgTB0JhYnlsb24xEDAOBgNV
BAcTB0JhYnlsb24xHjAcBgNVBAoTFUludGVyc3RlbGxhciBBbGxpYW5jZTESMBAGA1UECxMJQmFi
eWxvbiA1MRYwFAYDVQQDEw1Kb2huIFNoZXJpZGFuMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQCm8c9Kar1NhYMHbWJ00It9T9vZaSeUypItQ7Z0oWNIR2GqRrxG+JXdKZxC2BF7XV3QGvTUvfLa
sauu4/+GZEG/wkMUJZowhj/EXqzM87UbsbQFPikNNyY/o+LvWnGyyaq5jAxFqPeyMbx36vmS0ven
/TRWZWFIh16rnTpjaf0vuQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAFP1PZOkJwepQXPjklVrdwuV
Fdbd3cxZtsB0kX0iPumtlwDrdfcb9BEK4ejsTAm0XZ1XemvxBfVLNW9N6BeG4seVZk4gDsQaKq+o
O0QxPH0I30pdfEtPY3N3xB4wwFwAh6CwVYG/zbvW+P+YNNzf5HXntMZUJZ8K+9NGsaKoPVRB
            </X509Certificate>
         </X509Data>
      </KeyInfo>
   </Signature>
</FundsTransfer>

Note that we use XML Canonicalization in two places: first as a CanonicalizationMethod, second as a Transform inside the Reference. The difference is that the former canonicalizes the SignedInfo element before digest calculation, while the latter canonicalizes the soon-to-be-signed resource, which is FundsTransfer in this example. Next, let's see how to generate an enveloping signature.

Create an enveloping signature

In an enveloping signature, the signed element is inside the Signature element. The code for setting up the element and generating the signature is quite similar to the one for enveloped signature, except for a little difference:

   Document doc = XMLUtil.getDocumentFromFile("FundsTransferTemplate.xml");
   TemplateGenerator templGen = new TemplateGenerator(doc,
     XSignature.SHA1,
     Canonicalizer.W3C2,
     SignatureMethod.RSA);
   // Wrap the whole thing in an object, and name it  
   // "sheridan_signature"  
   Element wrappedResource = templGen.wrapWithObject(doc.getDocumentElement(), 
     "sheridan_signature");
   Reference ref = templGen.createReference(wrappedResource);
   templGen.addReference(ref);
   templGen.getSignatureElement();

You can see that the snippet above has an additional line, which wraps the signed data in an object before adding it to the Signature element. The signing code is identical to an enveloped signature. Here's the result:

<?xml version="1.0" encoding="UTF-8" ?>
<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="#sheridan_signature">
         <Transforms>
            <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
         </Transforms>
         <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
         <DigestValue>rBmyZDoDaJIq5KwJcfBR4LU0hwk=</DigestValue>
      </Reference>
   </SignedInfo>
   <SignatureValue>
    VHQCOFJQwlXAGiHnMxlQihF07Z8N/0u+kJoO4dMffjIPIv14rM2oCe4Wpy7NhBfGpi7UWFHc
    KuTC6vTXboYRZB9cTh1W1Q4QQ8II2kBhQrWuXB056VJR6tmzy6+Selz9rjCOX1wHAdaNUBRO
    g+KOtKlXP1E059BEJ0tu1GvozfY=
  </SignatureValue>
   <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <X509Data>
         <!-- omitted for brevity -->
      </X509Data>
   </KeyInfo>
   <dsig:Object Id="sheridan_signature" xmlns="" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
      <FundsTransfer>
         <Id>8293742</Id>
         <FromAccount>008-8-036982</FromAccount>
         <ToAccount>001-2-165921</ToAccount>
         <Money>
            <Amount>1000</Amount>
            <Currency>702</Currency>
            <Exponent>-2</Exponent>
         </Money>
      </FundsTransfer>
   </dsig:Object>
</Signature>

Eagle-eyed readers may notice that, although we sign the same document in both the enveloping and enveloped signatures, their DigestValues differ. The reason is that an enveloping signature's digest calculation includes the Object wrapper around the signed element, whereas an enveloped signature's calculation only covers the signed element itself.

Now let's see how to create a detached signature of a non-XML resource.

Create a detached signature

Creating a detached signature is straightforward: you only need to supply the URI of the resource you want to sign. In this example, I want to sign a screen capture of JavaWorld's homepage, which I saved in a file. Here's the code that does that (the setup and signing process are the same as the previous two):

   // Create the template generator up here
   File file = new File("javaworld.jpg");
   
   Reference ref = templGen.createReference(file.toURL().toString());
   templGen.addReference(ref);

And here's the result:

<?xml version="1.0" encoding="UTF-8" ?>
<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="file:/D:/Development/Java/IDE/eclipse/workspace/XML_Security_II/javaworld.jpg">
         <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
         <DigestValue>ineUF+cltlSh0YtzTJt8L/Kzqg0=</DigestValue>
      </Reference>
   </SignedInfo>
   <SignatureValue>
    akK3kdg5jpnXvtm0dM99NokrqG1HCWviMQLvm5eYuL2BqEd2sRB4JtNX7WTTtJCylEXUXbuX
    EG/daA3m89j0JwTOGELSvyNzmuZRPXp+j9abB/8gIwa9PNJ8l6hgcwT3ATh8/CnRdQG0HknM
    VRbAetYNb7ooioFXw0RnSLUy1fs=
  </SignatureValue>
   <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <X509Data>
         <!-- omitted for brevity -->
      </X509Data>
   </KeyInfo>
</Signature>

Notice how Reference's URI attribute refers to the signed file, which is local. Now that we've seen all XML signature types, let's learn how to verify them.

Verify the signatures

Verifying a signature is straightforward if you have the key—java.security.Key, to be exact. This is all you need to verify a signature:

   SignatureContext sigContext = new SignatureContext();
   sigContext.setIDResolver(
     new AdHocIDResolver(signatureElement.getOwnerDocument()));
   Validity val = sigContext.verify(signatureElement, verifyingKey);

Remember, we set the ID of the signed elements in preparing an enveloping signature. The ID is used to refer to the corresponding Object that wraps the signed data. During verification, somebody must resolve that ID and get the actual signed resource—IDResolver does just that. AdHocIDResolver is a simple implementation of IDResolver that proves sufficient for our examples.

The method verify() returns an instance of Validity, which lets you check each Reference's validity (more than one can exist). To check whether the signature itself is valid, that is, that SignedInfo and all its inside References are valid, use this call:

boolean isValid = val.getCoreValidity();

Now, what if you don't have a java.security.Key to pass to verify()? In all our previous examples, we didn't pass a key, but a certificate, which contains the key. The certificate is contained inside the signature's KeyInfo element.

So ideally, you should have another resolver that resolves KeyInfo to a java.security.Key. And if the signature already contains a KeyInfo, you'd want a method smart enough to realize that, extract the key, and verify the signature with it.

That's why I came up with a resolver (I call it a KeyInfoResolver, to be exact) for this article's downloadable sample. With this resolver help's, the helper class I've written allows you to just use one line of code to verify an XML signature:

   Validity val = XMLSignature.verify(sigElement);

XMLSignature.verify() uses a KeyInfoResolver instance (Note: this differs from KeyInfoResolver in XML Encryption); specifically, it uses a class that I named ever-so-unimaginatively KeyAndCertKeyInfoResolver.

This class knows how to get a key out of a KeyInfo if it's just a simple key or a certificate chain—which proves sufficient for many cases, including our examples. The class even knows how to validate a certificate chain if you pass it a KeyInfo containing the signer's certificate and a certificate chain that goes to the trust anchor (that is, the self-issued root certificate). Here's what the class's resolve() method looks like:

   public Key resolve(KeyInfo keyInfo)
      throws 
      KeyInfoResolvingException {
      Key extractedKey = keyInfo.getKeyValue();
      // This is good, assume this one
      if(extractedKey != null) {
         return extractedKey;
      } else {
        
         // Else check if there are
         // certificates there. The code below
         // assumes there's only one X509Data;
         // if there are more, just use the first.
         KeyInfo.X509Data[] x509Data = keyInfo.getX509Data();
         if (x509Data != null && x509Data.length > 0) {
            KeyInfo.X509Data x509DataItem = x509Data[0];
    
            // Get certificates
            X509Certificate[] certs = x509DataItem.getCertificates();
            if (certs == null || certs.length <= 0) {
               throw new KeyInfoResolvingException(
                 "No certificate in X509Data!");
            }
            // Get CRL for checking
            X509CRL crl = x509DataItem.getCRL();
            // verifyCertificates
            extractedKey = verifyCertificatesAndExtractKey(certs, crl);
         } else {
            // No key, no X509Data, give up
            throw new KeyInfoResolvingException(
              "This resolver doesn't know how to handle this KeyInfo.");
         }
      }        
      return extractedKey;
   }

Things to look forward to

In this article, you learned about XML Signature and the problems that accompany the flexibility of an XML-aware digital signature mechanism, as well as those problems' solutions. You also saw what XML Signatures look like, with all the variations in the coupling of the signature and signed data. You also learned how to easily generate XML Signatures with IBM's XSS; just look at the snippets sprinkled throughout the article, download the source bundle, and play around with it.

As of now, XML Signature is already more developed than its cousin XML Encryption. The official Java API for XML Signature should be coming out soon, so be on the look out. JavaOne 2002 featured a high-level overview of the API, and from the look of it, I gather it will be quite easy to learn and use. For example, the API features a class called XMLSignatureMarshaller, which converts XML Signatures to/from XML. If this is not the template feature we've come to know, I don't know what is. The API also includes Transforms, URIDereferencer, and so on, whose names, after reading this article, should clue you in to their functionalities.

Last but not least, XML Signature operates under the assumption that key distribution and registration has already been handled. This article's examples are trivial—we created the key and then used it to verify the signatures. In the real world, things are definitely more complicated. Keys must be registered and distributed properly, and trust relationships must be established. XKMS (XML Key Management Specification), related to Java Specification Request 104, tries to address these issues. In any case, many new and interesting things are emerging from XML security, and with its strong tie to Web services security, you can bet an official Java API will follow soon.

Ray Djajadinata first encountered Java in 1995 when he saw Duke cartwheeling inside the HotJava browser. He then wrote introductory Java articles for a local programming magazine and explored the language further while still using C++ at work. In 1999, he switched to Java at work as well, and he's been hooked ever since. Currently he's a senior technical manager at eXtropia, an open Web technology company. He likes eXtropia because the company encourages employees to embrace new technologies. He also gets to work on cool products like the eXtropia Digital Signing application, which triggered his research about the topic of this series.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more