Please note: these instructions were given to us by a past student. We don't necessarily understand them, let alone guarantee that they're up to date and accurate:-)

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 Krb5 Login Module 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 an instance of class 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 instance, 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 {

       String user = 'xxxx';
       String password = 'yyyyy';   // much better to read these from a secure file..

       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];
                char passwordchars[] = password.toCharArray();
                pc.setPassword( passwordchars );
                for (int i = 0; i < password.length(); i++) passwordchars[i] = '*'; 

             } else throw new UnsupportedCallbackException
                (callbacks[i], "Unrecognised callback");

          }
       }
    }

username and password are String instance variables containing the username and password to authenticate with. You might want to write a MyCallbackHandler( the_username, the_password ) constructor that stores them in the instance variables, and read the password from somewhere else securely.

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 mysubject object you supplied earlier now holds a valid Kerberos ticket, which you can access if necessary with mysubject.getPrivateCredentials().

Note that, for added security, we've added a simple for loop to overwrite the password array, so that it doesn't stay intact in memory for all to see via process introspection techniques.