RegisterLog In/Log OutView Cart
O'Reilly
web.oreilly.com
BooksSafari BookshelfConferencesO'Reilly NetworkO'Reilly GearLearning Lab
 
advertisement




Extending XSLT to Encrypt XML on the Fly

by Doug Tidwell
08/13/2001

XSLT is an extremely powerful, flexible language for transforming XML documents. Despite its power, there are times when we need to write extension elements and extension functions to do things the language was never designed to do. In this article, I'll take a look at XSLT's extension mechanism by writing an extension function that encrypts part of an XML document.

The good news is that the extension mechanism is defined in the XSLT 1.0 specification. Unfortunately, the specification doesn't define all of the details about how extensions should work, so there are some inconsistencies between processors. If you write an extension function or an extension element that works with your favorite XSLT processor, no one can do something sinister to prevent the extension from working. On the other hand, if you decide to change the XSLT processor you're using, you'll probably have to change some of your code.

Our example here is written in Java, using the interfaces defined by the Xalan XSLT processor. As we write an extension function, we'll have to figure out how to move data from the XSLT processor to our code and back. (If you're not a Java hacker, the Java version of Xalan also supports IBM's Bean Scripting Framework. That lets you write extensions in JPython [Jython], JavaScript, Perl Script, Jacl, and other scripting languages if you prefer.)

A Document in Need of Encryption

To get started, let's look at a sample document. Here's an XML document that represents a customer order:


<?xml version="1.0"?>
<customer_order>
  <items>
    <item>
      <name>Turnip Twaddler</name>
      <qty>3</qty>
      <price>9.95</price>
    </item>
    <item>
      <name>Snipe Curdler</name>
      <qty>1</qty>
      <price>19.95</price>
    </item>
  </items>
  <customer>
    <name>Doug Tidwell</name>
    <street>1234 Main Street</street>
    <city state="NC">Raleigh</city>
    <zip>11111</zip>
  </customer>
  <credit_payment>
    <card_issuer>American Express</card_issuer>
    <card_number>1234 567890 12345</card_number>
    <expiration_date month="10" year="2004"/>
  </credit_payment>
</customer_order>
  

Related Reading

XSLT
By Doug Tidwell


Read Online--Safari Search this book on Safari:
 

Code Fragments only

Glancing at our document, it's pretty clear that the <credit_payment> element contains information we'd like to keep secure. We'll use the element-wise encryption function shipped with the XML Security Suite (XSS4J), a package of XML security utilities available at IBM's alphaWorks Web site. This technology is built on the Java Cryptography Extension (JCE) and the XML Digital Signatures work done jointly by the W3C and the IETF. Using element-wise encryption, we can convert our sample document into code that looks something like this.

As you can see from the example, the <credit_payment> element has been replaced with an <EncryptedData> element. To anyone unfamiliar with the original document format, the encrypted version offers no clues as to the original content of the encrypted element. <EncryptedData> might represent a single empty element with a couple of attributes; it might represent a single element with lots of text; it might represent an element which contained multiple levels of descendants beneath it. Without the appropriate keys to unencrypt your data, the data is just so much garbage.

Designing Our Extension Function

What we'd like to do is write an extension function so that we can use an XSLT stylesheet to encrypt various elements. This gives us several advantages:

  • If we want to encrypt other portions of the document, we can do that with simple changes to our stylesheet.

  • We can add logic to our stylesheet so that different portions of the document will be encrypted for different users. For example, my medical history might be encrypted unless you work in the medical department, and my salary history might be encrypted unless you work in Human Resources.

  • We can keep all of the details of our encryption technology on the server. A client that receives the partially encrypted document above has no idea if this document was partially encrypted on demand, whether other users see a similarly encrypted document, or what the original document looked like.


Visit xml.oreilly.com for a complete list of O'Reilly books about XML technologies.

We want to create an extension function that takes as an argument the node of the XML document that we want to encrypt, along with whatever parameters we need to pass to the cryptographic routines. Here is what our extension function call looks like:


  <xsl:template match="credit_payment">
    <xsl:copy-of select="encrypt:encryptNode(., 'storepass', 
      'keystore', 'key', 'crypto-details.xml')"/>
  </xsl:template>

