Codehaus XFire
DocumentationQuicklinksDevelopers
Sponsors |
WS-Security support is now included in the 1.1 release. XFire uses WSS4J to provide WS-Security support. In addition to the documentation here, you may find their site helpful as well. XFire also includes some WS-Security examples in the distribution which you can try out. Enabling WS-SecurityBefore enabling WS-Security module, you must make sure that you have installed Unlimited Strength Jurisdiction Policy Files for your SDK version ( bottom of the page http://java.sun.com/j2se/1.5.0/download.jsp or http://java.sun.com/j2se/1.4.2/download.html), and the Bouncy Castle security provider from http://BouncyCastle.org ( Here is short description of instalation process : http://docs.safehaus.org/display/PENROSE/Installing+Security+Provider ) . If you don't, you will probably receive an exception message about invalid algorithm or key size. To enable WS-Security support you must add two handlers to the incoming (inhandlers) and/or outgoing (outhandlers) handler chains ( If your client sends secured data but does not expect to get such response, its enough to only add security handlers to the client's outHandlers and if your server takes secured messages but does not send such, you can add handlers only to the server's inHandlers chain). These handlers must be added to the inHandlers: Note: DOMInHandler requires XML Transformer support which is included in Xalan 2.7.0. This library is not included with XFire by default. and the handlers below to outHandlers: Server side configuration : <tns:inHandlers> <tns:handler handlerClass="org.codehaus.xfire.util.dom.DOMInHandler" /> <bean id="org.codehaus.xfire.security.wss4j.WSS4JInHandler" class="org.codehaus.xfire.security.wss4j.WSS4JInHandler"> <!-- security configuration goes here --> .... </bean> </tns:inHandlers> <tns:outHandlers> <tns:handler handlerClass="org.codehaus.xfire.util.dom.DOMOutHandler" /> <bean id="org.codehaus.xfire.security.wss4j.WSS4JOutHandler" class="org.codehaus.xfire.security.wss4j.WSS4JOutHandler"> <!-- security configuration goes here --> .... </bean> </tns:outHandlers> Client side configuration : Service serviceModel = new ObjectServiceFactory().create( .. ) // Create service client IBook service = (IBook) new XFireProxyFactory().create( ...); Client client = Client.getInstance(service); client.addOutHandler(new DOMOutHandler()); Properties outProperties = new Properties(); // CONFIGURE OUTGOING SECURITY HERE (outProperties) <-- client.addOutHandler(new WSS4JOutHandler(outProperties)); client.addInHandler(new DOMInHandler()); Properties inProperties = new Properties(); // CONFIGURE INCOMMING SECURITY HERE (inProperties) <-- client.addInHandler(new WSS4JInHandler(inProperties)); // Execute service Building keysYou can generate a key pair for the development environment via the following steps. 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 keytool -genkey -alias myAlias -keypass myAliasPassword -keystore privatestore.jks \
-storepass keyStorePassword -dname "cn=myAlias" -keyalg RSA
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). 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 keytool -export -alias myAlias -file key.rsa -keystore privatestore.jks -storepass keyStorePassword 4. Import the public key to new keystore: 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: How to create a production certificate can be found here: User Token AuthenticationThis WS-Security scenario adds username and password values to the message header. A password can be sent as plain text or in hashed form (depending on "passwordType" property). Client side configuration : protected void configureOutProperties(Properties config) { // Action to perform : user token config.setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); // Password type : plain text config.setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT); // for hashed password use: //properties.setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST); // User name to send config.setProperty(WSHandlerConstants.USER, "serveralias"); // Callback used to retrive password for given user. config.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordHandler.class.getName()); } The PasswordHandler class is responsible for finding the password for given user name and must implement the org.apache.ws.security.WSPasswordCallback interface. You can also specify an existing handler instance, using the WSHandlerConstants.PW_CALLBACK_REF property on the client/service instance or MessageContext ( e.g. client.setProperty(new PasswordHandler())). The WSHandlerConstants.PASSWORD_TYPE property determines how the password will be sent. If it is set to WSConstants.PW_TEXT, the password will be sent as plain text. If the value is WSConstants.PW_DIGEST, a password digest will be sent. If no value is set, a digest is used by default.. Server side configuration : <!-- ============= User Token with Plain Pasword ============= --> <service> <!-- --> <name>BookServiceUTPP</name> <namespace>http://xfire.codehaus.org/BookService</namespace> <serviceClass>org.codehaus.xfire.demo.BookService</serviceClass> <inHandlers> <handler handlerClass="org.codehaus.xfire.util.dom.DOMInHandler" /> <bean class="org.codehaus.xfire.security.wss4j.WSS4JInHandler" xmlns=""> <property name="properties"> <map xmlns="" > <entry key="passwordCallbackRef" xmlns=""> <bean xmlns="" class="org.codehaus.xfire.demo.PasswordHandler" id="passHandler" name="passHandler"/> </entry> <entry key="action" value="UsernameToken" /> </map> <!-- props> <prop key="action">UsernameToken</prop> <prop key="passwordCallbackClass">org.codehaus.xfire.demo.PasswordHandler</prop> </props --> </property> </bean> <handler handlerClass="org.codehaus.xfire.demo.ValidateUserTokenHandler" /> </inHandlers> </service> The "action" property contains an action to perform. public class ValidateUserTokenHandler extends AbstractHandler { public void invoke(MessageContext context) throws Exception { Vector result = (Vector) context.getProperty(WSHandlerConstants.RECV_RESULTS); for (int i = 0; i < result.size(); i++) { WSHandlerResult res = (WSHandlerResult) result.get(i); for (int j = 0; j < res.getResults().size(); j++) { WSSecurityEngineResult secRes = (WSSecurityEngineResult) res.getResults().get(j); int action = secRes.getAction(); // USER TOKEN if( (action & WSConstants.UT )>0 ){ WSUsernameTokenPrincipal principal = (WSUsernameTokenPrincipal) secRes .getPrincipal(); // Set user property to user from UT to allow response encryption context.setProperty(WSHandlerConstants.ENCRYPTION_USER,principal.getName()); System.out.print("User : " + principal.getName() + " password : " + principal.getPassword() + "\n"); } // SIGNATURE if( ( action & WSConstants.SIGN ) > 0 ){ X509Certificate cert = secRes.getCertificate(); X500Name principal = (X500Name) secRes.getPrincipal(); // Do something whith cert System.out.print("Signature for : " + principal.getCommonName()); } } } } } TimestampsTimestamps specify how long the security data remains valid. Client side configuration: protected void configureOutProperties(Properties properties) { properties.setProperty(WSHandlerConstants.ACTION,WSHandlerConstants.TIMESTAMP); // How long ( in seconds ) message is valid since send. properties.setProperty(WSHandlerConstants.TTL_TIMESTAMP,"15"); // if you want to use millisecond precision use this //properties.setProperty(WSHandlerConstants.TIMESTAMP_PRECISION,"true"); } The WSHandlerConstants.TTL_TIMESTAMP property specifies the number of seconds or milliseconds for which the message is considered valid . The WSHandlerConstants.TIMESTAMP_PRECISION property determines the time unit for the time stamp ( seconds or miliseconds). If not specified, seconds are used. Server side configuration: <!-- ============= TimeStamp ============= --> <service> <name>BookServiceTS</name> <namespace>http://xfire.codehaus.org/BookService</namespace> <serviceClass>org.codehaus.xfire.demo.BookService</serviceClass> <inHandlers> <handler handlerClass="org.codehaus.xfire.util.dom.DOMInHandler" /> <bean class="org.codehaus.xfire.security.wss4j.WSS4JInHandler" xmlns=""> <property name="properties"> <props> <prop key="action">Timestamp</prop> </props> </property> </bean> </inHandlers> </service> EncryptionAllows one to encrypt the message body ( or only its part ) using the given crypthography algorithm. For performance reasons, message data is encrypted using a symmetric key and then the symmetic key is encrypted with the receiver's public key. Client side configuration: protected void configureOutProperties(Properties properties) { properties.setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT); properties.setProperty(WSHandlerConstants.USER, "serveralias"); //Configuration of public key used to encrypt message goes to properties file. properties.setProperty(WSHandlerConstants.ENC_PROP_FILE, "org/codehaus/xfire/client/outsecurity_enc.properties"); } WSHandlerConstants.ENC_PROP_FILE - contains path to file with details of encryption configuration. The file can look like the following : 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=keystorePass org.apache.ws.security.crypto.merlin.alias.password=aliaspass org.apache.ws.security.crypto.merlin.keystore.alias=alias org.apache.ws.security.crypto.merlin.file=META-INF/xfire/myPublicstore.jks Server side configuration: <!-- ============= Encryption ============= --> <service> <name>BookServiceENC</name> <namespace>http://xfire.codehaus.org/BookService</namespace> <serviceClass>org.codehaus.xfire.demo.BookService</serviceClass> <inHandlers> <handler handlerClass="org.codehaus.xfire.util.dom.DOMInHandler" /> <bean class="org.codehaus.xfire.security.wss4j.WSS4JInHandler" xmlns=""> <property name="properties"> <props> <prop key="action">Encrypt</prop> <prop key="decryptionPropFile">META-INF/xfire/insecurity_enc.properties</prop> <prop key="passwordCallbackClass">org.codehaus.xfire.demo.PasswordHandler</prop> </props> </property> </bean> </inHandlers> </service> SignatureAllows one to send along with the message a digital signature of it, which assures that no one modified the message content between the sender and receiver. This action creates a digest of the message and encrypts it with sender private key. The receiver must have the sender's public key to verify this signature. Client side configuration: protected void configureOutProperties(Properties properties) { properties.setProperty(WSHandlerConstants.ACTION,WSHandlerConstants.SIGNATURE); // User in keystore properties.setProperty(WSHandlerConstants.USER, "client-344-839"); // This callback is used to specify password for given user for keystore properties.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, org.codehaus.xfire.demo.PasswordHandler.class.getName()); // Configuration for accessing private key in keystore properties.setProperty(WSHandlerConstants.SIG_PROP_FILE,"org/codehaus/xfire/client/outsecurity_sign.properties"); properties.setProperty(WSHandlerConstants.SIG_KEY_ID,"IssuerSerial"); } WSHandlerConstants.SIG_KEY_ID specify what key identifier should be used ( possible values are : "IssuerSerial" ( recommended ) and "DirectReference" ). Configuration file can look like: 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=keystorePass org.apache.ws.security.crypto.merlin.alias.password=aliaspass org.apache.ws.security.crypto.merlin.keystore.alias=alias org.apache.ws.security.crypto.merlin.file=META-INF/xfire/myPrivatestore.jks Server side configuration: <!-- ============= Signature ============= --> <service> <name>BookServiceSign</name> <namespace>http://xfire.codehaus.org/BookService</namespace> <serviceClass>org.codehaus.xfire.demo.BookService</serviceClass> <inHandlers> <handler handlerClass="org.codehaus.xfire.util.dom.DOMInHandler" /> <bean class="org.codehaus.xfire.security.wss4j.WSS4JInHandler" xmlns=""> <property name="properties"> <props> <prop key="action">Signature</prop> <prop key="signaturePropFile">META-INF/xfire/insecurity_sign.properties</prop> <prop key="passwordCallbackClass">org.codehaus.xfire.demo.PasswordHandler</prop> </props> </property> </bean> </inHandlers> </service> The file insecurity_sign.properties: 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=keystorePass org.apache.ws.security.crypto.merlin.keystore.alias=alias org.apache.ws.security.crypto.merlin.file=META-INF/xfire/myPublicstore.jks Using WS-Security with JSR 181 AnnotationsThis blog entry shows how to configure WS-Security using JSR 181 Annotations and InHandlers. |