Menu

Implementing XML Encryption in Java

April 21, 2004

Bilal Siddiqui

In the first column of this series, we presented a high-level WSS4J API, which we wish to implement in this series of columns. In the second column, we discussed the various Java components we are going to use in implementing the WSS4J API. The most important component is XML Security Suite for Java (XSS4J) from IBM alphaWorks. The second column also demonstrated how to use XSS4J for XML encryption.

In this column, we will use the XSS4J concepts discussed in the previous column to implement the XML encryption features in our WSS4J API.

Before we start the discussion, we would like to point out a name clash issue. There's an open source Web Services Security project at SourceForge.net (recently moved to Apache's web site) that shares the same WSS4J name. The WSS4J API we are developing in this series of columns is purely for educational purposes and aims at demonstrating the use of WSS in Java applications. The WSS4J open source project at SourceForge (now at Apache) is an independent project and is not part of our effort.

A WSS Message Before and After XML Encryption

We are going to add support for both symmetric and asymmetric keys in our WSS4J API. Listing 1 shows a simple SOAP message we will use as sample input to our XML encryption process. We will encrypt a portion of Listing 1 to demonstrate the working of our WSS4J API.

We are going to perform three types of XML encryption. Listings 2, 3, and 4 are three WSS messages that show the outcome of our encryption process. Listings 2, 3, and 4 are slightly different from each other. As we have already covered the details of WSS format in our Web Services Security series (especially in Part 4), we will not go into the details of WSS format. Here we will only highlight the differences between Listings 2, 3, and 4:

Listing 2 shows the following:

  1. If you look at the contents of the SOAP body in Listings 1 and 2, you will see that the first Parameter element of Listing 1 has been replaced by an EncryptedData element in Listing 2. The EncryptedData element contains the encrypted form of the Parameter element. The encryption algorithm used for this XML encryption is triple DES as specified by the EncryptionMethod child of the EncryptedData element.
  2. The EncryptedData element in Listing 2 does not contain any key information. The corresponding key information resides inside the wsse:Security element in the SOAP header, which contains an EncryptedKey element. The EncryptedKey element wraps a symmetric triple DES secret key in encrypted form. Notice from Listing 2 that the EncryptionMethod used to produce the EncryptedKey is RSA and the key used for this RSA encryption is the tour operator's public key. We covered the details of such encrypted keys in Step 2 of the Using XSS4J for XML Encryption section in the second column.
  3. How do we relate an EncryptedData element with its corresponding key used for encryption? Look at the ReferenceList element inside the EncryptedKey element of Listing 2. The ReferenceList element has a child element named DataReference, whose URI attribute points to the EncryptedData element we produced using the encrypted key.

The EncryptedKey element is a security token in WSS terms. Although the XML encryption specification simply calls it an encryption key, the WSS specification talks about abstract (or more general) tokens. This means we can treat cryptographic keys as a type of security token. Therefore, while implementing WSS, we will treat an EncryptedKey structure as a type of token and call it an encrypted key token.

Now look at Listing 3, which is a very slightly modified form of Listing 2. The only difference between Listings 2 and 3 is that the EncryptedData element in Listing 3 contains a KeyInfo element, which wraps a SecurityTokenReference element. The ecurityTokenReference element has a Reference child element, whose URI attribute refers to the wsu:Id of the EncryptedKey that was used to produce the EncryptedData.

If there are more than one EncryptedData and EncryptedKey elements in a WSS message, then it is computationally more expensive to find the EncryptedKey corresponding to a particular EncryptedData structure in Listing 2 as compared to Listing 3. That's because in Listing 2 you have to look into the ReferenceList elements of several EncryptedKey structures until you find the required EncryptedKey structure. On the other hand, if you have to find the EncryptedData structure corresponding to a particular EncryptedKey Listing 3, you can read the URI attribute value of the Reference element and jump directly to the required EncryptedKey structure.

We will call Listing 3 as an encrypted key token with cross reference.

Now look at Listing 4, which shows the following:

  1. The EncryptedData element in Listing 4 contains a KeyName element, which specifies the name of the key used for encryption. Here we are assuming that the recipient of this message can locate the actual key corresponding to this name. Therefore, we are not concerned about how an application will map a key name to the actual key.
  2. There's no EncryptedKey element inside the wsse:Security header. Instead we just have a ReferenceList element that refers to the EncryptedData element inside the SOAP body. This type of ReferenceList element does not specify the key used for producing the EncryptedData element. This ReferenceList only specifies the order in which different actions take place in the header (e.g. if you encrypt and then sign a portion of a message, the ds:Signature element will appear before the ReferenceList element. For details please refer to the discussion accompanying Listings 7 and 8 in the fourth article of my Web Services Security series).

We will call this ReferenceList element a reference list only token.

We will implement the first two types of tokens (encrypted key token and encrypted key token with cross reference) in this column and the third type (reference list only token) in the next column of this series.

Implementing the Encrypted Key Token