We're passing several things to our function. The first is the current node (represented by the compact XPath expression '.'). Next is the password to JCE keystore, followed by the name of the keystore file (cleverly named keystore here), and the alias of the key (named key, continuing our naming convention). The last item we pass to the function is the name of a configuration file used by XSS4J. Here's what that file looks like:


<?xml version="1.0"?>
<EncryptedData xmlns="http://www.w3.org/2000/11/temp-xmlenc" 
  Type="Element">
  <EncryptedKey>
    <EncryptionMethod Algorithm="urn:rsadsi-com:rsa-v1.5"/>
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <KeyName>key</KeyName>
    </KeyInfo>
  </EncryptedKey>
  <EncryptionMethod Algorithm="urn:nist-gov:tripledes-ede-cbc"/>
</EncryptedData>

This file defines various details about the cryptographic algorithms used to encrypt the data. In this case, we're using a randomly generated TripleDES key, which is encrypted with our RSA key (this is also known as key transport). This configuration file is one of the requirements of the XSS4J classes we're using. Specifying these details in a separate file gives us more flexibility in implementing encryption; if we want to change the algorithms we're using, we simply modify the XML file listed above.

At this point, we've defined the arguments to our extension function. We chose these arguments based on the requirements of the XSS4J classes we're using to encrypt the data, and based on the structure of the document we're encrypting. The only thing left is the minor detail of writing the code.

Writing Our Extension Function

Our final step is to actually write the code that will transform the appropriate part of the XML document. We've already looked at the method signature in the stylesheet; here's a table that lists the arguments, their datatype in the stylesheet, and their datatype in the extension function:

Extension Function Parameter XSLT Datatype Java Datatype
. [The current node] An XPath node-set org.w3c.dom.NodeList
'storepass' A string A string
'keystore' A string A string
'key' A string A string
'crypto-details.xml' A string A string

