Friday, February 15, 2013

Apache CXF and WS-Security: how to encrypt a SOAP client request using an X.509 certificate

There are cases where communication data are too sensitive to travel over the network in a plain form, exposing them to the risk of being intercepted by a packet analyzer.

In such a case, a Web Service may require that the communication between client and server should be encrypted.

The WS-Security standard protocol allows a variety of encryption formats and algorithms, and is open to various security token models.

This article tries to explain how to achieve such a level of confidentiality using a X.509 certificate.

Introduction

To encrypt a SOAP communication using this model, as one might imagine, you need the X.509 certificate of the server that provides the Web Service. Actually the X.509 certificate contains a public key (signed by a Certification Authority, but here we are out of the scope of this article), that we will use to cipher the Body part of our SOAP request.

After the encryption, the obfuscated data can be reversed in its plain form exclusively by the owner of the private key which is bound to the public key that has been used to cipher it. This is called, as the matter of the fact, Public-key cryptography.

Preparing the Keystore

Once we have the X.509 certificate of the server (say called server.cer), we need to prepare a Keystore file (that we will call keystore.jks) that can be used in our Java program. To do that, we will use the command line tool keytool, which is automatically installed in the system with the JDK setup:

keytool -import -file server.cer -keystore keystore.jks -alias myAlias -storepass 123456

A keystore.jks file will be finally created. It will be protected by the password 123456 , and will actually contains the server X.509 certificate, retrievable with the myAlias alias.

Note

Obviously, you are encouraged to use a more respectable password than 123456, but since the keystore, for this specific purpose, contains just public information, a secure password is not really needed.

The Java IDE part

Place the keystore.jks file in the classpath of your application.

Create also a plain text file called client_encryption.properties, with the following content:

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

The Java code

Here is the code:

 MyClassWS myClassWS;

 Service service = Service.create(SERVICE_NAME);

 service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress);

 myClassWS = service.getPort(MyClassWS.class);

 JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
 factory.getInInterceptors().add(new LoggingInInterceptor());
 factory.getOutInterceptors().add(new LoggingOutInterceptor());
 factory.setServiceClass(MyClassWS.class);
 factory.setAddress(endpointAddress);
 myClassWS = (MyClassWS) factory.create();
 
 Client client = ClientProxy.getClient(myClassWS);
 
 
 // Creates the properties Map
 Map<String, Object> outEncryptionProps = new HashMap<String, Object>();
 
 // The configuration file we created. It must be in the classpath.
 outEncryptionProps.put(WSHandlerConstants.ENC_PROP_FILE, "client_encryption.properties");
 
 outEncryptionProps.put(WSHandlerConstants.ENC_KEY_ID, "X509KeyIdentifier");

 // Now you can configure peculiar properties about the encryption method that the Web Service requires.
 // The following are popular encryption algorithms:
 outEncryptionProps.put(WSHandlerConstants.ENC_SYM_ALGO, "http://www.w3.org/2001/04/xmlenc#aes128-cbc");
 outEncryptionProps.put(WSHandlerConstants.ENC_KEY_TRANSPORT, "http://www.w3.org/2001/04/xmlenc#rsa-1_5");
 
 outEncryptionProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT);
 
 // The alias of the x.509 certificate contained in the keystore
 outEncryptionProps.put(WSHandlerConstants.ENCRYPTION_USER, "myAlias");
 
 WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);

 Endpoint endPoint = client.getEndpoint();
 endPoint.getOutInterceptors().add(wssOut);
 endPoint.getOutInterceptors().add(new SAAJOutInterceptor());

Since now, each SOAP request will be encrypted with the x.509 certificate contained in the keystore. By default, only the BODY part of the request will be ciphered.

If you want to encrypt different parts of the SOAP message, you can let the CXF framework know it with the property key WSHandlerConstants.ENCRYPTION_PARTS.

For example, if your client need to present itself with a crypted UsernameToken (as well as with an encrypted Body), you could instruct CXF by adding the following property pair:

 outPlainProps.put(WSHandlerConstants.ENCRYPTION_PARTS, "{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body; {Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}UsernameToken;");

1 comment:

  1. Hello, Look at this blog, we provide another example of how to do that.
    http://www.systemdeveloper.info/2013/11/digital-signature-in-c.html

    ReplyDelete