Now let's see how to implement and use an encrypted key token. In order to implement and use an encrypted key token, we'll need to do the following:

  1. Implement the Token interface we presented in Listing 3 of the first column of this series. The Token interface contains several methods and we'll need to implement each of them in a Java class named EncryptedKeyToken.
  2. Implement the constructor of the WSSMessage class we introduced in Listing 2 of the first column of this series.
  3. Implement the encryptElement() and encryptElementWithXPath() methods of the WSSMessage class.

Let's do these steps one by one.

Implementing the Token Interface

Have a look at the EncryptedKeyToken class shown in Listing 5. This class implements the Token interface and is responsible for performing the second, third, and fourth steps of using XSS4J for XML encryption that we described in the second column of this series. The final result of using an encrypted key token is what we have already seen in Listing 2.

Have a look at the EncryptedKeyToken constructor, which takes the following parameters:

  1. keyStoreFileName is the full path name of a Java key store file. The key store file contains the public key of the recipient of the WSS message.
  2. keyStorePassword is the string representation of the password that we need in order to access the key store.
  3. KeyName is the string representing the name of the recipient's public key that is stored in the key store.
  4. wsuKeyID is the wsu:Id of the encrypted key token. The token will author this IDin the token's XML format.
  5. wssMessage is the parent WSSMessage object that wraps this token.

The constructor first stores the parent WSSMessage object, the key token ID, and the key name in their corresponding fields. It then loads the encryption template in a DOM object. We demonstrated the use of an encryption template in Listing 3 of the second column. Listing 6 shows the encryption template that our EncryptedKeyToken will use. Listing 6 is slightly different from the encryption template we introduced in Listing 3 of the second column. We will explain the difference between the two templates later.

The EncryptedKeyToken constructor then opens the key store file, loads it into an input stream, and loads the input stream into a Java KeyStore object. It then instantiates a KeyStoreKeyInfoResolver object and calls its setAlgorithmFactory() method to set the algorithm factory to be used during XML encryption. Recall that we have covered the details of these steps in the Using XSS4J for XML Encryption section of the second column of this series.

Notice that the EncryptedKeyToken constructor cannot access the key in the key store. That's because we have not supplied the password required to access the key residing inside the Java KeyStore object. The application that instantiates the EncryptedKeyToken object will also call the EncryptedKeyToken.setSecret() method to specify the password to access the key.

The setSecret() method (Listing 5) takes the key access password in byte array form, converts the byte array to a character array, and supplies the character array to the KeyStoreKeyInfoResolver. The KeyStoreKeyInfoResolver will internally fetch the required key.

Now have a look at the encrypt() method in Listing 5. This method authors the encrypted form (an EncryptedData structure) of the plain text element that we want to encrypt. The encrypt() method takes three parameters:

  1. wsuEncryptedElementID: The wsu:Id of the EncryptedData structure that the encrypt() method will author.
  2. encryptionAlgo: The algorithm to be sued for encryption.
  3. elementWantToEncrypt: The DOM element representation of the XML element that we want to encrypt.

The encrypt() method performs the following eight steps (marked with comments in Listing 5):

Step 1: KeyStoreKeyInfoResolver can do both encryption and decryption. So the first step is to set the mode of operation of the key resolver to "encryption."

Step 2: If you look at the encryption template we are using (Listing 6), you will find that there is a ReferenceList child of the EncryptedKey element. This ReferenceList element was not present in the encryption template we introduced in Listing 3 of the second column. We need this ReferenceList element in the EncryptedKey because the final objective of implementing the EncryptedKeyToken class is to author the structure shown in Listing 2 (which contains a ReferenceList element).

Notice from Listing 2 that the DataReference child of the ReferenceList element refers to the wsu:Id of the EncryptedKey that was produced using the EncryptedKey. Therefore, our second step is to add a URI attribute to the DataReference element. The URI attribute refers to the wsu:Id of the EncryptedData that we are about to author.

Step 3: The third step is to author the wsu:Id of the EncryptedData that we will shortly author. At this moment the encryption template looks like as shown in Listing 7.

Step 4: We also need to tell that it is the encryption algorithm that we want to use. For this purpose, our fourth step is to set the value of the Algorithm attribute of the EncryptionMethod element in Listing 7.

Step 5: The EncryptedKey element has its own wsu:Id. So the fifth step is to author the wsu:Id of the EncryptedKey element.

Now our template is all set and looks like what's shown in Listing 8.

Step 6: The next step is to XML encrypt the input DOM element using the procedure we described while discussing step 4 of KeyInfo and encrypt it using the Using XSS4J for XML Encryption section of the second article of this series.

Step 7: Assuming we encrypted the first Parameter element of Listing 1, it will look like Listing 9 after encryption. If you compare Listing 2 and Listing 9, you will find that the EncryptedData element in Listing 9 is not the required form that we were trying to author (see EncryptedData in Listing 2).

The main difference between the EncryptedData elements in Listing 2 and Listing 9 is that the EncryptedData element in Listing 9 contains a KeyInfo element, which in turn contains the EncryptedKey element. In Listing 2, the same EncryptedKey element resides in the SOAP header.

Therefore, the seventh step is to copy the EncryptedKey element and store it as a DOM element object named tokenElement.

