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

XML Signature ensures your XML documents' integrity

1 2 3 Page 2
Page 2 of 3

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);
1 2 3 Page 2
Page 2 of 3