Using XSS4J for XML Encryption
November 25, 2003
In the previous column of this series, we discussed the usage model of Web Services Security (WSS) and introduced WSS4J, a Java-based API for WSS, a project we will be constructing through this series.
In this column, we start implementing the WSS4J API. The first section of this column discusses the role of different Java components in implementing the WSS4J API. One of the components is XML Security Suite for Java from IBM alphaWorks. The second section will provide a comprehensive tutorial demonstrating how to accomplish XML encryption using the XML Security Suite.
Java Components in WSS4J implementation
We are going to need several Java components to implement our API. We will explain three components: the Java API for XML Processing (JAXP), Java Cryptographic Architecture (JCA), and IBM's XML Security Suite for Java (XSS4J). Future columns of this series will introduce more components as the need arises.
Java API for XML Processing
We will use Java API for XML Processing (JAXP) to fulfill XML processing and authoring requirements of WSS4J. JAXP is an API for XML processing, authoring, and transformations. Using JAXP, Java programmers can develop XML applications that do not rely on a particular XML processing engine.
Xerces-Java (from Apache) is an XML processing engine that includes an implementation of the W3C DOM specification. At the moment, the JAXP reference implementation from Sun uses Xerces as its default XML parser.
It is not feasible to cover the details of low level XML processing with JAXP in this column. Please refer to the resources section for more details of JAXP.
Java Cryptographic Architecture (JCA) and Java Cryptographic Extension (JCE)
WSS4J will depend on JCA and JCE for low level cryptographic support. JCA is the basic cryptographic architecture in Java, and JCE is an extensibility mechanism for JCA. JCE allows cryptographic implementations (called providers) from third party vendors to be plugged into JCA. This allows application developers to rely only on the JCA/JCE API for application development without worrying about vendor-specific interfaces. Refer to the resources section for further details of JCA/JCE. JDK 1.4 includes a reference implementation of JCA along with a default JCE provider from Sun. We are going to demonstrate the use of both symmetric and asymmetric keys for XML encryption in this and the next column. (Recall our brief discussion on symmetric and asymmetric keys in the second article of my series on Web Services Security.) Perhaps the most popular encryption algorithm that uses asymmetric keys is RSA, which we are going to use for our XML encryption demo in this article.
Unfortunately, the default JCE provider from Sun that ships with JDK 1.4 does not support RSA. You need to download a third party provider that supports RSA. A good choice is to use the open source implementation from Bouncy Castle. A tutorial at IBM developerWorks explains how to use Bouncy Castle's provider with your JDK 1.4.
Note an important point about the use of keys in WSS4J. The Web Services Security specification by OASIS does not dictate or define the process of distributing cryptographic keys. This means that the WSS specification does not answer key management questions like "how will I fetch the public key of my business partner?" or "how can I map a key name to the actual key?" Key management and distribution is a separate topic and therefore not the focus of the WSS specification. You can use any key management and distribution mechanism (like PGP or XKMS, for instance) with WSS. That's the reason our WSS4J implementation will also not care about the source of a particular key. We'll rely on the application to provide us the required key (the public key of my business partner, for example).
We are going to use the Java key store mechanism to store keys. This mechanism allows storing keys privately as normal files on your hard disk. Even if someone has access to a key store file, he will not be able to hack the key unless he knows the password of your key in the key store.
As we said earlier, our WSS4J implementation does not care where a particular key came from. So for the sake of demonstration, you can generate a key using the following command line use of the Java keytool utility:
<JAVA_HOME>\bin\keytool -genkey -alias myTourOperatorKey -keyalg RSA -dname "CN=Bilal Siddiqui, OU=HotelBookings, O=myTourOperator, C=PK" -keypass myKeyPass -keystore tourOperatorRSAKey -storepass myKeyStorePass
This command generates a key store file named "tourOperatorRSAKey", which contains an RSA public-private key pair whose alias (name) is "myTourOperatorKey", it belongs to the "HotelBookings" business unit of a company called "myTourOperator". The password of the key is "myKeyPass", while the password of the key store is "myKeyStorePass".
You may generate this key using the command line as given above, as you'll shortly need this key. Or you may use the key that I have included in the sample code download of this article.
XML Security Suite for Java (XSS4J) from IBM alphaWorks
XSS4J implements XML signature and XML encryption specifications. We will use XSS4J for low level XML signature and XML encryption support while implementing the WSS4J API. You can say that WSS4J will use XSS4J in the same manner as the WSS specification by OASIS makes use of the low level functionality of XML Signature and XML Encryptions specifications by W3C (refer to the second article of my series on Web Services Security for more details).
You can download a copy of XSS4J from alphaWorks' site. We tested all code of this column with the version of XSS4J that was released on January 27, 2003 (latest for now) and therefore called xss4j-20030127.
Apart from the JDK, JCE, and Xerces, XSS4J also requires one additional component called International Components for Unicode for Java (ICU4J). XSS4J uses ICU4J for some text normalization algorithms. You can download ICU4J from here. We tried the code of this column with version 2.4 of ICU4J. Later versions of ICU4J are not directly compatible with the current XSS4J implementation. So use ICU4J version 2.4 to try the code of this column. ICU4J does not require any special installation steps. Just include the icu4j.jar file in your classpath.
XSS4J plays a vital role in our WSS4J implementation. Therefore, the rest of this column is a step-by-step tutorial that demonstrates the use of XSS4J for XML encryption. Once we know how to use XSS4J, we will devise and implement a strategy to integrate XSS4J in our WSS4J implementation.
Using XSS4J for XML Encryption
Have a look at the XMLEncryptionSample
class of Listing 1, which
implements a four step strategy that you can follow to XML-encrypt an element using
XSS4J.
We have used a very simple sequence of operations in Listing 1 to
demonstrate the process of XML encryption. We have hard coded different file names
in Listing 1 just for
the sake of simplicity.
The first step is to author the complete plain text form of the XML data, part or whole of which you wish to XML encrypt. In actual real world applications, the plain text form of the XML data may come from many sources: the result of some server side processing, or it may be available from some XML data repository. In any case, in order to use XSS4J to encrypt whole or part of any XML resource, you first need to load the plain text form into a W3C DOM object.
Have a look at Listing 1, where we have authored a simple SOAP message with an empty header and a
SOAP body with a GetSpecialDiscountedBookingForPartnersResponse
child element.
We are assuming that a hotel's business logic server is authoring this SOAP message
for a
special customer, i.e. the tour operator who is the owner of the key named
"myTourOperatorKey" that we generated using the Java keytool utility. The tour operator
is
entitled to a special, confidential discount in room reservations. Therefore, the
hotel
would like to keep the discount confidential and therefore will encrypt the entire
GetSpecialDiscountedBookingForPartnersResponse
element of the SOAP body.
Look at Listing
2, which shows the SOAP message that is a result of XML authoring in step 1 of Listing 1. Notice
from step 1 of Listing 1 that the GetSpecialDiscountedBookingForPartnersResponse
element of Listing
2 corresponds to the Java object named hotelBookingResponse
in Listing 1. This is
the element that we are shortly going to encrypt using XSS4J.
The XML encryption classes of XSS4J work on the idea of an encryption template. Therefore,
the second step is to load an encryption template into a DOM object. The encryption
template
will specify complete information about the EncryptedData
element that will
result after encrypting the GetSpecialDiscountedBookingForPartnersResponse
element of Listing
2.
For example, look at Listing 3, which
shows an EncryptedData
element along with its child nodes. Notice that the
EncryptedData
element of Listing 3 is
itself a complete XML encrypted structure, except just one thing: The two
CipherValue
tags in Listing 3 are
empty, i.e. they don't contain any cipher data. That's because Listing 3 is a
template. It shows the structure and provides complete information about the XML encrypted
structure, but does not contain the actual cipher data. This means later steps in
our
XML-encryption strategy will just fill in the cipher values inside the
CipherValue
tags.
Try to gather what bits of information the encryption template of Listing 3 provides:
- The Id attribute of the
EncryptedData
element specifies an identifier for theEncryptedData
element. - The
Type
attribute of theEncryptedData
element specifies the type of encryption (i.e. encrypt an XML element). - The
Algorithm
attribute of theEncryptionMethod
element specifies the encryption algorithm that we are going to use during our XML encryption. - The
KeyInfo
element (which belongs to the XMLDS namespace) specifies the key used for encryption. Listing 3 contains twoKeyInfo
elements. The firstKeyInfo
element is the direct child of theEncryptedData
element and wraps anEncryptedKey
element, which in turn contains the secondKeyInfo
element. TheEncryptedKey
element inside the outerKeyInfo
element specifies that the outerKeyInfo
element actually wraps an encrypted key. The innerKeyInfo
element specifies the key used to produce the encrypted key. Notice that the innerKeyInfo
element has a child namedKeyName
, which wraps a string "myTourOperatorKey". This means that the hotel will use the public key of the tour operator to encrypt a randomly generated key. The outerKeyInfo
element is actually a symmetric key and the innerKeyInfo
element is an asymmetric key. You can use this nested hierarchy ofKeyInfo
-EncryptedKey
-KeyInfo
elements whenever you wish to use an asymmetric key (the public key of the recipient of the XML encrypted message) to encrypt a symmetric key (normally a randomly generated number).
Step 2 in Listing
1 shows the Java code that loads the XML structure of Listing 3 into a
DOM object. We saved Listing 3 as an XML file named "EncryptionTemplate.xml" in the same directory from
where we compiled the "XMLEncryptionSample
" class of Listing 1. The
lines of Java code in step 2 of Listing 1 will
load the complete EncryptedData
structure of Listing 3 (the
"EncryptionTemplate.xml" file) into an element node named encryptionTemplate
.
We will need the encryptionTemplate
node later in step 4.
The source code download of this column contains the encryption template of Listing 3 as "EncryptionTemplate.xml".
Step 3: The third step is to instantiate a KeyInfoResolver
object.
KeyInfoResolver
is an XSS4J interface. This interface represents a mechanism,
which resolves a KeyInfo
element to a key value. The
KeyInfoResolver
object takes a KeyInfo
element along with all
information required to resolve the value of the key. It then resolves the
KeyInfo
element to its value.
For example, let's assume the public key corresponding to the inner KeyInfo
element (whose name is "myTourOperatorKey") sits inside a Java key store. Therefore,
you
will need to tell the password of the key to the KeyInfoResolver
object, so
that the KeyInfoResolver
object could fetch the key from the store.
The KeyInfoResolver
interface contains an important method name
resolve()
, which resolves a KeyInfo
element to the actual key.
We don't need to call this method anywhere. The XSS4J framework will internally fetch
the
KeyInfo
element from the encryption template and then pass on the
KeyInfo
element to the KeyInfoResolver.resolve()
method call.
You will need to implement the KeyInfoResolver
interface according to the key
resolution mechanism that you want to use in your application. For example, if you
want to
use the encryption template of Listing 3 in your
application, your key resolution mechanism should be able to do the following:
- Read name of the asymmetric key from the KeyName element of the inner
KeyInfo
element. - Fetch the actual key corresponding to the name of the asymmetric key. The mechanism used to fetch the key depends upon how the key is stored. For example, if the key is stored in a Java key store, you will need to access the correct key store, provide the key access password, and fetch the key.
- Generate a random number. The random number is the key that you will use to encrypt the plain text data.
- Encrypt the random number with the asymmetric key you obtained from the key store.
You
will wrap the encrypted form of the random number inside the
CipherValue
tag (which is the grand child of theEncryptedKey
element in Listing 3).
You need to implement the above stated four step strategy in the resolve()
method of the KeyInfoResolver
implementation corresponding to the encryption
template of your choice. Naturally you will need a different implementation of the
KeyInfoResolver
interface for every type of KeyInfo
structure
that you wish to use in your XML encryption application. For example, the XSS4J
implementation comes with a class named KeyStoreKeyInfoResolver
, which is
capable of performing the four steps described above (including reading from Java
key
stores).
Therefore, you just need to instantiate the correct KeyInfoResolver
object.
Once you have the required object, you will simply tell the XSS4J framework to use
the
object for KeyInfo
-to-key mapping. Let's see how we do this in our
XMLEncryptionSample
application. Have a look at the code following the "Step
3" comment line in Listing 1:
- First you have to load a key store into a
KeyStore
object. You call theKeyStore.getInstance()
static method to instantiate aKeyStore
object, then you create aFile
object corresponding to the key store file that you want to use, then you read the contents of the file into an input stream, and finally you load the input stream into theKeyStore
object. - Next you create a new
KeyStoreKeyInfoResolver
object by calling its constructor. The constructor takes theKeyStore
object as a parameter. - Then you create an instance of the
AlgorithmFactoryExtn
class, which is similar to the factory classes that Java programmers are quite familiar with. The XSS4J framework will internally use this factory object to instantiate algorithm specific classes corresponding to the algorithms that the encryption template specifies. - Next you call the
setAlgorithmFactory()
method of theKeyStoreKeyInfoResolver
object. This method sets the factory object for use by theKeyStoreKeyInfoResolver
object during the key resolution process. - Next we have to tell the password of the key to the
KeyStoreKeyInfoResolver
object. The JavaKeyStore
object will not let the keyKeyStoreKeyInfoResolver
object to fetch the key unless it knows the correct password. Therefore, we have to call theputAliasAndPassword()
method of theKeyStoreKeyInfoResolver
class and pass on the alias (name) and password of the tour operator's key along with the method call. TheputAliasAndPassword()
method will store the alias and the password of the key in memory inside theKeyStoreKeyInfoResolver
object. - You can use the
KeyStoreKeyInfoResolver
class for both encryption and decryption, but the behavior of this class is different while encrypting and decrypting. Here we are only demonstrating encryption (we will have a separate column to demonstrate and explain decryption in our WSS4J implementation). So, you have to tell theKeyStoreKeyInfoResolver
object that you wish to use this object for encryption. Therefore, you have to call thesetOperationMode()
method of theKeyStoreKeyInfoResolver
object passingKeyInfoResolver.ENCRYPT_MODE
as its value (which means we are using this object for encryption).
Now the KeyStoreKeyInfoResolver
object is all set, ready to be used in step 4
of our XML encryption strategy.
In this step, we prepare an EncryptionContext
object. The
EncryptionContext
class forms the core of XML encryption framework in XSS4J.
This is the place where we put the pieces together and perform the actual encryption:
- First instantiate the
EncryptionContext
class. The constructor does not take any parameter. - Next we call the
setAlgorithmFactory()
method of theEncryptionContext
class to specify which algorithm factory we wish to use in our encryption application. - Next we call the
setKeyInfoResolver()
method of theEncryptionContext
class to set the key resolver object to be used to resolve the key value from aKeyInfo
element. - Next we call the
setData()
method of theEncryptionContext
class and pass the element node that we want to XML encrypt along with the method call (in our case, we want to encrypt thehotelBookingResponse
element node, which corresponds to theGetSpecialDiscountedBookingForPartnersResponse
of Listing 2). - Next we call the
EncryptionContext.setEncryptedType()
method, which is meant to specify encryption details forEncryptionContext
object. The setEncryptedType() method takes four parameters. The first parameter specifies the encryption template that we want to use (e.g. theEncryptedData
element of Listing 3, which we loaded into theencryptionTemplate
element node in step 2). The second parameter specifies the type of encryption, the third parameter specifies theKeyInfo
element, and the fourth element specifies theEncryptionMethod
element that we wish to use during XML encryption. Notice that the encryption template of Listing 3 already contains all the information about our required XML encryption (the type of encryption, the encryption method, and theKeyInfo
element). Therefore, we just need to pass on theencryptionTemplate
element as the first parameter and can pass null for rest of the three parameters. - We are all set to do the actual encryption. The
EncryptionContext.encrypt()
method encrypts theGetSpecialDiscountedBookingForPartnersResponse
element of Listing 1 and produces anEncryptedData
element. TheEncryptionContext.replace()
method replaces the plain textGetSpecialDiscountedBookingForPartnersResponse
element with theEncryptedData
element.
Listing 4 shows
the final XML encrypted version of Listing 2, which
was produced by the Java file of Listing 1. You can
compare Listings 2 and 4. You will find that the
GetSpecialDiscountedBookingForPartnersResponse
element in Listing 2 has been
replaced by an EncryptedData
element in Listing 4.
Conclusion
We have learned about the major components required to implement the WSS4J API. We
have
also learned about the use of XSS4J for XML encryption. Next time, we'll use XSS4J
and
implement the WSSMessage.encryptElement()
method of our WSS4J API that we
presented in Listing 2 of the first column of this
series.
Resources
|