WS Security with Apache Axis

How can we use WS Security with Apache Axis? As we saw, VeriSign's WS Security API works on SOAP messages whereas you usually don't work with SOAP messages while using Axis client library or writing a service. If you look at the EchoClient.java file, what you find is a Java Object as argument and a Java Object as return value. Likewise, at the service end, you work with the Java objects. We know that Axis libraries convert the Java objects into SOAP messages at the transmitting end and SOAP messages into Java objects at the receiving end. As WS Security protects SOAP messages, we must have some way of accessing and modifying a SOAP message after the conversion at the transmitting end and before the conversion at the receiving end.

The JAX-RPC handler mechanism provides a solution. One or more handlers, forming a chain of handlers, can be specified to process outgoing and/or incoming messages at the client or the service. At the client, a handler chain must be specified programmatically by making appropriate API calls. At the service end, a handler chain can be specified through the deployment descriptor, at the time of deployment. We talk more about both these forms of handler specification later, in the subsection WS Security Example.

The next subsection outlines how to write JAX-RPC-compliant handlers for WS Security using VeriSign's implementation. The subsequent subsection uses these handlers to augment our example client program EchoClient and the service StringEchoPort1 with WS Security-based message protection. The complete source code of WS Security handlers is in the directory srcjsbookch11wss4axis and the augmented example is in the directory srcjsbookch11ex2. To make it possible to deploy the original as well as modified services simultaneously, we call the modified service StringEchoPort2 and the corresponding source file StringEchoService2.java. Different service names are required to keep them unique within a single instance of the Axis engine.

WS Security Handlers

The implementation class of a JAX-RPC handler must implement the Handler interface defined in the package javax.xml.rpc.handler. This interface has the methods handleRequest() that gets invoked for outgoing messages, handleResponse() which gets invoked for incoming messages, and handleFault() which gets invoked when a SOAP Fault occurs. All of these methods take a MessageContext object as argument and can retrieve the SOAPMessage from it. Besides these methods, it also has the lifecycle methods init() to initialize the handler instance and destroy() to perform the cleanup.

This brief description gives us sufficient background to understand the source code in WSServiceHandler.java, the file defining the service side handler for WS Security processing. The source code for this handler is shown in Listing 11-7. As you can see, the handler assumes that it is configured with details of a keystore and truststore. The keystore has a key entry with the service's private key and certificate and the truststore has certificate entry with the client's certificate. The handler retrieves the configured parameters in its init() method, which gets invoked by Axis engine at the time of initializing the handler, and stores them in private member fields. The mechanism to specify these parameters and their values are different for client and service and illustrated in the subsection WS Security Example.

Listing 11-7. JAX-RPC handler to process request and response at a Web service
// File: wss4axissrcorgjstkwss4axisWSServiceHandler.java
package org.jstk.wss4axis;

import javax.xml.rpc.handler.Handler;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.SOAPMessage;
import org.w3c.dom.Document;
import java.util.Map;

public class WSSServiceHandler implements Handler {
  private String keyStoreFile, keyStoreType, keyStorePassword ,
       keyEntryAlias, keyEntryPassword, trustStoreFile,
      trustStoreType, trustStorePassword, certEntryAlias;

  public boolean handleRequest(MessageContext context) {
    try {
      SOAPMessageContext soapCtx = (SOAPMessageContext)context;
      SOAPMessage soapMsg = soapCtx.getMessage();
      Document doc = SOAPUtility.toDocument(soapMsg);

      WSSUtility.decrypt(doc, keyStoreFile, keyStoreType,
							keyStorePassword, keyEntryAlias, keyEntryPassword);
							WSSUtility.verify(doc, trustStoreFile, trustStoreType,
							trustStorePassword);
							WSSUtility.cleanup(doc);

      soapMsg = SOAPUtility.toSOAPMessage(doc);
      soapCtx.setMessage(soapMsg);
    } catch (Exception e){
        System.err.println("handleRequest -- Exception: " + e);
        return false;
    }
    return true;
  }

