11.2. Turn on WS-Security
WSS4J security is triggered through interceptors that are added to the service and client individually or as required. These interceptors allow you to perform the most common WS-Security related processes:
- Pass authentication tokens between services.
- Encrypt messages or parts of messages.
- Sign messages.
- Timestamp messages.
Interceptors can be added either programmatically or through the Spring xml configuration of endpoints. For instance, on server side, you can configure signature and encryption in the
jbossws-cxf.xml
file this way:
<beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:beans='http://www.springframework.org/schema/beans' xmlns:jaxws='http://cxf.apache.org/jaxws' xsi:schemaLocation='http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd'> <bean id="Sign_Request" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="Timestamp Signature Encrypt"/> <entry key="signaturePropFile" value="bob.properties"/> <entry key="decryptionPropFile" value="bob.properties"/> <entry key="passwordCallbackClass" value="org.jboss.test.ws.jaxws.samples.wsse.KeystorePasswordCallback"/> </map> </constructor-arg> </bean> <bean id="Sign_Response" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="Timestamp Signature Encrypt"/> <entry key="user" value="bob"/> <entry key="signaturePropFile" value="bob.properties"/> <entry key="encryptionPropFile" value="bob.properties"/> <entry key="encryptionUser" value="Alice"/> <entry key="signatureKeyIdentifier" value="DirectReference"/> <entry key="passwordCallbackClass" value="org.jboss.test.ws.jaxws.samples.wsse.KeystorePasswordCallback"/> <entry key="signatureParts" value="{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"/> <entry key="encryptionParts" value="{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/> <entry key="encryptionKeyTransportAlgorithm" value="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/> <entry key="encryptionSymAlgorithm" value="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/> </map> </constructor-arg> </bean> <jaxws:endpoint id='ServiceImpl' address='http://@jboss.bind.address@:8080/jaxws-samples-wsse-sign-encrypt' implementor='org.jboss.test.ws.jaxws.samples.wsse.ServiceImpl'> <jaxws:invoker> <bean class='org.jboss.wsf.stack.cxf.InvokerJSE'/> </jaxws:invoker> <jaxws:outInterceptors> <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor"/> <ref bean="Sign_Response"/> </jaxws:outInterceptors> <jaxws:inInterceptors> <ref bean="Sign_Request"/> <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/> </jaxws:inInterceptors> </jaxws:endpoint> </beans>
This specifies the whole security configuration (including algorithms and elements to be signed or encrypted); moreover it references a properties file (
bob.properties
) providing the keystore-related information:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=password org.apache.ws.security.crypto.merlin.keystore.alias=bob org.apache.ws.security.crypto.merlin.file=bob.jks
As you can see in the
jbossws-cxf.xml
file above, a keystore password callback handler is also configured; while the properties file has the password for the keystore, this callback handler is used to set password for each key (it has to match the one used when each key was imported in the store). Here is an example:
package org.jboss.test.ws.jaxws.samples.wsse; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class KeystorePasswordCallback implements CallbackHandler { private Map<String, String> passwords = new HashMap<String, String>(); public KeystorePasswordCallback() { passwords.put("alice", "password"); passwords.put("bob", "password"); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pc = (WSPasswordCallback)callbacks[i]; String pass = passwords.get(pc.getIdentifer()); if (pass != null) { pc.setPassword(pass); return; } } } public void setAliasPassword(String alias, String password) { passwords.put(alias, password); } }
On the client side, you can similarly setup the interceptors programmatically; here is an excerpt of the client for the above described endpoint:
Endpoint cxfEndpoint = client.getEndpoint(); Map<String,Object> outProps = new HashMap<String,Object>(); outProps.put("action", "Timestamp Signature Encrypt"); outProps.put("user", "alice"); outProps.put("signaturePropFile", "META-INF/alice.properties"); outProps.put("signatureKeyIdentifier", "DirectReference"); outProps.put("passwordCallbackClass", "org.jboss.test.ws.jaxws.samples.wsse.KeystorePasswordCallback"); outProps.put("signatureParts", "{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"); outProps.put("encryptionPropFile", "META-INF/alice.properties"); outProps.put("encryptionUser", "Bob"); outProps.put("encryptionParts", "{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"); outProps.put("encryptionSymAlgorithm", "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"); outProps.put("encryptionKeyTransportAlgorithm", "http://www.w3.org/2001/04/xmlenc#rsa-1_5"); WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps); //request cxfEndpoint.getOutInterceptors().add(wssOut); cxfEndpoint.getOutInterceptors().add(new SAAJOutInterceptor()); Map<String,Object> inProps= new HashMap<String,Object>(); inProps.put("action", "Timestamp Signature Encrypt"); inProps.put("signaturePropFile", "META-INF/alice.properties"); inProps.put("passwordCallbackClass", "org.jboss.test.ws.jaxws.samples.wsse.KeystorePasswordCallback"); inProps.put("decryptionPropFile", "META-INF/alice.properties"); WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps); //response cxfEndpoint.getInInterceptors().add(wssIn); cxfEndpoint.getInInterceptors().add(new SAAJInInterceptor());
11.2.1. Package and deploy
To deploy your web service endpoint, you need to package the following files along with your service implementation and WSDL contract:
- The
jbossws-cxf.xml
descriptor. - The properties file.
- The keystore file (if required for signature/encryption).
- The keystore password callback handler class.
For instance, here are the archive contents for the signature and encryption sample (POJO endpoint) mentioned before:
[cxf-tests]$ jar -tvf target/test-libs/jaxws-samples-wsse-sign-encrypt.war 0 Tue Jun 03 19:41:26 CEST 2008 META-INF/ 106 Tue Jun 03 19:41:24 CEST 2008 META-INF/MANIFEST.MF 0 Tue Jun 03 19:41:26 CEST 2008 WEB-INF/ 0 Tue Jun 03 19:41:26 CEST 2008 WEB-INF/classes/ 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/ 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/ 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/ 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/ 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/ 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/ 1628 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/KeystorePasswordCallback.class 364 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/ServiceIface.class 859 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/ServiceImpl.class 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/jaxws/ 685 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/jaxws/SayHello.class 1049 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/jaxws/SayHelloResponse.class 2847 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/jbossws-cxf.xml 0 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/wsdl/ 1575 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/wsdl/SecurityService.wsdl 641 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/wsdl/SecurityService_schema1.xsd 1820 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/bob.jks 311 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/classes/bob.properties 573 Tue Jun 03 19:41:24 CEST 2008 WEB-INF/web.xml
On client side, instead, you only need the properties and keystore files (assuming you set up the interceptors programmatically). You just need to deploy and test your WS-Security-enabled application.