Step 8: Now we remove the entire KeyInfo child of the EncryptedData element, so that the EncryptedData element in Listing 10 looks exactly like the EncryptedData in Listing 2.

The EncryptedKeyToken class contains another public method named getXMLString(), which returns the string form of the encrypted key token. Notice that in step 7 above we stored the EncryptedKey in an object named tokenElement. The getXMLString() method simply returns the string form of the tokenElement object.

Now let's see how the WSSMessage class will use the EncryptedKeyToken class to author a WSS message containing an encrypted key token.

Implementing the WSSMessage Constructor

Listing 11 shows the WSSMessage implementation. Have a look at the WSSMessage constructor. The constructor is very simple. It just takes a plain text SOAP message and loads it into a DOM object. For the moment, our WSSMessage class constructor is very humble. It will become more complex in later columns as our WSSMessage class grows in functionality.

Implementing Encryption Methods in the WSSMessage Class

Note that the WSSMessage class, as described in the first column of this series, represents the whole WSS message (as opposed to the EncryptedKeyToken class, which represents only a single token). Therefore, the encrypt() method of the EncryptedKeyToken class only handles one type of token, while the encryption methods in the WSSMessage class should be able to handle different types of tokens.

Look at the encryptElement() and encryptElementWithXPath() methods in Listing 11. Each method takes four parameters as already described in the first column. Each of the two methods first locates the element to be encrypted and then calls a method named encrypt(). The encrypt() method is private and performs the following tasks:

  1. It checks what type of token is being used. In our case, we are using an encrypted key token.
  2. If the token being used is an encrypted key token, the encrypt() element simply calls the token.encrypt() method.
  3. The encrypt() method works according to the eight-step procedure described above and encrypts the plain text element.
  4. Next, the WSSMessage.encrypt() method calls the getXMLString() method of the token object. The getXMLString() method returns the string representation of the encrypted key token.
  5. After fetching the string representation of the token, the WSSMessage.encrypt() method places the token inside the Security header, which resides inside the SOAP header.

Listing 12 shows the final result when the first Parameter element of Listing 1 is encrypted using an encrypted key token.

In a similar manner the WSSMessage.encrypt() method will handle different types of tokens.

Using our WSSMessage Class in an XML Encryption Application

The final question is how an application will use our WSSMessage and EncryptedKeyToken classes. Have a look at Listing 13, which shows the main() method of a WSSEncryptionSample class. You can see the following steps in the main() method of Listing 13, which demonstrates how easy and simple it is to use our WSS4J API for encryption:

  1. The main() method first instantiates a WSSMessage object, passing a plain text SOAP message string to the WSSMessage constructor.
  2. It then instantiates an EncryptedKeyToken object and then calls its setSecret() method passing the key access password along with the method invocation call.
  3. The third step is to call the encryptElement() method of the WSSMessage object, passing the token as well as the wsu:Id of the plain text element to be encrypted.

Implementing the Encrypted Key Token with a Cross Reference

This section demonstrates how we can easily enhance our EncryptedKeyToken to implement support for the encrypted key token with cross reference that we introduced in Listing 3. This needs the following steps:

  1. Write a Java class named EncryptedKeyTokenWithCrossReference, which extends the EncryptedKeyToken class. We have shown the EncryptedKeyTokenWithCrossReference class in Listing 14.
  2. The EncryptedKeyTokenWithCrossReference constructor takes the same parameters as the EncryptedKeyToken constructor. Since we don't need any additional functionality in the EncryptedKeyTokenWithCrossReference constructor, the EncryptedKeyTokenWithCrossReference constructor simply calls the super's constructor.
  3. The encrypt() method in the EncryptedKeyTokenWithCrossReference class needs just one extra step as compared to the encrypt() method in the EncryptedKeyToken class. The extra step is to add the SecurityTokenReference structure (that we described in Listing 3) wrapped inside a KeyInfo element. Therefore, in the encrypt() method of the EncryptedKeyTokenWithCrossReference class, we simply call the super's encrypt() method and then add a SecurityTokenReference element.

We have demonstrated the use of EncryptedKey structure in WSS messages. Next time, we will start by implementing the reference list token that we introduced in Listing 3. We will also discuss the use of XSS4J for XML digital signatures in the next column of this series.

Resources

Download the source code zip of this article. The zip contains the source as well as the compiled form of the code. The zip also contains a readme.txt file that will help you compile and run the code of this article.

Read the first and second column of this series, which introduced the WSS4J API. Also check out the first, second, third, and fourth parts of the series of article on Web Services Security from the same author.

Check out the official JAXP and JCA/JCE pages at Java.Sun.com.

Visit BouncyCastle.org to download the latest version of their provider. We tested the code of this article with the bcprov-jdk14-120.jar file installed as a provider.

Download your copy of XSS4J from IBM alphaWorks web site.

Download ICU4J from here.

This tutorial at IBM developerWorks will help you learn the basics of cryptography in Java.

Read this article at OnJava.com that introduces JAXP. Also check out this page about JAXP.

Check out these articles on XML Encryption (Part 1 and Part 2) by the same author.