Department of Computing Imperial College London
Kerberos authentication using Java

There are a number of steps which must be taken in order to enable a Java class for Kerberos authentication. The first is to let Java know we're using Kerberos for authentication. This is done in the file ~/.java.login.config, although you could use a different file if you have sufficient privileges to edit ${java.home}/jre/lib/security/java.security.

The file should look like this:

EntryName {
   com.sun.security.auth.module.Krb5LoginModule required;
};

Where EntryName is a name we will reference later from the Java code. The file can of course contain multiple entries like the one shown.

There are some options you can use to modify the behaviour of the module. Probably the most important is useTicketCache. When set to true, it causes the module to look in the ticket cache for a valid ticket first before prompting for a username and password. If it finds a ticket it can use, it'll just use that. The default is false, which means always prompt for the username and password. There are detailed explanations of all the possible options at the Krb5LoginModule Javadoc, but if you just need useTicketCache, simply change the configuration line to:

com.sun.security.auth.module.Krb5LoginModule required useTicketCache=true;

Once the security config file is set up, the next thing is to write the Java code itself. You will need the follow import directives to use Kerberos:

import javax.security.auth.*;
import javax.security.auth.login.*;
import javax.security.auth.callback.*;
import javax.security.auth.kerberos.*;

The next thing you need is a Subject to authenticate. Usually this will be enough:

Subject mysubject = new Subject();

However, if you are using the ticket cache and the above code, Kerberos will simply authenticate the user with a ticket from the cache and get a new ticket based on that, without prompting for a username or password. Depending on what you're trying to do, this could be good or bad.

The next thing is to create a LoginContext, which is a way of communicating with the authentication module.

LoginContext lc;

try {
   lc = new LoginContext("EntryName", mysubject, new MyCallbackHandler());
} catch (LoginException e) {
   // If an exception is caused here, it's likely the ~/.java.login.config file is wrong
}

Where MyCallbackHandler is the class you want the authentication module to ask for the username and password. Even if you want to use the ticket cache, you still need to supply this in case the cache is empty. The suggested code is as follows:

class MyCallbackHandler implements CallbackHandler {
   public void handle(Callback[] callbacks)
      throws IOException, UnsupportedCallbackException {

      for (int i = 0; i < callbacks.length; i++) {
         if (callbacks[i] instanceof NameCallback) {
            NameCallback nc = (NameCallback)callbacks[i];
            nc.setName(username);
         } else if (callbacks[i] instanceof PasswordCallback) {
            PasswordCallback pc = (PasswordCallback)callbacks[i];
            pc.setPassword(password.toCharArray());
         } else throw new UnsupportedCallbackException
            (callbacks[i], "Unrecognised callback");
      }
   }
}

username and password are Strings containing the username and password to authenticate with. You could have the callback handler as an inner class and then use global variables to pass the Strings, but a better way to do it would be to write a constructor that takes the two Strings and have them as properties of the MyCallbackHandler class.

Going back to the main code, the final step is to call the login() method of the Logincontext class (called lc in my code).

try {
   lc.login();
} catch (LoginException e) {
   // Bad username/password
}

If no exception is thrown, the user is now authenticated and the Subject class you supplied earlier now holds a valid Kerberos ticket, which you can access if necessary with mysubject.getPrivateCredentials(). For added security, it is recommended that you have a simple for loop to overwrite the password String, so that it doesn't stay intact in memory for all to see. I've written all the other code for you, so I thought I might as well do this too.

char passwordchars[] = password.toCharArray();
for (int i = 0; i < password.length(); i++) passwordchars[i] = '*';

© CSG / 2004 / help@doc