Password Callback Handlers Explained

Archived Content
This article is provided for historical perspective only, and may not reflect current conditions. Please refer to relevant product page for more up-to-date product information and resources.
  • By Nandana Mihindukulasooriya
  • 22 Jun, 2008

Applies To

Project/Language version
Apache Axis2 / Java 1.4
Apache Rampart / Java 1.4

Table of Contents

What is a password callback handler ?

First thing you need to know about the password callback handler class, is that it can be any class that implements javax.security.auth.callback.CallbackHandler interface. javax.security.auth.callback.CallbackHandler has only one method which we can use to provide a password for a given identifier. These passwords may be retrieved from a database, from a protected configuration file or the password callback handler may even delegate this functionality to some other component within the application like a user manager component, where the user management functionality is centralized.

password callack image

Let's look at a very simple password callback handler implementation class. The following  PasswordCallbackHandler class implements the  javax.security.auth.callback.CallbackHandler interface that has only a simple method called handle().

package org.apache.rampart.tutorial;

import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import java.io.IOException;

public class PasswordCallbackHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
            String id = pwcb.getIdentifer();

	     //Pass the id to whatever the password retrieval logic and get the password 	
            // eg.	
            // String password = UserManager.getPassword(id);

            pwcb.setPassword(password);
  
        }
    }

}

How the password callback handler is configured in Apache Rampart ? 

There are two ways to configure a password callback handler in Apache Rampart. The most common way is to provide it as a RampartConfig property.

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
<ramp:passwordCallbackClass>org.apache.rampart.tutorial.PasswordCallbackHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>

The most important point is to have this password callback handler class available in the classpath, at the runtime. Password callback handler for the service can be bundled with the service archive with other service implementation classes. Password callback handler for the client need to be in client’s classpath.

The other way is to provide a password callback handler object as Message Context property. In the client side, you can easily do this using client's options.

Options options = client.getOptions();
options.put(WSHandlerConstants.PW_CALLBACK_REF, new PasswordCallbackHandler());

Here we are passing the password callback handler object as a property named org.apache.ws.security.handler. PW_CALLBACK_REF. “passwordCallbackClass” property in the RampartConfig takes precedence over this property.

How does the password callback handler work ?  

So let's look at how password callback handlers work. Whenever Rampart Engine needs a password to create a username token, whether it is to build a signature, validate username token or decrypt an encrypted content, it will create a  WSPasswordCallback instance setting the appropriate identifier which it extracts from the <ramp:user> parameter of the Rampart configuration and pass it to the password callback class via the handle method. Then password callback fills the password relevant to the given identifier so that the Rampart Engine can use it for further processing.

Different usages of the password callback handler

As you have may have already noticed Apache Rampart uses a single password callback handler for multiple functionalities in a polymorphic manner. For example, Rampart uses the same password callback handler to obtain the password of a client to build username token and additionally to obtain private key password of the client's key pair to retrieve the private key to build the signature. Password callback handler should be able to provide the correct password according to the usage in each of the above scenarios.  So how does the password callback handler know, which usage scenario is present at the point it is called? Usage data is passed to the password callback handler via a flag present in WSPasswordCallback instance that is created by the Rampart Engine. Password callback handler can then understand usage information using this flag.

WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
int usage = pwcb.getUsage()

Usage values are defined in the org.apache.ws.security.WSPasswordCallback Class. Let's look at most commonly values used.

WSPasswordCallback.USERNAME_TOKEN

Usage is set to USERNAME_TOKEN when the Rampart Engine wants to get the password to build a Username Token. Password callback handler is called with a  WSPasswordCallback instance where the identifier is set to the username and usage is set to USERNAME_TOKEN.

WSPasswordCallback.SIGNATURE

Usage value is SIGNATURE when the Rampart Engine wants to get the pass phrase of the private key of the key pair when it wants to create a signature in an out going message. Password callback handler is called with a  WSPasswordCallback instance where the identifier is set to the key alias of the key pair and usage is set to SIGNATURE.

WSPasswordCallback.DECRYPT

Usage value is set to DECRYPT when the Rampart Engine wants to get the pass phrase of the private key of the key pair when it wants to decrypt the encrypted content  of an incoming message. Password callback handler is called with a  WSPasswordCallback instance where the identifier is set to the key alias of the key pair and usage is set to DECRYPT.

