Safeguard your XML-based messages

Create secure Web services with Apache XML Security

Web services are here to stay, but if you are like most software developers, you worry about the plaintext SOAP (Simple Object Access Protocol) messages being exchanged over the Web. Web services security is a hot topic today because the success of this exciting technology hinges directly upon how secure we can make it. To that end, the World Wide Web Consortium (W3C) has defined the XML Signature and XML Encryption specifications for digitally signing and encrypting XML-based communication messages, such as the SOAP messages used in Web services. Furthermore, companies such as IBM, Microsoft, and VeriSign have partnered to provide additional specifications, such as WS-Security (Web Services Security), that build upon these W3C specifications. And who hasn't heard of the Liberty Alliance Project, a consortium of companies led by Sun Microsystems to provide a standards-based single sign-on solution to Web services? In the midst of all these initiatives lies the Apache XML Security project, an open source project that currently implements the W3C XML Signature specification and will soon support the XML Encryption specification. This article serves as a tutorial to get you up to speed with this outstanding implementation.

Introducing the Apache XML Security project

The Institute for Data Communications Systems at the University of Siegen in Germany donated the XML Security project to the Apache Foundation in September 2001. As of this writing, the latest version is 1.0.4, which is the version I discuss in this article—version 1.0.5 is currently in testing and available for download from Apache. Once you download the zipped binary distribution from Apache, unzip it in your root directory (for example, C:) in a directory called xml-security-1_0_4. (yes, these instructions have a Windows slant). The Apache XML Security project requires the Java Cryptography Extensions (JCE) library, which is not included with the distribution due to US export restrictions on cryptography. I obtained my copy, jce_1_2_2.jar, from Sun's Website and placed it in the ext directory under my Java runtime (that is, the directory C:\jdk1.3\jre\lib\ext). You will also need a stable version of Xalan (2.2.0 or later), which you can also download from Apache. Ensure xalan.jar is either in your classpath or in the ext directory along with the JCE JAR. You are now ready to start signing your XML!

Note: I use J2SE (Java 2 Platform, Standard Edition) 1.3, since my client originally required that. For those of you using J2SE 1.4, which comes bundled with a beta version of Xalan, you have one more installation step: You must put the xalan.jar into a special directory in your JDK—j2sdk1.4.0/jre/lib/endorsed/xalan.jar. If you installed an out-of-the-box J2SE 1.4 (for example, on Windows 2000), the endorsed directory does not exist; you'll have to create it by hand. Putting this JAR in another location like lib/ext will not work. For more on that issue, check the Unofficial JAXP (Java API for XML Processing) FAQ. Why did Sun include a beta version of Xalan in J2SE 1.4? I have no idea.

Secure your Web services

As I mentioned previously, Web services are based upon exchanging SOAP messages, which are clear text XML messages. While clear text XML-based message exchange is fine for amateur applications, it proves unacceptable for real-world business applications that deal with sensitive data, such as your credit card number, mother's maiden name, or social security number. It goes without saying that these messages must be secured. Security in the context of message exchange between two or more parties typically implies that each message sent and received exhibits the following four characteristics: authenticity, data integrity, nonrepudiation, and privacy, or confidentiality. That definition of secure message exchange holds true even for digital messages sent over the network. For such messages, digital signatures provide the first three of the four characteristics (please see the sidebar, "What Is a Digital Signature?," for more details). Data encryption provides the fourth. As mentioned above, the Apache XML Security project is an implementation of the W3C's XML Signature specification and hence can provide authenticity, integrity, and nonrepudiation to your SOAP-based Web services.

A real-world example of Apache XML Security

To make this tutorial slightly more interesting, I discuss the Apache XML Security library in the context of the Apache Axis project. Axis is Apache's next-generation SOAP implementation and has an extremely extensible architecture (see Resources for more links and articles about Axis). Unlike the previous Apache SOAP implementation, Axis allows you to get into the engine and extend the SOAP (that is, message) processing with your own custom code in the form of handlers. I exploited that exact feature to create a custom handler that digitally signs the SOAP request message from the client just before it hits the wire, and then verifies and removes the signature on the server side. Similarly, when the server sends back a SOAP response, the handler signs it on the sever side and verifies it on the client side. The figure below shows the flow of messages between the client and the server.

Message flow between the client and the server. Click on thumbnail to view full-size image.

The handler uses Apache XML Security to sign the SOAP message and later verify the signature. Obviously, in this scenario, both the client and server use the Axis SOAP engine, but the client does not have to use Axis. The digital signature XML that Apache XML Security creates complies with W3C's XML Signature specification and hence can be consumed and verified by any compliant client.

The handler's mechanics and configuration reach beyond this article's scope. However, we will certainly look at the Apache XML Security-related code within it.

Canonicalization: What is it and why is it necessary?

Although not directly related to our example, XML Canonicalization is an important concept to grasp in the context of XML signatures. Consider the following two XML fragments:

Fragment 1

<book>
   <Author>Joe</Author>
   <ISBN value="12300093456"/>
</book>

Fragment 2

<book>
   <Author    >Joe</Author>
   <ISBN value="12300093456"/>
</book>

