We implemented WS-Security recently. The web services were developed using Rational Application Developer (RAD) and hosted on Websphere. RAD does not support WS-Security on the client-side unless the client is hosted in the Websphere Environment. If the client is not running on a Websphere server, the best one can do is to hosted in a Websphere J2EE client application. For portability, I decided to give it a try to use open source apache Axis and WSS4J to access the web service.
The WS-Security that we implemented is fairly straightforward. We use UsernameToken for authentication and also encrypt the soap body. The procedure to do it in RAD is well documented and only requires configuration. Unfortunately, the WSS4J is not well document. With a little bit Googling and looking into the source code, I am able to piece it together and make it work.
The following is my client_deploy.wsdd file:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"/>
<globalConfiguration >
<requestFlow >
<handler type="java:org.apache.ws.axis.security.WSDoAllSender" >
<parameter name="action" value="UsernameToken Encrypt"/>
<parameter name="user" value="myUserInUserNameToken"/>
<parameter name="passwordType" value="PasswordText"/>
<parameter name="passwordCallbackClass" value="com.mercuryinsurance.q2.ws.test.WSCallback"/>
<parameter name="encryptionUser" value="myKeyStoreAlias"/>
<parameter name="encryptionPropFile" value="request.properties" />
<parameter name="encryptionSymAlgorithm" value="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<parameter name="encryptionKeyIdentifier" value="SKIKeyIdentifier" />
</handler>
</requestFlow >
<responseFlow>
<handler name="DoSecurityReceiver" type="java:org.apache.ws.axis.security.WSDoAllReceiver">
<parameter name="action" value="Encrypt"/>
<parameter name="decryptionPropFile" value="response.properties" />
<parameter name="passwordCallbackClass" value="com.mercuryinsurance.q2.ws.test.WSResponseCallback"/>
</handler>
</responseFlow>
</globalConfiguration ></
deployment>
The things to pay attentions are:
-
In WSDoAllSender, user parameter contains the user name in the usernameToken. The encryptionUser parameter contains the alias in the key store. Use SKIKeyIdentifier for encryptionKeyIdentifier (The default is X509KeyIdentifier).
-
By default, WSS4J uses aes128-cbc encryptiion algorithm. We used tripledes-cbc. So we added encryptionSymAlgorithm and encryptionKeyIndentifier parameters.
-
We uses paswordCallbackClass for both request and response. For request, this is the password for UsernameToken. For response, this is the password for our decryption key entry in the keystore.
In addition, the following are the jars we need to include to make it work:
-
axis.jar
-
bcprov.jar
-
commons-discovery.jar
-
jaxrpc.jar
-
saaj.jar
-
wsdl4j.jar
-
wss4j.jar
-
xalan.jar
-
xmlsec.jar
The following is the contents of request.properties:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jceks
org.apache.ws.security.crypto.merlin.keystore.password=mykeystorepassword
org.apache.ws.security.crypto.merlin.file=mykeyfile.jceks
org.apache.ws.security.crypto.merlin.keystore.alias=myservicealias
org.apache.ws.security.crypto.merlin.alias.password=myservicepassword
The following is the contents of request.properties:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jceks
org.apache.ws.security.crypto.merlin.keystore.password=mykeystorepassword
org.apache.ws.security.crypto.merlin.file=mykeystorefile.jceks
org.apache.ws.security.crypto.merlin.keystore.alias=myclientalias
org.apache.ws.security.crypto.merlin.alias.password=myclientpassword