WSPasswordCallback.USERNAME_TOKEN_UNKNOWN

Usage value is set to USERNAME_TOKEN_UNKNOWN when the Rampart Engine wants the password callback handler to validate the username and password in the Username Token. In this case, password callback handler is called with a  WSPasswordCallback instance, where the identifier is set to the username token and password is set to the password of the username token. Usage is set USERNAME_TOKEN_UNKNOWN. Password callback handler should validate the username and t password and should throw an exception if they are invalid.

if we summarize above four usages

Value WSPasswordCallback Expected behavior of 

Password callback handler
Usage
USERNAME_TOKEN identifier:username fill the password of the given username when building a username token or when validating a Username token where the password type is Digested Password
SIGNATURE identifier: private-key-alias fill the password of the private key for the given alias When creating a digital signature
DECRYPT identifier: private-key-alias fill the password of the private key for the given alias When decrypting encrypted data
USERNAME_TOKEN_UNKNOWN identifier: username

password: password
validate the given username and password and throw an Exception if they are invalid when validating a Username token where the password type is Digested Password

 

Why does username token validation is handled in two different ways ?

Excepted behavior of the password callback handler when validating username token depends on the password type of the username token. Password of the username token can be of two types.

1.) Plain text password

2.) Digested password

In the case of digested password, the password callback handler will fill the password for the given identifier and Rampart Engine recreate a digested password and will validate it with the digested password that was on the Username Token.  

In the case of plain text, the behavior changes slightly. Here Rampart Engine passes both identifier and the password to the password callback handler and the callback handler does the actual validation. This is useful if the service store the hash of the passwords of clients and not the real passwords. In this case, as Rampart providing the password in the Username Token to the callback handler, it can do hashing operation on it and check the hashed value with the hashed password associated with the username. For example this is the case if the service uses LDAP to authenticate the clients.

plain-text password 

Does the username and private key alias has to be the same always ?

You may have noticed that we provide the username and the key alias using the <ramp:user>identifier/ramp:user>. But there can be situations where the username and private key alias are different. Rampart handles addresses this situation using another configuration property. If your private key alias is different from the username, then you can use <ramp:userCertAlias>private-key-alias</ramp:userCertAlias>.

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
     <ramp:user>username</ramp:user>
     <ramp:userCertAlias>private-key-alias</ramp:userCertAlias>
     <ramp:passwordCallbackClass>org.apache.rampart.tutorial.PasswordCallbackHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>

Password callback handlers for common practical scenarios

Now, let's take a look at some practical scenarios, and how Rampart is configured for each of those scenarios. Let us also see how the Rampart password callback looks like for each of these scenarios.

Client side (Initiator) scenarios

In the client side, password callback handler is used for two main functions. They are:



1.) Get client password to build Username Tokens

2.) Get password for the client's private key which is used

  •      to create signatures in the request message
  •      to decrypt the encrypted content in the response message
scenario client username password for username token client private key alias private key password
scenario 1 client clientPW client clientPW
scenario 2 client clientPW client clientPKPW
scenario 3 client clientPW client-alias clientPKPW

 Scenario 1

This is the simplest scenario where both client's username and the key alias are "client" and  both password for both the cases is "clientPW".

Rampart Configuration

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
     <ramp:user>client</ramp:user>
     <ramp:passwordCallbackClass>org.apache.rampart.client.PasswordCallbackHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>

Password Callback handler implementation

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {
        
        WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[0];
        
        String id = pwcb.getIdentifer();
        // Logic to get the client password to build the username token
        if("client".equals(id)) {
            pwcb.setPassword("client");
        } 
        
    }
Scenario 2

In this scenario, still, both client's username and the key alias are "client", but this time password for the username token is different from the password for the private key.

Rampart Configuration

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
     <ramp:user>client</ramp:user>
     <ramp:passwordCallbackClass>org.apache.rampart.client.PasswordCallbackHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>

Password Callback handler implementation

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        
        WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[0];
        
        String id = pwcb.getIdentifer();
        int usage = pwcb.getUsage();
        
        if(usage == WSPasswordCallback.USERNAME_TOKEN ) {
            // Logic to get the password to build the username token
	    if ("client".equals(id)) {
                pwcb.setPassword("clientPW");
            }
        } else if (usage == WSPasswordCallback.SIGNATURE || usage == WSPasswordCallback.DECRYPT ) {
            // Logic to get the private key password for signature or decryption
	    if ("client".equals(id)) {
                pwcb.setPassword("clientPKPW");
            }
        } 
    }