  public boolean handleResponse(MessageContext context) {
    try {
      SOAPMessageContext soapCtx = (SOAPMessageContext)context;
      SOAPMessage soapMsg = soapCtx.getMessage();
      Document doc = SOAPUtility.toDocument(soapMsg);

      WSSUtility.sign(doc, keyStoreFile, keyStoreType,
							keyStorePassword, keyEntryAlias, keyEntryPassword);
							WSSUtility.encrypt(doc, trustStoreFile, trustStoreType,
							trustStorePassword, certEntryAlias);

      soapMsg = SOAPUtility.toSOAPMessage(doc);
      soapCtx.setMessage(soapMsg);
    } catch (Exception e){
      System.err.println("handleResponse -- Exception: " + e);
      return false;
    }
    return true;
  }

  public boolean handleFault(MessageContext context) {
    return true;
  }

  public void init(HandlerInfo config) {
    Map configProps = config.getHandlerConfig();
    keyStoreFile = (String)configProps.get("keyStoreFile");
    keyStoreType = (String) configProps.get("keyStoreType");
    keyStorePassword = (String) configProps.get("keyStorePassword");
    keyEntryAlias = (String) configProps.get("keyEntryAlias");
    keyEntryPassword = (String) configProps.get("keyEntryPassword");
    trustStoreFile = (String)configProps.get("trustStoreFile");
    trustStoreType = (String) configProps.get("trustStoreType");
    trustStorePassword = (String) configProps.get("trustStorePassword");
    certEntryAlias = (String) configProps.get("certEntryAlias");
  }
}

This handler decrypts, verifies and cleans up (i.e., removes the Header elements) the incoming request SOAP message in the handleRequest() method and encrypts and signs the outgoing response SOAP message in the handleResponse() method making use of the utility class WSSUtility. This utility class is a simple wrapper over VeriSign's WSSecurity library. The code for each operation in this class, such as sign(), encrypt(), verify(), decrypt(), and so on, is similar to the one we saw in WSSSign.java file, shown in Listing 11-4, and hence is not shown here. You can find source file WSSUtility.java along with other source files in the srcjsbookch11wss4axis subdirectory.

There is one more aspect of this program that needs some discussion. As you must have noticed, what you get in a handler method is a javax.xml.soap.SOAPMessage object and not an org.w3c.dom.Document object. However, WSSecurity library expects a W3C DOM Document object as input. Although both classes represent an XML document (a SOAP message is an XML document), they have their own internal structure and cannot be simply converted from one to another by a typecast. We have delegated this task of conversion to utility class SOAPUtility. The source code of this utility class is not shown here but can be found in the JSTK distribution along with other files referenced in the chapter. This class achieves conversion by serializing the input object into an in-memory byte stream and recreating the desired output object. This way of doing the conversion is quite expensive and can have significant performance impact, especially for large documents.

Moreover, there seems to be no easy way to avoid this performance hit. Essentially, there is an impedance mismatch between what JAX-RPC API provides and what WSSecurity library expects. A epecially written WS Security library that works efficiently for SOAPMessage class could be an option.[2]

[2] This problem has been solved by SAAJ 1.2, a maintenance release of SAAJ made available in April 2003, by adding org.w3c.dom.Node as one of the base interfaces to javax.xml.soap.SOAPMessage.

The client side handler class WSSClientHandler is similar, performing the signing and encryption in handleRequest() and decryption, verification and SOAP header cleanup in handleResponse().

The wss4axis directory, within srcjsbookch11 directory, includes scripts to compile the source files and create a jar file—wss4axis.jar, with all the handler and supporting class files. We use this jar in our next example.

WS Security Example

To make use of WS Security in our previous example, we need to do these things:

1.
Generate keys and certificates for client and service and store them in respective keystore and truststore files.

2.
Modify the client program to setup the client handler and initialize it with client keystore and truststore details.

3.
Modify the service deployment descriptor to specify the service handler and initialize it with service keystore and truststore details.

For the first step, we use the keystore and truststore files client.ks, client.ts, server.ks and server.ts, generated in the section SSL Security for Web Services.

For the second step, let us modify EchoClient.java as shown below. The bold statements indicate additions to the original EchoClient.java program of Listing 11-3:

Service svc = svcFactory.createService(wsdlUrl, svcQName);