The above two XML fragments differ only in that the second fragment's Author start element has a series of whitespaces. Logically the two fragments remain equivalent. However, the digital signature for the above fragments would indicate otherwise, because digital signatures rely on the data's physical representation, not its logical meaning. The above two fragments are just two examples of logically equivalent XML. Since XML is whitespace agnostic, an infinite amount of such variations are available. For digital signatures to be more useful than hassle, there must be some way that we can account for such variations (that result in logically equivalent XML). XML Canonicalization addresses that need by resolving issues with enveloping and resulting namespace inclusions. Apache XML Security supports both inclusive and exclusive XML Canonicalization. Listing 1 shows a sample program that canonicalizes the XML Fragment 1 above:

Listing 1. CanonicalizationExample.java

import java.io.ByteArrayInputStream;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xml.security.c14n.Canonicalizer;
public class CanonicalizationExample 
{
   static String input = "<book>\n\t<Author>Joe</Author>
     \n\t<ISBN value=\"12300093456\"/>\n</book>";
   public static void main(String args[]) throws Exception 
   {
      org.apache.xml.security.Init.init();
      DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
      dfactory.setNamespaceAware(true);
      dfactory.setValidating(true);
      DocumentBuilder documentBuilder = dfactory.newDocumentBuilder();
      // This is to throw away all validation warnings
      documentBuilder
         .setErrorHandler(new org.apache.xml.security.utils
            .IgnoreAllErrorHandler());
      byte inputBytes[] = input.getBytes();
      Document doc = documentBuilder.parse(new ByteArrayInputStream(inputBytes));
      
      Canonicalizer c14n = Canonicalizer.getInstance(
        "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
      byte outputBytes[] = c14n.canonicalizeSubtree(doc);
      System.out.println("Input (Uncanonicalized) Document\n" + input);
      System.out.println("\nOutput (Canonicalized) Document\n" + new String
        (outputBytes));
   }
}

Compile this example using the following command line:

javac -classpath C:\xml-security-1_0_4\build\xmlsec.jar;C:\xml-security-
   1_0_4\libs\xercesImpl.jar;C:\xml-security-1_0_4\libs\xml-apis.jar;C:\xml-
   security-1_0_4\libs\;C:\xml-security-1_0_4\libs\xmlParserAPIs.jar;C:\xml-
   security-1_0_4\libs\log4j-1.2.5.jar CanonicalizationExample.java

Run the example with the following command line:

java -classpath .;C:\xml-security-1_0_4\build\xmlsec.jar;C:\xml-security-
   1_0_4\libs\xercesImpl.jar;C:\xml-security-1_0_4\libs\xml-apis.jar;C:\xml-
   security-1_0_4\libs\;C:\xml-security-1_0_4\libs\xmlParserAPIs.jar;C:\xml-
   security-1_0_4\libs\log4j-1.2.5.jar;C:\xalan-j_2_4_0\bin\xalan.jar 
   CanonicalizationExample

Remember to substitute the directory where you unzipped the Apache XML Security binary distribution with C:\xml-security-1_0_4 in the above two command lines.

The input string in the above listing initializes to Fragment 1. The canonicalized output is shown below:

<book>
        <Author>Joe</Author>
        <ISBN value="12300093456"></ISBN>
</book>

You will notice that the self-closed ISBN tag in the fragment has expanded. One of the canonicalization rules requires the expansion of all such self-closing tags. The rules of XML Canonicalization are beyond the limited scope of this article; refer to Resources for links to more detailed discussions.

Now let's quickly look at the Apache XML Security-specific code in the above listing. Before you can invoke any of the Apache XML Security library's services, you must initialize it as shown below:

org.apache.xml.security.Init.init();

Once the library initializes, obtain a canonicalizer object by calling the static getInstance() method on the Canonicalizer class. Tell the getInstance() method what type of canonicalizer to return by passing in the appropriate URI constant. Valid values are http://www.w3.org/TR/2001/REC-xml-c14n-20010315, http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments, http://www.w3.org/2001/10/xml-exc-c14n#, and http://www.w3.org/2001/10/xml-exc-c14n#WithComments. The actual canonicalization is performed by calling the canonicalizeSubtree() method on the canonicalizer object, thereby passing in the DOM (Document Object Model) document. This method returns the canonicalized document in the form of a byte array.

Most of you will never directly canonicalize XML documents because the XML digital signature library that you use (such as Apache XML Security) will perform the canonicalization during the signature-creation process. Regardless, realizing that canonicalization happens under the covers is important.

Sign a SOAP message

Now that we've taken care of the basics, let's actually sign some SOAP messages. Assume we have access to a simple Web service that retrieves stock quotes from the Internet and sends back a JavaBean (serialized in the form of XML, of course) containing various pieces of information such as the current price, day's high and low, or 52-week high and low. Here's a sample SOAP message that a client could send to the Web service to retrieve stock quote information about IBM:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="
  http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <soapenv:Body>
  <ns1:getPrice soapenv:encodingStyle=
    "http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="urn:quote">
   <symbol xsi:type="xsd:string">IBM</symbol>
  </ns1:getPrice>
 </soapenv:Body>
</soapenv:Envelope>

And a typical response from the Web service could be:

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