Scenario 3

In this scenario, client's username and the key alias is different. So in the Rampart configuration, we need to provide them using two different parameters as given below.  Passwords for the two cases are also different.

Rampart Configuration

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
     <ramp:user>client</ramp:user>
     <ramp:userCertAlias>client-alias</ramp:userCertAlias>
     <ramp:passwordCallbackClass>org.apache.rampart.client.PasswordCallbackHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>

Password Callback handler implementation

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        
        WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[0];
        
        String id = pwcb.getIdentifer();
        int usage = pwcb.getUsage();
        
        if(usage == WSPasswordCallback.USERNAME_TOKEN ) {
            // Logic to get the password to build the username token
	    if ("client".equals(id)) {
                pwcb.setPassword("clientPW");
            }
        } else if (usage == WSPasswordCallback.SIGNATURE || usage == WSPasswordCallback.DECRYPT ) {
            // Logic to get the private key password for signture or decryption
	    if ("client".equals(id)) {
                pwcb.setPassword("clientPKPW");
            }
        } 
    }

Service side (Recipient) scenarios

In the server side, password callback is used for three main functions.

1.) Get the password of the client to validate Username Tokens in the request(Username Tokens with digested Passwords)

2.) To directly validate the username and password in the Username Tokens in the requests (Username Tokens with plain text Passwords)

3.) Get the password of the service's private key which is used to

  • decrypt the encrypted content in the request messages
  • create signatures in the response messages
scenario client username client  password

client

password type

service private key alias service private key password
scenario 4 client clientPW digested service servicePKPW
scenario 5 client clientPW plain text service servicePKPW

Scenario 4

In this scenario, it should be noted, that the password type in the username token is digested. So password callback handler only needs to provide the password for the client, to allow Rampart verify it with the password recived with the username token. Password callback is also need to provide private key password of the service's key.

Rampart Configuration

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
     <ramp:user>service</ramp:user>
     <ramp:passwordCallbackClass>org.apache.rampart.service.PasswordCallbackHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>

Password Callback handler implementation

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        
        WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[0];
        
        String id = pwcb.getIdentifer();
        int usage = pwcb.getUsage();
        
        if(usage == WSPasswordCallback.USERNAME_TOKEN ) {
            // Logic to get the password for the username token to validate the username token
	    if ("client".equals(id)) {
                pwcb.setPassword("clientPW");
            }
        } else if (usage == WSPasswordCallback.SIGNATURE || usage == WSPasswordCallback.DECRYPT ) {
            // Logic to get the private key password for signature or decryption
	    if ("service".equals(id)) {
                pwcb.setPassword("servicePKPW");
            }
        } 
    }

Scenario 5

In this scenario, the type of the password for the username token is plain text. Therefore in this case, password callback should validate the password against the username and throw an error in the case of an invalid password. It also needs to provide private key password of the service's key.

Rampart Configuration

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> 
     <ramp:user>service</ramp:user>
     <ramp:passwordCallbackClass>org.apache.rampart.service.PasswordCallbackHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>

Password Callback handler implementation

  public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        
        WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[0];
        
        String id = pwcb.getIdentifer();
        int usage = pwcb.getUsage();
        
        if(usage == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN ) {
            // Logic to verify the password in the username token
	    if ("client".equals(id) && "clientPW".equals(pwcb.getPassword())) {
		return;
	    } else {
		throw new UnsupportedCallbackException(callbacks[i], "authentication failed");
	    }
        } else if (usage == WSPasswordCallback.SIGNATURE || usage == WSPasswordCallback.DECRYPT ) {
            // Logic to get the private key password for signature or decryption
	    if ("service".equals(id)) {
                pwcb.setPassword("servicePKPW");
            }
        } 
    }

Conclusion

As you have seen from the examples described, complexity of the password callback handler depends on both the usage scenario and how passwords are retrieved. Having a good understanding of how password callback handler works will help you decide how best to use the handler, depending on very specific requirements of your enterprise scenario.

References

 

Author

Nandana Mihindukulasooriya, Software Engineer, WSO2 Inc. nandana AT wso2 DOT com

About Author

  • Nandana Mihindukulasooriya
  • Tech Lead
  • WSO2 Inc.