In Part 1 of this series, I described how the Web Services Gateway can be used to route inbound Web services requests based on information that the requestor places in the header of a SOAP message. The example uses a simple character string to indicate the requestor’s subscription level for a service and showed how messages can be routed to service implementations with QoS characteristics that match the level indicated in the message. In this paper, I build on the example in my previous paper, adding the following requirements:
- The subscription token uniquely identifies the subscriber.
- The token format chosen exhibits the property that only the service providers can issue the tokens; in other words, non-subscribers cannot create tokens and masquerade as subscribers.
Design the header element for the subscription token
You might recall from the first paper in this series that there are several aspects of SOAP headers that a JAX-RPC handler must consider when it is looking for headers that it should process. Here is a brief summary:Table 1: Aspects of SOAP header processing
| Header element aspect | Significance |
| The role (Actor) to which the header element is assigned | The handler should examine only those headers which have an actor value matching one of the JAX-RPC handler's roles. |
| The header element's qualified name | Handlers look for header elements that they recognize and understand how to process by examining the qualified name of each header element assigned to their role. |
| The mustunderstand attribute's value | The handler must recognize and understand any headers assigned to a role held by a JAX-RPC handler if this attribute is set to “1.” Otherwise the JAX-RPC handler must avoid any processing of the inbound message (including the processing of headers it does understand) and emit a SOAP Fault. |
While the above illustrates that there are many things to consider when processing a SOAP message, the qualified name of a header element is the primary means of recognizing it and invoking the processing rules associated with it. Consider though that requirements are not static; as an application evolves it might be necessary to alter the format of a header in ways that are not compatible with earlier versions. The next section of this paper covers some techniques that you can use to allow for requirements change in the future.
Create SOAP headers that support versioning
Systems evolve, and it is prudent to assume that our subscription header token might evolve over time; so this brings you to the first design decision you must make -- what approach should I use to allow for the evolution of the subscription token?Approach 1: Use a unique header name for each iteration of the subscription token
Since the qualified header name is the primary means of recognizing the header, you could use a unique qualified name to identify each version of the subscription token format as the token evolves. You can do this by introducing an iteration identifier in the namespace of the subscription header element. Here are some examples:
xmlns="http://com.videos-r-us/v1/membership" xmlns="http://com.videos-r-us/2004-10-15/membership" |
In the first example a simple sequence numbering scheme is employed; the second uses a date in a similar fashion to what is commonly seen for versions of important standards like XML Schema and WSDL.
The draw-back to using a unique namespace for headers is that a JAX-RPC handler might not recognize the header at all if it is not searching for that exact name. This means that deployment errors such as deploying the wrong version of a handler are more difficult to detect; the header might simply be ignored leaving the administrator to figure out why. Problem determination could be simplified if the mustunderstand attribute of the header is set to "1." Assuming that the Actor at which the header is targeted matches that of the handler, a well-behaved handler will emit a SOAP fault for an unrecognized header and draw attention to the problem. Unfortunately this also introduces processing semantics that might not be appropriate for all situations; for example, it might be desirable to make the header processing optional for some circumstances. In a nutshell, the mustunderstand attribute is not intended to support header versioning; it is intended to alter the processing semantics for a given message.
Approach 2: Use a version qualifier inside the header element
A second approach to header versioning uses a version value inside the header, Listing 1 shows:
Listing 1: Embedding a version element inside a SOAP header
<shns:subscriptionToken actor="router" xmlns:shns= "http://com.videos-r-us/membership"> <shns:headerVersion>1</shns:headerVersion> </shns:subscriptionToken> |
When using this approach, handlers always recognize the subscription token header because the qualified name does not change. They can also examine the version identifier and determine if they understand the header format. If they do not understand the format they can either:
- Do nothing if mustunderstand is set to "0" (the default) or
- Emit a SOAP fault containing an informative description of the problem if mustunderstand is set to "1."
The drawback with this approach is that the JAX-RPC handler must locate and examine the version identifier each time it sees a subscriptionToken header. It cannot assume that the header is understood simply because the qualified name was a match. So this represents extra processing overhead for each request.
The example in this paper uses a version element inside the header to allow for different header formats to evolve over time.
In the previous article, the subscribed QoS was self-evident when looking at the routing token; Gold subscribers inserted "GoldService" as the subscription token, Silver subscribers inserted "SilverService," and so on. With the addition of the requirement that each token uniquely identify the subscriber, some other means must be used to establish the target service implementation for the message. Essentially, the routing table must be modified to associate a subscriber’s identity with the subscription they have purchased and to handle requests accordingly. To satisfy the new requirements, provider-signed X509 certificatesare used as the subscription token. X509 certificates have several properties that make them attractive for this purpose:
- Signature verification, certificate revocation lists (CRLs), and certificate life-span can be used to validate the subscription (token).
- Provider-signed certificates eliminate the possibility that non-subscribers can create (forge) tokens.
- Changes in the subscription level can be made without impacting the token that is exchanged in the message. There are no client-side changes when the requestor negotiates a different subscription level. The token carries the requestor's identity; the provider manages the subscription level.
The approach taken in this paper assumes that the Web service provider has decided to act as a private Certifying Authority (CA) and will issue a certificate to each subscriber that has been signed with the provider's private key.
Since serialized X509 certificates are binary blobs of data, you can't embed them directly into the SOAP message header. They must be rendered in XML-compatible form. A common means of embedding binary information in a SOAP request is to apply BASE64 encoding to the information, rendering it XML compatible. For the example in this paper, the requestor’s handler applies BASE64 encoding to the token prior to inserting it into the routing header.
Listing 2 in this sidefile shows an example of a subscription header that meets the requirements identified.
The example does not identify that the token is a BASE64-encoded X509 certificate. That knowledge is implicit in the implementations of the JAX-RPC handlers deployed at the requestor and in the Web services Gateway. If there is a future requirement to support various token types, information about the type of token provided can be added to the header along with a related version change in the header format.
Now that you know the format of the subscription header and the nature of the subscription token, the next step is to identify some property of the token that is unique among subscribers to use as the basis for the subscription mapping. Conveniently, the certificate subject's identity is represented as a distinguished name (DN). A DN is a set of attribute-value pairs that represent a path through an X.500 directory information tree. Here is an example:
CN=Michael S. Ellis, O=IBM Canada Ltd., OU=SWG, L=Ottawa, ST=Ontario, C=CA |
RFC1485: A String Representation of Distinguished Names identifies a set of DN components (see Table 2). In order to reach a high degree of interoperability between X.500 and LDAP names, you should restrict yourself to these components when assigning DNs. This interoperability is desirable since many organizations use LDAP directories when managing user identities.
Table 2: Standardized DN Keywords
| LDAP short name | DN Attribute | Description |
| CN | commonName | Individual’s name, Server name or IP address |
| O | OrganizationName | Company name |
| OU | OrganizationalUnitName | Business unit name, identifier |
| L | LocalityName | Name of the community in which the subject resides |
| ST | StateOrProvinceName | Name of the state or province where the subject resides within the country of origin |
| C | countryName | Name of the subjects’ country of origin |
For the sake of simplicity, I use the CommonName attribute as the basis for subscription mapping in this article.
The processing flow for the X509 subscription token
The processing requirements are not that much different from the example in my previous paper. Here is a summary:
- The requestor inserts a BASE64-encoded X509 certificate into the header of his message and sends the message to the Gateway Service endpoint.
- The subscription token handler in the Gateway identifies the subscription header.
- The handler extracts and decodes the encoded token to recreate the
X509Certificateinstance. - Standard X509 certificate processing occurs:
- The certificate is checked to confirm that the private CA signed it.
- The certificate is validated (the expiration is checked).
- A certificate revocation list is checked to ensure that the certificate has not been revoked.
- The CommonName component of the DN is extracted from the certificate and mapped to a service endpoint based on the subject’s membership level.
- The message is forwarded to the selected service endpoint.
The
com.ibm.security.x509.X500Name
class provides methods to access the certificate DN's common components. This class is included in the JCE implementation that the Security provider uses in the IBM® WebSphere® Application Server V5.1 (Application Server).
The classes for performing BASE64 encode/decode operations that are included with Application Server are not part of the public API supported by IBM. Since they are not supported, there is no guarantee that they will continue to be included or packaged in a consistent manner as Application Server evolves. Due to the lack of support, I recommend that you avoid using these BASE64 routines.
There are, however, several open source BASE64 encoder/decoder implementations available. I have chosen one, the MiGBase64 implementation, to provide the encode/decode operations for this example. This is not an endorsement of that implementation over others that are available, though I can say that I had no problems using the MigBase64 encoder.
Implement the requestor-side X509 certificate handler
Requestor handler initialization parameters
I built the client-side handler example with the following assumptions:
- The client's subscription certificate is located in a password-protected Java Keystore (.jks) file. Therefore, the handler must be provided with the keystore location and password.
- The keystore may contain several certificates, so it is necessary to identify the certificate by its alias within the keystore.
These specifics are provided to the handler as initialization parameters, replacing the explicit ServiceLevel parameter I used in the previous paper. Table 3 shows the new set of client handler configuration parameters:
Table 3: Client handler initialization parameters
| Parameter Name | Description |
| KeystorePath | The filesystem path name of the keystore file |
| KeystorePswd | The keystore password |
| CertAlias | The certificate alias |
| ActorName | The role name to assign to the subscription header |
| HeaderName | The tag name to use for the header |
| HeaderNamespace | The namespace to use for the header and its parts |
Client-side handler handleRequest() method
Here is the handleRequest() method for the new client-side handler implementation. As you can see, the structure is similar to the previous one, with the exception that this one retrieves the client certificate and encodes it into a string which is then embedded in the message; whereas the previous handler just inserted the service level. The headerVersion element is also added here to reflect the updated header structure.
In Listing 3 the configuration parameter accessors (for example getActorName()) use the same technique described in the previous article.
Listing 3: Service client handler
handleRequest() method
public boolean handleRequest(MessageContext mc) {
boolean result = false;
try {
X509Certificate cert = getClientCertificate();
// create the header element that will hold the membership token
SOAPHeaderElement headerElement =
HandlerUtil.addHeader(
(SOAPMessageContext) mc,
getHeaderTagName(),
RoutingConstants.HEADER_PREFIX,
getHeaderNamespace(),
getActorName(),
false);
// insert the header-version child element into the header
SOAPElement versionElement =
headerElement.addChildElement(
RoutingConstants.VERSION_ELEMENT_NAME,
RoutingConstants.HEADER_PREFIX);
versionElement.addTextNode(RoutingConstants.HEADER_VERSION);
// insert the membership token child element into the header
SOAPElement tokenElement =
headerElement.addChildElement(
RoutingConstants.TOKEN_ELEMENT_NAME,
RoutingConstants.HEADER_PREFIX);
// place the certificate data into the tokenElement,
// encoded in Base 64
tokenElement.addTextNode(
Base64.encodeToString(cert.getEncoded(), true));
// header processing was successful
result = true;
}
catch (Exception e) {
throw new SOAPFaultException(
new QName("Client"),
"Client processing failure: " + e.getMessage(),
"",
null);
}
return result;
}
|
Access the client subscription certificate
Accessing the client certificate is a straightforward process using classes that Java security provides. This would get tricky if I didn’t make an assumption about the keystore file format. Fortunately, the Java Keystore format (.jks) is common and generally supported.
Listing 4: Load the subscription certificate from the keystore
private X509Certificate getClientCertificate()
throws
KeyStoreException,
IOException,
NoSuchAlgorithmException,
CertificateException,
FileNotFoundException {
// assume a jks keystore and get a jks keystore instance
KeyStore ks = KeyStore.getInstance("jks");
// load the keystore instance
ks.load(
new FileInputStream(getKeystorePath()),
getKeystorePassword().toCharArray());
// get the certificate described in the handler configuration
X509Certificate cert =
(X509Certificate) ks.getCertificate(getKeystoreAlias());
// emit some info about the certificate
printCertDetail(cert);
return cert;
}
|
There are several X509 certificate property accessors on the X509Certificate class. The printCertDetail() method uses these to display the certificate details in the console. Listing 5 shows a sample of the output.
Listing 5: X509 Certificate Details
Class = com.ibm.security.x509.X509CertImpl Type = X.509 SigAlgName = SHA1withRSA SigAlgOID = 1.2.840.113549.1.1.5 Version = 3 IssuerDN = CN=Int CA2, OU=TRL, O=IBM, ST=Kanagawa, C=JP SubjectDN = CN=SOAPRequester, OU=TRL, O=IBM, ST=Kanagawa, C=JP Principal class = com.ibm.security.x509.X500Name IssuerUniqueID = null NotAfter = Sat Oct 01 05:54:06 EDT 2011 NotBefore = Mon Oct 01 05:54:06 EDT 2001 Public key format = X.509 |
The detail for this particular certificate shows that it was signed by an issuer with the DN of CN=Int CA2, OU=TRL, O=IBM, ST=Kanagawa, C=JP. You will see shortly that this is the same DN that has been assigned to the SubjectDN value of the Certifying Authority (CA) certificate used to validate the requestor certificate.
This completes the necessary changes that must be made to the client handler for this subscription token scenario. The majority of the work for this scenario is in the provider-side handler, which must recover the certificate from the header, verify it, and validate it.
Implement the provider-side X509 certificate handler
Provider handler initialization parameters
To verify a client certificate, the provider-side handler must have access to the issuers’ public key. In the example provided, I make the assumption that the CAs’ serialized certificate, containing both the private and public keys, is available somewhere in the local file system. Therefore, the handler must be provided with that file system location in order to recover the certificate and make use of the public key.
The path name of the certificate is provided to the handler as an initialization parameter. In addition, the set of initialization parameters identified in the previous paper apply equally in this case. Table 4 shows the results to the set of initialization parameters for the provider side handler:
Table 4: Provider handler initialization parameters
| Parameter name | Description |
| CA_Path | This is the file system path name of the file containing the CA certificate. |
| RoutingConfig | The file system path name of the routing table containing key value pairs, where the "key" is an issued client certificate's subject CommonName (CN), and the "value" is the URL of the service endpoint that provides the QoS that the subscriber has purchased. |
| HeaderName | This is the tag name to use for the header. This must match the value configured on the client handler. |
| HeaderNamespace | The namespace to use for the header and its parts |
The provider-side handleRequest() method
The main difference between the handleRequest() method used in the previous paper and this one are that this method must also do the following:
- Check the header version to ensure that the format is understood.
- Process the digital certificate.
The two additional steps are captured in two new methods:
-
The
isCompatibleHeaderFormat()method which checks the header version. -
The
extractAndVerifyCert()method, which reconstitutes the client’s digital certificate from the encoded data in the header, verifies the signer, and confirms that the certificate has not expired.
The actual routing step is similar in that each implementation maps a character string to a service endpoint to determine the route of the SOAP message. In the first case, that string is an explicit service level, for example GoldService, and in this case it is the CN of a verified, valid certificate.
Listing 6 in this sidefile shows the strategy for header processing at the provider.
Verify that the subscription header format is correct for this handler implementation
The program code required to test the subscripion header's version is quite simple. All that is required is that the version element be located, and that it's value match the value expected by the receiving handler. A more elaborate approach might assume that each iteration of the handler can understand the version that was current when it was written and in all earlier versions; however, the example presented here takes the simpler approach of expecting a specific header version. Also, note that the absence of a header version child element results in a return value of "false."
Listing 7: Test the header version for compatibility with the receiving handler
handleRequest() method
/**
* This method examines the subscription header to see if the format
* is compatible with this handler implementation
* @param subscriptionHeader
* @return boolean
*/
private boolean isCompatibleHeaderFormat(
SOAPHeaderElement subscriptionHeader) {
boolean isCompatible = false;
SOAPElement elem =
findChildElement(subscriptionHeader, getVersionElementName());
if (elem != null)
isCompatible =
SUPPORTED_HEADER_VERSION.equalsIgnoreCase(elem.getValue());
return isCompatible;
}
|
Access the CA public key and test the client certificate
Since the code required to process the certificates is of primary interest in this paper, I include it here in its entirety.
This first method (see Listing 8) simply locates the subscription token element within the header, extracts its value, which is a string containing the encoded certificate, and delegates the certificate processing to another method.
Listing 8: Locate the subscription token element, extract the certificate data and have it verified
/**
* This method locates the element that contains the encoded
* subscription token, extracts the encoded string and uses
* the extractAndVerifyCert(String encodedCert) method to
* establish the actual X509Certificate. If successful it
* returns a reference to the resulting X509Certificate
* instance.
*
* @param subscriptionHeader
* @return
* @throws InvalidKeyException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws FileNotFoundException
* @throws UnrecoverableKeyException
* @throws KeyStoreException
* @throws IOException
* @throws HandlerConfigurationException
* @return java.security.cert.X509Certificate
*/
private X509Certificate extractAndVerifyCert(SOAPHeaderElement subscriptionHeader)
throws
InvalidKeyException,
NoSuchProviderException,
SignatureException,
NoSuchAlgorithmException,
CertificateException,
FileNotFoundException,
UnrecoverableKeyException,
KeyStoreException,
IOException,
HandlerConfigurationException {
X509Certificate result = null;
SOAPElement elem =
findChildElement(subscriptionHeader, getTokenElementName());
if (elem != null)
result = extractAndVerifyCert(elem.getValue());
return result;
}
|
Generally, there are 3 steps that must be performed when verifiying and validating a certificate. The certificate must be tested to confirm that:
- It was signed (issued) by the appropriate certifying authority.
- It has not expired.
- It is not listed in a certificate revocation list (CRL), which ensures that past subscribers (in other words, those that are no longer customers but were at some time in the past) are denied service.
The code in Listing 9 does not check a CRL to see if the certificate has been revoked. That task is left as an exercise for the reader. I have inserted a comment at the location where you should perform that test.
Listing 9: Verify that the certificate data represents an X509 token that we issued and that it is still valid
/**
* This method uses a BASE64 decoder to decode the String argument and
* create a X509Certificate instance from the resulting byte array. If
* successful it returns a reference to the resulting X509Certificate
* instance.
*
* @param encodedCert
* @return
* @throws InvalidKeyException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws FileNotFoundException
* @throws UnrecoverableKeyException
* @throws KeyStoreException
* @throws IOException
* @throws HandlerConfigurationException
* @return java.security.cert.X509Certificate
*/
private X509Certificate extractAndVerifyCert(String encodedCert)
throws
InvalidKeyException,
NoSuchProviderException,
SignatureException,
NoSuchAlgorithmException,
CertificateException,
FileNotFoundException,
UnrecoverableKeyException,
KeyStoreException,
IOException,
HandlerConfigurationException {
byte[] ba = Base64.decode(encodedCert);
// we're expecting an X509 cert
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert =
(X509Certificate) cf.generateCertificate(new ByteArrayInputStream(ba));
// recover the public key of the CA
PublicKey ca = getCAPublicKey();
System.out.println("\n\nRequestor Certificate Details:\n");
printCertDetail(cert);
// verify that the cert was signed by the CA
cert.verify(ca);
// confirm that the certificate is valid, i.e. hasn't expired
cert.checkValidity();
// check for certificate revocation here...
return cert;
}
|
As I mentioned earlier, this example assumes that the CA certificate has been serialized to a file somewhere in the filesystem. Specifically, the certificate must have been serialized into the file as a DER-encoded byte stream; the -export option of the Java
keytool
utility creates files in this format by default. The getCAPublicKey() method listed below recovers the public key from that certificate.
The java.security.cert.X509Certificate is a standard Java interface that defines the operations on a certificate; most of the time your application will not care what implementation it is using since the construction of the instances is handled elsewhere. In this case you need to know the implementation class because you have to construct an instance of it from the information in the serialized certificate. The java.security.cert.X509Certificate implementation that Application Server uses is com.ibm.security.x509.X509CertImpl. It has a constructor that consumes a FileInputStream so that the task of recovering a certificate from a file is a straightforward one. The example uses a lazy initialization approach and caches a reference to the Public Key from the CA Certificate to avoid the need to reload it each time the handler is invoked.
Listing 10: Retrieve the CA certificate from the file system and extract the public key
/**
* This method returns a reference to the PublicKey contained in the CA
* certificate. It uses a lazy initialization technique to delay the
* reading of the certificate until it is needed. N.B. this method
* assumes that the CA_Path initialization parameter has been
* configured with the full filesystem pathname of the certificate that
* will be used as the CA. The CA cert must be stored in the file as a
* DER-encoded bytestream. A reference to the CA cert is held in the
* handlers instance state to avoid reading it over and over.
* @return java.security.PublicKey
*/
private synchronized PublicKey getCAPublicKey()
throws
IOException,
NoSuchAlgorithmException,
CertificateException,
FileNotFoundException,
UnrecoverableKeyException,
KeyStoreException,
HandlerConfigurationException {
FileInputStream fis = null;
try {
// recover the CA PublicKey on the first invocation
if (CA == null) {
fis = new FileInputStream(getCACertFileSystemPath());
X509Certificate cacert = new X509CertImpl(fis);
System.out.println("\n\nCA Certificate Details:\n");
printCertDetail(cacert);
setCAPublicKey((PublicKey) cacert.getPublicKey());
}
}
finally {
if (fis != null)
fis.close();
}
return CA;
}
|
This brings you to the end of the coding details for this routing approach. All that remains now is to deploy the client and provider handlers to the appropriate handler chains.
The deployment steps this articles' solution are very similar to the deployment steps described in the first part of this series. The only exceptions are that the initialization parameters are slightly different for each of the handlers and the names of the jar files and handler classes provided have been changed to reflect their focus on X509 certificates. Table 5 lists the project files that I have included with this paper. You can download them using by clicking on the code icon at the top of this paper.
| Resource Name | Description |
EchoServiceEAR.ear
| EchoService Web service implementation. To be deployed in an instance of WebSphere Application Server.EchoService Web service implementation. To be deployed in an instance of WebSphere Application Server. |
ServiceClientEAR.ear
| A test client for the EchoService. |
WSGW_X509_RoutingHandlers.jar
| The JAX-RPC handlers used in this article. To be deployed in the <WAS_install>/lib/ext directory of the application server where <WAS_install> is the root directory of the application server installation. It also contains the MiGBase64 base 64 encoder/decoder used in this article. This jar is provided for deployment convenience as the same jar is included in each of the EAR files. |
Software required to test this example
To test this example, you need to have the following software installed. Download links for trial versions of this software are available at the end of this article:
| WebSphere Application Server V5.1 |
|
| WebSphere Application Server Deployment Manager V5.1 Fixpack 1 | This fixpack is required for recent updates to the Web Services Gateway application; you will be using the Gateway ear files provided by this fixpack instead of those provided with the GA release. |
| WebSphere Application Server V5.1 Fixpack 1 | Apply this fixpack to gain the benefit of recent product updates if you are using the standalone edition of WebSphere Application Server. |
| Web Services Gateway with an IBM SOAP over HTTP Channel installed (wsgwsoaphttp1.ear) | Click here for Installation Instructions |
| WebSphere Studio Application Developer V5.1.2 | Used to develop applications targeted at WebSphere Application Server V5.1. |
Deploy the EchoService to Application Server
The EchoService Web application is contained in the EchoServiceEAR.ear file. Install this EAR file in the WebSphere Application Server instance you are using; it has no special configuration requirements. The endpoint for the EchoService Web service is:
http://<hostname>:<port>/EchoServiceWeb/services/EchoService
<hostname> is the name of the host running the application server and <port> is the port number of the http listener for the application server instance on which the service is installed.
To confirm that the application is installed properly, enter the following URL in a Web browser:
http://<hostname>:<port>/EchoServiceWeb/services/EchoService?wsdl
A successfully installed service responds with its WSDL.
Configure the Web services client deployment descriptor in WebSphere Studio Application Developer
A client application has been provided in the ServiceClientEAR.ear file. It contains:
| J2EE Component Name | Description |
ServiceClientEAR
| J2EE client enterprise application |
ServiceClient
| J2EE application client holding the web service requestor |
WSGW_X509_RoutingHandlers.jar
| Utility JAR containing the client and service handlers |
Application Server provides sample X509 certificates. The deployment configuration described here uses those certificates. You may use your own certificate if you wish.
Import the EAR into WebSphere Studio Application Developer (Application Developer); do not create a utility project for the WSGW_X509_RoutingHandlers JAR file.
Open the Project Navigator view in the J2EE perspective and expand the ServiceClient project as shown in Figure 1. Open the webservicesclient.xml file in the Web services client editor and select the Handlers tab (see Figure 2.
Figure 1: Open the Web services client deployment descriptor
Figure 2: Web services client editor: Handler tab
Add a handler to the client configuration. The client handler class for this example is called com.ibm.jaxrpc.certrouting.ClientX509CertHandler. Click the Add button and select this class from the list provided. Configure the initial parameters for this handler as follows:
| Parameter name | Value |
| KeystorePath | <WAS Install>etc\ws-security\samples\dsig-sender.ks |
| KeystorePswd | client |
| CertAlias | soaprequester |
| HeaderName | subscription |
| HeaderNamespace | http://com.videos-r-us/membership |
| ActorName | MessageRouter |
When you are done, the handler configuration should look like the one in Figure 3. Save the changes and close the editor.
Figure 3: Completed client handler configuration
At this point the client is configured to provide the proper header information to the proxy service. Now it’s time to set up the gateway proxy service.
Set up the proxy mode service in the Web Services Gateway
The administration of the Web Services Gateway is performed using the Gateway Admin Web application. Assume that the Application Server instance is running the HTTP listener on port 9080 (the default). The admin application can be accessed through the URL: http://<hostname>:9080/wsgw/admin/index.html, where you are presented with the page shown in Figure 4.
Figure 4: The Web Services Gateway admin application
Select the Services:Deploy link in the left-hand menu and fill in the following information:
| Gateway Service Name | EchoProxyService |
| Selective SOAP Parsing/Generic Classes | selected |
| Act as a proxy service | selected |
| Channels | SOAPHTTPChannel1 selected |
Since this is a proxy mode service, there is no requirement to provide information about the target service. It is assumed that a handler will be added to the service with the responsibility to set the target endpoint on a per-message basis. So, with this information entered, you have provided all the necessary deployment information, and your deployment configuration should resemble the one shown in Figure 5. Complete the deployment step by clicking Ok at the bottom of the form. You should see a message box reminding you that you are required to deploy a JAX-RPC handler whose job it is to set the transport.url property for each message to this service.
Figure 5: Completed gateway service deployment
The remaining steps take care of deploying and configuring the routing handler; a task which breaks down as follows:
- Install the handler class and dependencies in the application server that is hosting the gateway.
- Deploy the handler class in the gateway.
- Add the handler to the proxy mode service handler chain.
Install the handler class and dependencies in the gateway application server
Before the routing handler can be associated with the proxy mode service, the handler class and its dependencies must be visible to the gateway and channel class loaders. The deployment options available are dependent upon the application server's classloader mode.
If the application server instance running the gateway is configured in single classloader mode, the handler class can be in any EAR installed in the application server. If the application server is configured in multiple classloader mode (the default), the handler must be placed in one of the following locations:
- Use a JAR file placed in <WAS_install>/lib or <WAS_install>/lib/ext directories, where <WAS_install> refers to the directory where Application Server is installed.
- Deploy the handler classes to the <WAS_install>/classes directory.
- Use a JAR file placed in a WebSphere shared library associated with the channel and gateway enterprise applications.
I use the first approachfor this example. Place the WSGW_X509_RoutingHandlers.jar file into the <WAS_install>/lib/ext directory.
Deploy the handler in the gateway
Select the Deploy link under the Handlers heading of the Gateway admin Web Application. You will be prompted for the details of the handler you wish to deploy. The parameters to use for this example are as follows:
| Handler Name | ServiceRoutingHandler |
| Handler Class | com.ibm.jaxrpc.certrouting.X509CertRoutingHandler |
| Init Parameter name:value | RoutingConfig:/routing.properties |
| Init Parameter name:value | HeaderName:subscription |
| Init Parameter name:value | HeaderNamespace:http://com.videos-r-us/membership |
| SOAP header QName | {http://com.videos-r-us/membership}subscription |
| SOAP Role | MessageRouter |
When you have entered this information your deployment screen should resemble the one shown in Figure 6. Click Ok to complete the deployment and proceed to the next step.
Figure 6: Completed gateway handler configuration
Add the handler to the proxy mode service handler chain
Now that the handler is deployed to the Gateway, it is available to be added to the handler chains of any deployed gateway services. Select the List link under the Services sub-menu of the Gateway admin application; you should see EchoProxyService in the list of available services. Click the EchoProxyService link and scroll down to locate the Channels list for the service (see Figure 7. Select Edit JAX-RPC handler configuration for the SOAPHTTPChannel1 channel.
Figure 7: EchoProxyService channels list
Find the ServiceRoutingHandler in the Add JAX-RPC Handler list and add it to the handler configuration. At this point the configuration page should appear similar to Figure 8.
Figure 8: Completed proxy service handler chain
The routing handler uses a properties file to map subscriber common names to target service endpoints. In the configuration instructions provided above, the RoutingConfig property was assigned the value /routing.properties. With this configuration, place a properties file called routing.properties in the <WAS_install>/properties directory containing information similar that shown in Listing 11.
Listing 11: Sample routing properties file
#sample Service Level routing table SOAPRequester=http://localhost:9082/EchoServiceWeb/services/EchoService SOAPRequester2=http://localhost:9083/EchoServiceWeb/services/EchoService |
In this case, the two entries cause requests from the subscribers with the CNs SOAPRequester and SOAPRequester2 to be mapped to ports 9082 and 9083 on the server hosting the Gateway instance. You should make sure that the URLs are adjusted to reflect the particulars of the service(s) you have deployed.
Please note that the sample certificates provided with Application Server do not include a certificate with a CN of SOAPRequester2; it is used here for illustration purposes only. However, the client has been configured to use a certificate with a CN of SOAPRequester so that the first entry in this file will be used.
Test the gateway proxy service
Once the properties file is created and the foregoing configuration is completed, you can test the service using the ServiceClient project you imported into Application Developer earlier in this exercise.
The main client class is called test.EchoServiceClient. As provided, this class targets its request at the following endpoint:
http://localhost:9081/wsgwsoaphttp1/soaphttpengine/urn%3Aibmwsgw%23EchoProxyService
This URL assumes that the client is running on the same machine as the server, and that the application server HTTP listener is listening on port 9081. If your application server HTTP listener is running on a different port or machine, edit this URL to point to the appropriate location.
Figure 9: Sample client application
Once the URL points to your Gateway instance, select the application client class in the Project Navigator of the WebSphere Studio J2EE Perspective (see Figure 9), and select the Run > Run As > WebSphere V5.1 Application Client menu item. This will launch the client and send a request to your proxy service, which resembles Listings 12 and 13 in this sidefile.
The sample messages were captured using the TCPMonitor feature of Application Developer to display the message traffic between the client application and the Web Services Gateway.
This paper showed you how you can use X509 certificates to provide routing criteria for proxy mode services in the IBM WebSphere Web Services Gateway. X509 certificates provide the following features that are desireable for some routing applications:
- They are non-forgeable.
- They are revocable.
- They carry the issuer's signature.
- They contain information in the subject's distinguished name, which can be used as a key into a routing table.
To be effective, this approach requires that the network link between the requestor and provider be secure and that the subscriber not provide third party access to their certificate. If either of these conditions is not met, there is a chance that the certificate of one subscriber might be used by a party that is not the subject of the certificate. The use of transport-level security, such as SSL, prevents the certificate from being stolen from a message, but the subscriber must not make his keystore's content accessible to others.
Combining the techniques presented here with a distributed resource such as an RDBMS to hold mapping information turns the mapping example presented here into a very scalable and robust Web services routing solution.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | ws-routing2code.zip | 83 KB | HTTP |
Information about download methods
Web Services Gateway
- "Employ the IBM WebSphere Web Services Gateway, Part 1" (
developerWorks
, September 2004) Part 1 of this article series.
- "Introduction to the Web Services Gateway" (
developerWorks
, May 2002) describes the role and operation of the IBM Web Services Gateway.
- "Exploring the new Features of the WebSphere Web Services Gateway" (
developerWorks
, March 2004) discusses the new gateway features available in Version 5.1.
-
Samples for the Web Services Gateway
-
Installing the Web Services Gateway in WebSphere Application Server version 5.0x
-
Installing the Web Services Gateway in WebSphere Application Server version 5.1x
JAX-RPC Handlers
-
Process SOAP 1.1 headers with JAX-RPC 1.0 SOAP handlers(
developerWorks
, April 2004) examines how JAX-RPC SOAP handlers process SOAP message headers.
Download IBM WebSphere Free Trial Products / Fixpacks
-
Trial download of WebSphere Application Server 5.1
-
WebSphere Application Server Version 5.1 Fix Pack 1
-
Trial download of WebSphere Studio Application Developer
Web Services Specifications
X509 Certificates
-
Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile
-
RFC1485: A String Representation of Distinguished Names
-
Java Security, 2nd Edition (O'Reilly
, May 2001) The book covers in depth the security model of Java 2, version 1.3, including the JAAS and JSSE security APIs. It provides a thorough description of how to use X509 certificates iwith Java.
Other resources
- Browse for books on these and other technical topics.
- Want more? The developerWorks SOA and Web services zone hosts hundreds of informative articles and introductory, intermediate, and advanced tutorials on how to develop Web services applications.
Michael Ellis is a solution architect on the IBM Software Services for WebSphere team and lives in Ottawa, Canada. He specializes in Web services, XML, J2EE technology, and object-oriented architecture and design. You can contact him at msellis@ca.ibm.com.





