Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

To enable WS-Security within CXF for a server or a client, you'll need to set up the WSS4J interceptors. You can either do this via the API or via Spring XML configuration. This section will provide an overview of how to do this, and the following sections will go into more detail about configuring the interceptors for specific security actions.

It is important to note that:

  1. You must add the SAAJ(In/Out)Interceptors if you're using WS-Security. These enabled creation of a DOM for each request/response which is required for WS-Security.
  2. You may not need both in and out WS-Security. For instance, if you are just requiring Signature on incoming messages, you'll just need the incoming interceptors.

Adding the interceptors via the API

...

Code Block
java
java
Map<String,Object> inProps= new HashMap<String,Object>();
... // how to configure the properties is outlined below;

WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
cxfEndpoint.getInInterceptors().add(wssIn);
cxfEndpoint.getInInterceptors().add(new SAAJInInterceptor());

Map<String,Object> outProps = new HashMap<String,Object>();
... // how to configure the properties is outlined below;

WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
cxfEndpoint.getOutInterceptors().add(wssOut);
cxfEndpoint.getOutInterceptors().add(new SAAJOutInterceptor());

Spring XML Configuration

Configuring WS-Security Actions

It is important to note that

  1. You must add the SAAJ*Interceptor if you're using WS-Security. These enabled creation of a DOM for each request/response which is required for WS-Security.
  2. You may not need both in and out WS-Security. For instance, if you are just requiring Signature on incoming messages, you'll just need the incoming interceptors.

Configuring WS-Security Actions

Username Token Authentication

WS-Security supports many ways of specifying tokens. One of these is the UsernameToken header. It is a standard way to communicate a username and password or password digest to another endpoint.

For the server side, you'll want to set up the following properties on your WSS4JInInterceptor:

Code Block

inProps.setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
// Password type : plain text
inProps.setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// for hashed password use:
//properties.setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
// Callback used to retrive password for given user.
inProps.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, ServerPasswordHandler.class.getName());

The password callback class allows you to retrieve to retrieve the password for a given user so that WS-Security can determine if they're authorized. Here is a small example:

Code Block
java
java

public class ServerPasswordCallback implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        if (pc.getId().equals("joe") {
            // set the password on the callback. This will be compared to the
            // password which was sent from the client.
            pc.setPassword("password");
        }
    }

}

On the Client side you'll want to configure the WSS4J outgoing properties:

Code Block

outProps.setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
// Specify our username
outProps.setProperty(WSHandlerConstants.USER, "joe");
// Password type : plain text
outProps.setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// for hashed password use:
//properties.setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
// Callback used to retrive password for given user.
outProps.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordHandler.class.getName());

Once again we're using a password callback, except this time instead of specifying our password on the server side, we're specifying the password we want sent with the message. This is so we don't have to store our password in our configuration file.

Code Block
java
java

public class ClientPasswordCallback implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        // set the password for our message.
        pc.setPassword("password");
    }

}

...

Generating keys using keytool

For the Signature and Encryption actions, you'll need to create a public & private key for the entities involved. You can generate a key pair for the development environment via the following steps.

For more information, there is an excellent tutorial here.

Keep in mind these will not be signed by an external authority like Verisign.

1. Creating private key with given alias and password like "myAlias"/"myAliasPassword" in keystore (protected by password for
security reasons)

Code Block

keytool -genkey -alias myAlias -keypass myAliasPassword -keystore privatestore.jks \
  -storepass keyStorePassword -dname "cn=myAlias" -keyalg RSA

The alias is simply a way to identify the key pair. In this instance we are using the RSA algorithm.

2. Self-sign our certificate (in production environment this will be done by a company like Verisign).

Code Block

keytool -selfcert -alias myAlias -keystore privatestore.jks -storepass keyStorePassword -keypass myAliasPassword

3. Export the public key from our private keystore to file named key.rsa

Code Block

keytool -export -alias myAlias -file key.rsa -keystore privatestore.jks -storepass keyStorePassword

4. Import the public key to new keystore:

Code Block

keytool -import -alias myAlias  -file key.rsa -keystore publicstore.jks -storepass keyStorePassword

So now we have two keystores containing our keys - a public one (publicstore.jks) and a private one (privatestore.jks). Both of them have keystore password set to keyStorePass (this not recommended for production but ok for development) and alias set to myAlias. The file key.rsa can removed from filesystem, since it used only temporarily. Storing keys in keystores is strongly advised because a keystore is protected by a password.

A more detailed description of key generation can be found here:
http://www.churchillobjects.com/c/11201e.html
http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html

How to create a production certificate can be found here:
http://support.globalsign.net/en/objectsign/java.cfm

Signing

To sign our message, we'll want to configure our client to sign the message via it's private key and configure the server to verify the signature using the Client's public key. To do this you must ensure that the Client's public key has been imported into the server's keystore using keytool.

On the client side, our outgoing WS-Security properties will look like so:

Code Block
java
java

outProps.put(WSHandlerConstants.ACTION, "Signature");
outProps.put(WSHandlerConstants.USER, "myAlias");
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientCallbackHandler.class.getName());
outProps.put(WSHandlerConstants.SIG_PROP_FILE, "client_sign.properties");

The USER that is specified is the key alias that you used when creating your keys. The password callback class is responsible for providing the key's password.

Our client_sign.properties file contains several settings to configure WSS4J:

Code Block

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=keystorePassword
org.apache.ws.security.crypto.merlin.keystore.alias=myAlias
org.apache.ws.security.crypto.merlin.file=client_keystore.jks

On the server side, we need to configure our incoming WSS4J interceptor to verify the signature using the Client's public key.

Code Block
java
java

inProps.put(WSHandlerConstants.ACTION, "Signature");
inProps.put(WSHandlerConstants.SIG_PROP_FILE, "server.properties");

Our server_sign.properties file contains several settings to configure WSS4J:

Code Block

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=amex123
org.apache.ws.security.crypto.merlin.file=server_keystore.jks

...

Encryption

Timestamp

Importing PFX files

...