Most of the datatype conversions are straightforward, since we're passing mostly string data back and forth. The NodeList object is more complex, however. Part of the Document Object Model (DOM) standard, a NodeList is an ordered set of document nodes. We'll use one of these nodes (there's only one in our case, but there could be many with a different XML document) as one of the inputs to the XEncryption.encrypt() function.

Here's the actual signature of our extension function:


  public static XNodeSet encryptNode(NodeList nl, String passPhrase,
                                     String keyStore, String keyName,
                                     String encryptionTemplate)

You can see how the arguments to our function match the table above. Now that our extension function has the data it needs from the XSLT processor, we need to invoke the XEncryption.encrypt() function, the code that actually does the work of encrypting our data. The version of the function we'll be using takes five parameters:

XEncryption.encrypt() Parameter Comes from The data to be encrypted The XSLT processor. We get a NodeList, and the first (and only, in this case) node in the list is what we want to encrypt. A flag indicating whether just the element's content should be encrypted It's always false. A DOM Element representing the <EncryptedData> element from our configuration file Parsing the XML configuration file. The name of the file is passed to us by the XSLT processor. A Java Key object (java.security.Key) A Java keystore. We get the password of the keystore, the name of the keystore, and the name of the key alias from the XSLT processor. A DOM Element representing the <EncryptedKey> element from our configuration file Parsing the XML configuration file. Again, we get the name of this file from the XSLT processor.

At this point, we simply gather the five parameters we need and call encrypt(). The first two items in the table above are given, so we can move on to the third. We'll create a DOMParser object to parse the XML configuration file. Remember, the root element of that file was an <EncryptedData> element, so getting the root of the DOM tree will give us the third parameter we need. Here's how the code looks:


   DOMParser parser = new DOMParser();
   parser.setIncludeIgnorableWhitespace(false);
   parser.parse(encryptionTemplate);
   doc = parser.getDocument();
   ee = doc.getDocumentElement();
   Element ek = 
     (Element)(ee.getElementsByTagName("EncryptedKey").item(0));

In this code, the document element of the DOM tree is a DOM Element that represents the <EncryptedData> element. We also get its first <EncryptedKey> element; we'll need that as the fifth parameter to XEncryption.encrypt(). (The DOM getElementsByTagName method returns a NodeList of all the child elements with a given name. We take the first one from the NodeList and save it for later.)

The only thing left is the key from the Java keystore. We need to open the keystore using the keystore password we were given by the XSLT processor. Once we've opened the specified file, we'll try to retrieve the key whose alias we received. If any of these steps fail, our code will throw an exception and stop. Here's the actual code:


   KeyStore ks = KeyStore.getInstance("JKS");
   ks.load(new FileInputStream(keyStore), passPhrase.toCharArray());
   Key k = null;
   if (ks.isKeyEntry(keyName)) 
     k = (ks.getCertificate(keyName)).getPublicKey();

In this sample, we create a Java keystore (that's what JKS stands for), then attempt to load the keystore using the filename and password given to us by the XSLT processor. Once the keystore has been loaded, we attempt to get the public key whose name we received from the processor.

At this point, we have all the data we need to invoke XEncryption.encrypt(). Here are the magic lines of code that do this:


   encrypted = xenc.encrypt((Element)nl.item(0), false, ee, k, ek);
   encryptedResult = new XNodeSet(encrypted);

In this listing, encrypted is a DOM Element. We use this to create an XNodeSet; the XNodeSet is a Xalan-specific class that represents an XPath node-set. We then return the XNodeSet, and we're done.


   return encryptedResult;

The output of our transformation is the encrypted document we looked at earlier. The <credit_payment> element has been encrypted. This technology brings up an interesting scenario. Today, if I go to a Web site and order something, I'm comfortable sending my credit card number across a secure socket. In doing so, of course, I'm trusting that the employees of the online merchant won't do something improper with my credit card number. What if I used my private key and American Express's public key to encrypt my credit card information? The merchant would be unable to view any of the data; they would simply pass it on to American Express for verification. American Express could use its private key and my public key to unencrypt the data, validate that it actually came from me, then return an authorization code to the online merchant.

Summary

Now that the code is done, we have a powerful extension function we can invoke in our XSLT stylesheets. As I mentioned earlier, we can use logic in our stylesheets to change which parts of the document (if any) are encrypted for particular users or in certain circumstances. Most extension functions are written similarly: we need to use functionality available in some external package, and our work in writing the extension function involves gathering the appropriate data from the XSLT environment, passing it to our extension function, generating the appropriate results, then getting that data back to the XSLT environment. Once you're comfortable writing extension functions to integrate other code with the XSLT processor, you'll be amazed at what your stylesheets can do.

Resources

Here's a list of various resources that might be useful to you:

  • My O'Reilly book, XSLT, contains an extensive discussion of the XSLT extension mechanism, with many more examples of both extension functions and extension elements. See XSLT for more information.

  • The XML Security Suite is available at IBM's alphaWorks site. It comes with a variety of sample applications, the DOMHASH algorithm, and code to translate between XML and ASN.1, and vice versa. XSS4J was created at IBM's Tokyo Research Lab by the same team that created what ultimately became the Xerces parser.

  • The Xalan stylesheet engine is available at the Apache XML project. The Xerces parser used in this article is the xerces.jar that ships with Xalan.

  • Find out more about the Java Cryptography Extension at java.sun.com/products/jce. You can find a list of companies and organizations that offer cryptographic service providers at this site as well.

  • Information about the IETF and W3C's work on digital signatures is also available.

Click on Code Listings to view all the code in the examples discussed in this article.


Doug Tidwell is a senior programmer at IBM. He has more than a sixth of a century of programming experience and has been working with markup languages for more than a decade. He was a speaker at the first XML conference in 1997 and has taught XML classes around the world. His job as a Cyber Evangelist is to look busy and to help people use new technologies to solve problems. Doug also writes regularly for IBM's developerWorks site on XML and Web Services topics. Using a pair of zircon-encrusted tweezers, he holds a master's degree in computer science from Vanderbilt University and a bachelor's degree in English from the University of Georgia. He lives in Raleigh, North Carolina, with his wife, cooking teacher Sheri Castle (see her Web site at www.sheri-inc.com), and their daughter Lily.

XSLT

Related Reading

XSLT
By Doug Tidwell


Read Online--Safari
Search this book on Safari:
 

Code Fragments only



Sponsored by:



O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.