Java.util.HashMap cfg = new java.util.HashMap();
							cfg.put("keyStoreFile", "client.ks");
							cfg.put("trustStoreFile", "client.ts");
							cfg.put("certEntryAlias", "serverkey");
							Class hdlrClass = org.jstk.wss4axis.WSSClientHandler.class;
							java.util.List list = svc.getHandlerRegistry().
							getHandlerChain(new QName(nameSpaceUri, portName));
							list.add(new javax.xml.rpc.handler.HandlerInfo(hdlrClass, cfg, null));

Call call = (Call) svc.createCall();

The new statements initialize a HashMap with name value pairs, get the handler chain associated with the Service object, create a HandlerInfo initialized with WSSClientHandler class and the HashMap object and add this HandlerInfo to the handler chain. The Axis library creates a WSSClientHandler object and invokes init() with HandlerInfo as argument, letting the handler initialize itself.

The third step is to modify the deployment descriptor for the service. Let us look at the modified deployment descriptor file deploy.wsdd, with new declarations shown in bold:

<deployment xmlns="http://xml.apache.org/axis/wsdd/"
          xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

 <service name="StringEchoPort2" provider="java:RPC">
  <parameter name=
"wsdlTargetNamespace" value="http://www.pankaj-k.net/jsbook/examples/"/>
  <parameter name="wsdlServiceElement" value="StringEchoService2"/>
  <parameter name="wsdlServicePort" value="StringEchoPort2"/>
  <parameter name="wsdlPortType" value="StringEcho"/>
  <parameter name="scope" value="session"/>
  <parameter name="className" value="StringEchoService2"/>
  <parameter name="allowedMethods" value="*"/>
  <requestFlow>
							<handler type="java:org.apache.axis.handlers.JAXRPCHandler">
							<parameter name="scope" value="session"/>
							<parameter name="className"
							value="org.jstk.wss4axis.WSSServiceHandler"/>
							<parameter name="keyStoreFile"
							value="c:\ch11\ex2\server.ks"/>
							<parameter name="trustStoreFile"
							value="c:\ch11\ex2\server.ts"/>
							<parameter name="certEntryAlias" value="clientkey"/>
							</handler>
							</requestFlow>
							<responseFlow>
							<handler type="java:org.apache.axis.handlers.JAXRPCHandler">
							<parameter name="scope" value="session"/>
							<parameter name="className"
							value="org.jstk.wss4axis.WSSServiceHandler"/>
							<parameter name="keyStoreFile"
							value="c:\ch11\ex2\server.ks"/>
							<parameter name="trustStoreFile"
							value="c:\ch11\ex2\server.ts"/>
							<parameter name="certEntryAlias" value="clientkey"/>
							</handler>
							</responseFlow>
							</service>

</deployment>

You may find it a bit odd that the same parameter names and values need to be specified twice within the deployment descriptor. This is so because Axis allows separate handlers for request and response path. The original Axis handler mechanism, with separate handler classes for request and response, was designed and implemented before JAX-RPC specification was developed. Later on, the JAX-RPC API was added to the existing design.

To deploy the service and run the client program, follow the same sequence of steps as in the previous example. One thing to remember is that before you run the client program, you must copy tsik.jar, wssecurity.jar and wss4axis.jar to the lib directory of Axis deployment and make sure that a JCE Provider with RSA encryption is properly installed in your J2SE setup. Figure 11-5 shows different components of this example.

Figure 11-5. WS Security example setup.


At a high level, we have essentially defined a simple application level message based protocol where the client sends a SOAP message with signed and encrypted Body. The service decrypts and verifies the message, performs the processing and sends back the response SOAP message with signed and encrypted Body. The client decrypts the messages and verifies it. Both client and service have their own private keys that they use for signing and decryption. They also have each other's public key that they use for encryption.

Note that the handlers retrieve the public key of the recipient from the truststore for encryption based on static configuration. This means that these handlers won't work if an endpoint wants to communicate with more than one party with message signing and encryption using different keys. Also, we have used the same private key for signing as well as decryption, something not recommended for high security systems.

One advantage of Web services is that all the interaction takes place by exchanging well-defined XML messages and it is possible to intercept and process these messages en route. Security-related processing is an ideal candidate for such interception at enterprise perimeter and centralized processing, and there are commercial products that perform this kind of processing. VeriSign's XML Trust Gateway, based on TSIK library, is an example of such a commercial product.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.246.47