Consuming a UsernameToken with PasswordDigest

By default, the web services security UsernameToken consumer, UNTConsumeLoginModule, only can consume tokens with a PasswordType of #PasswordText. When custom code is in place, UNTConsumeLoginModule can also consume tokens with PasswordType of #PasswordDigest.

About this task

The application server has access to user names in the user registry, but it does not have access to passwords. To validate a digested password, the application server compares the digested password in the inbound message to the digest of the password that matches the user name in the message. Because the application server cannot retrieve the known password for the user name in the inbound message from the user registry, the default implementation cannot validate a digested password. To use password digest, implement custom code to retrieve the known password. The custom code makes the known password available to the UsernameToken consumer so it can perform the password validation. The developer of the custom code decides on the implementation of the password retrieval, which can be performed through various methods. These methods include hardcoding the password in the custom code or looking up the password in a table on the local file system or on a database lookup. The implementation of the custom password lookup does not matter if it returns the valid known password for the user name.

If you want the UNTConsumeLoginModule to consume UsernameTokens with PasswordType of #PasswordDigest, you must either provide a callback handler that returns the password or a custom JAAS login module that validates the digested password in the token. This task describes how to implement the callback handler. If you want to use a custom JAAS login module instead, see Replacing the authentication method of the UsernameToken consumer using a stacked JAAS login module for instructions.

When you use this callback handler method, after the run time receives the password from the callback handler, it will digest the password then compare it to the Password in the UsernameToken element. If they match, the password in the UsernameToken object is replaced with the undigested password. This means that the username/password pair can now be used for registry checking by the UNTConsumeLoginModule class or the LoginProcessor class if a caller is configured for the UsernameToken.

Avoid trouble: When you use the JAAS login module method to validate the token, the UsernameToken object is not modified in any way. This means that the token cannot be used as a caller as-is since the password in the UsernameToken object is still digested. If you want to use it as a caller, you also must implement Configuring a UsernameToken caller configuration with no registry interaction.

Procedure

  1. Develop a callback handler to retrieve the password and make it available to your application code. The callback handler must implement javax.security.auth.callback.CallbackHandler.
    The following example illustrates the password digest callback handler.
    package test.usertoken;
    
    import java.io.IOException;
    
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.callback.UnsupportedCallbackException;
    import javax.security.auth.callback.NameCallback;
    import javax.security.auth.callback.PasswordCallback;
    import com.ibm.websphere.wssecurity.wssapi.WSSUtilFactory;
    
    public class customDigestCallbackHandler implements CallbackHandler {
        //For the sake of readability, this class does not
        //protect against all NPE's
    
        @Override
        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
    
            NameCallback ncb = null;
            PasswordCallback pcb = null;
    
            if (callbacks == null || callbacks.length == 0) {
                throw new UnsupportedCallbackException(null, "There is no callback.");
            }
            for (int i = 0; i < callbacks.length; i++) {
                Callback callback = callbacks[i];
                if (callback instanceof NameCallback) {
                    ncb = (NameCallback)callback;               
                }
                if (callback instanceof PasswordCallback) {
                    pcb = (PasswordCallback)callback;               
                }
            }
    
            String username = ncb.getName();
            String password = yourCustomMethodToRetrievePassword(username);
    
            pcb.setPassword(password.toCharArray());
    
            return;     
        }
    }
  2. Configure your UsernameToken callback handler to use your password digest callback handler.
    1. In the administrative console, open the bindings configuration that you want to change.
    2. Click WS-Security > Authentication and protection.
    3. Under Authentication tokens, select the Username Token inbound token that you want to change.
    4. Click Callback handler.
    5. Set the following custom property to the name of your new custom callback handler class:
      com.ibm.wsspi.wssecurity.token.UsernameToken.digestPasswordCallbackHandler=test.usertoken.customDigestCallbackHandler
    6. Optional: If you want the digested password comparison to exclusively determine validity of the token and not do a username/password check against the user registry, add the following custom property:
      com.ibm.wsspi.wssecurity.token.UsernameToken.disableUserRegistryCheck=true
    7. Click OK.
  3. Click Save.
  4. Reload the bindings.
    • If you are using general bindings, restart the application server.
    • If you are using application-specific bindings, restart the application.
  5. Test your service.