Aug 18, 2009

Securing a Web Service with Username Token + HTTPS with Apache Rampart

This is the first of a series of blogs about using Rampart effectively for securing web services. In this blog, I am explaining how to use an Username Token with transport level security to secure a web service. You can find all the resources related to this post here.

Securing Approach

Username Token is a Signed Supporting token that can be used to send the username/password to the other end. Then the recipient can check whether this is coming from a valid user. In this example we are are achieving transport level security through HTTPS. In HTTPS the service and the client are communicating through a pre-established a secured tunnel. So the confidentiality and the integrity of the messages are protected.

Security Policy Used


Following listing is the security policy that we are using in this blog.

    
        
            
                
                    
                        
                            
                        
                    
                    
                        
                            
                        
                    
                    
                        
                            
                        
                    
                    
                
            
            
                
                    
                
            
        
    

If you observe this security policy carefully, you will notice that two main policy assertions are specified. TransportBinding assertion is used to enforce the transport level security. HttpsToken inside the Transport Token declares the requirement of using HTTPS. SignedSupportingTokens assertion contains the UsernameToken assertion which establishes the requirement of providing a username token.

In the client side, a custom tag is appended to the policy.

   alice
 org.apache.rampart.utSample.PWDCBHandler


Here I am using the policy based configuration approach instead of programmatically setting the parameters. I am setting the alice as the user and org.apache.rampart.utSample.PWDCBHandler as the password callback handler. So the alice and the password returned from the password callback handler will be sent to the server as the username/password pair.

In the server side I am setting the Password Callback handler to be used for verifying the password.

    org.apache.rampart.utSample.PWDCBHandler


These custom assertions will not be appearing in the WSDL. They are used within Rampart to get the configuration details.

Creating the service

We are using a simple "echo" service as the service. The service class that we are using in this blog is given below.
public class SimpleService {
  public String echo(String arg) {
    return arg;
  }
}

You can find the sample01.aar file that contains this service in the resources. Deploy it in Tomcat + Axis2 or in WSO2 Web Services Application Server (WSO2 WSAS). If you are using Tomcat, you must enable SSL first. This guide will take you through the steps required to enable SSL in Tomcat.

Client Code
package org.apache.rampart.utSample;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.neethi.Policy;
import org.apache.neethi.PolicyEngine;
import org.apache.rampart.RampartMessageData;

public class UTClient {

    public static void main(String[] args) throws Exception {

        String repo = "/path/to/repo";
        String trustStore = "/path/to/server's_keystore";
        String storePass = "keyStore_passwprd";
        String policyFile = "/path/to/UT-Policy.xml";
        String EPR = "https://localhost:8443/axis2/services/sample01";

        System.setProperty("javax.net.ssl.trustStore", trustStore);
        System.setProperty("javax.net.ssl.trustStorePassword", storePass);

        ConfigurationContext ctx = ConfigurationContextFactory.createConfigurationContextFromFileSystem(repo, null);

        ServiceClient client = new ServiceClient(ctx, null);
        Options options = new Options();
        options.setAction("urn:echo");
        options.setTo(new EndpointReference(EPR));
        options.setProperty(RampartMessageData.KEY_RAMPART_POLICY, loadPolicy(policyFile));
        client.setOptions(options);

        client.engageModule("addressing");
        client.engageModule("rampart");

        OMElement response = client.sendReceive(getPayload("Hello world"));

        System.out.println(response);

    }

    private static Policy loadPolicy(String xmlPath) throws Exception {
        StAXOMBuilder builder = new StAXOMBuilder(xmlPath);
        return PolicyEngine.getPolicy(builder.getDocumentElement());
    }

    private static OMElement getPayload(String value) {
        OMFactory factory = OMAbstractFactory.getOMFactory();
        OMNamespace ns = factory.createOMNamespace("http://sample01.policy.samples.rampart.apache.org", "ns1");
        OMElement elem = factory.createOMElement("echo", ns);
        OMElement childElem = factory.createOMElement("param0", null);
        childElem.setText(value);
        elem.addChild(childElem);

        return elem;
    }

}

Lets walk through the important code segments of the Client.

Following code lines are used to set the trust store properties. Since we are using HTTPS as the transport, we need to trust the certificate used by the server. So I am adding the keystore used by the server as a trusted store.
System.setProperty("javax.net.ssl.trustStore", trustStore);
System.setProperty("javax.net.ssl.trustStorePassword", storePass);

Then you need to specify a repository in order to locate the module archives of Rampart, Addressing and Rahas. This tutorial explains how to create a Axis2 Repository.
ConfigurationContext ctx = ConfigurationContextFactory.createConfigurationContextFromFileSystem(repo, null);

You should engage Rampart and Addressing modules in the client side. You should point to a valid repository in the previous step in order to successfully engage these modules.
client.engageModule("addressing");
client.engageModule("rampart");

Following is the Password Callback handler  I am using at the client side.
package org.apache.rampart.utSample;

import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;

public class PWDCBHandler implements CallbackHandler {
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback cb = (WSPasswordCallback) callbacks[0];
        if ("alice".equals(cb.getIdentifier())) {
            cb.setPassword("bobPW");
        } else {
            cb.setPassword("bobPW");
        }
    }
}

This class implements the javax.security.auth.callback.CallbackHandler interface .

All these source codes can be found with the resources.

Request & Response

Since we are using SSL, only the encrypted form of the request and response can be captured. But the request and response in plain text should appear as follows.

Request


    
        
            
                2009-08-18T15:12:58.053Z
                2009-08-18T15:17:58.053Z
            
            
                alice
                
                    bobPW
                
            
        
        http://localhost:8081/axis2/services/sample01
        urn:uuid:B429936843779AAE931250608376908
        urn:echo
    
    
        
            Hello world
        
    


Observe the UsernameToken element in the security header. It contains the username/password pair.

Response


    
        
            
                2009-08-18T15:12:58.544Z
                2009-08-18T15:17:58.544Z
            
        
        urn:echoResponse
        urn:uuid:B429936843779AAE931250608376908
    
    
        
            Hello world
        
    


Now we have gone through all the steps required to invoke a service with username token over HTTPS.

All the source files, policy files, and other resources can be found here.

4 comments:

Datagenn said...

It is not possible to access the resource files listed on any of your pages.

vietanh said...

can you send to me that resources. My email : pvanh80@gmail.com

Thanks

jeff said...

Can you send to me that resources too. MY mail is: keinplan48@googlemail.com

Lauraine said...

Thanks for sharing your info. I really appreciate your efforts and I will be waiting for your further write ups thanks once again.
html5 development

Post a Comment