Password Callback Handlers Explained
Submitted on June 22, 2008 - 09:21. Story : Level : Project : Realm :
Apache Rampart is the Axis2 module that provides WS-Security functionality to Axis2 Web services and their clients. Apache Rampart configuration allows providing a password callback handler class, that can be used to provide passwords needed for Rampart engine to build username tokens and create signatures when sending messages. It is also used to provide passwords required to validate incoming username tokens and decrypt encrypted content in incoming messages. In this tutorial, Nandana Mihindukulasooriya will go through possible usage scenarios of password callback handler and describe how to configure Rampart and write password callback handlers to suit each of those usage scenarios.
Applies To
| Project/Language | version |
| Apache Axis2 / Java | 1.4 |
| Apache Rampart / Java | 1.4 |
Table of Contents
- What is a password callback handler ?
- How password callback handler is configured in Apache Rampart ?
- How does password callback handler work ?
- Different usages of password callback handler
- Why does username token validation is handled in two different ways ?
- Does the username and private key alias has to be the same always ?
- Password callback handlers for common practical scenarios
- Conclusion
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.

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.
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
- Web Services Security with Apache Rampart – Part 1 ( Transport Level Security )
- Web Services Security with Apache Rampart – Part 2 (Message-Level Security)
Author
Nandana Mihindukulasooriya, Software Engineer, WSO2 Inc. nandana AT wso2 DOT com
- Login or register to post comments
- Printer friendly version
- 1542 reads











PWCBHandler Class (server side)
Hi Nandana.
First of all, thank you so mutch: with your tutorial I start to understand (just a little bit! :)) how I can run Axis2 client - server system with Rampart. I have some problem now.
1°) In my PWCBHandler class (server side) I take RESERVED data from DB; I need for these data in my service class (the class where is the method that implements the service). Where can I store these data so that I can use these in my business logic? Is there any data structure in any Axis2 scope that gives me a solution?
2°) Could you say how I can access to http request data (e.g. used browser, language, encoding, ip address...). Is it possible?
Thank you in advance for your assistance in this matter and.. sorry for my bad english! :)