A Java Active Directory JAAS example

I normally don't like to put source code out here that I can't support, but in this case I thought I'd make an exception, because I remember a former co-worker having a really hard time getting this Java Active Directory JAAS code working properly. It's been over two years since I got it working for him, so I don't remember what the problems were. Hopefully if you're trying to get Java working with Active Directory (using JAAS), this example source code will get you pointed in the right direction.

Unfortunately these days I don't even have a Windows computer to work with, let alone an Active Directory system, so I can't test this any more. All I can say is that I hope this code makes sense to you, and I hope it works properly. Here's the source code for a first Java / Active Directory / JAAS class:

package com.example.authentication.activedirectory;

import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

/**
 * Copyright Alvin Alexander, http://devdaily.com.
 * This code is shared here under the Attribution 3.0 Unported License.
 * See this URL for details: http://creativecommons.org/licenses/by/3.0/
 *
 * Validates a user against a Kerberos server 
 * VM arguments:
 *    -Djava.security.auth.login.config="C:/Working/ADTest/jaas.conf"
 */
public class ActiveDirectoryValidator
{
  
  private static final String USERNAME = "FOO";
  private static final String PASSWORD = "BAR";
  
  private ActiveDirectoryValidator()
  {
  }
  
  /**
   * ActiveDirectoryValidator sets the environment
   * @param realm (domain in all caps)
   * @param kdc (domain controller)
   * @param configurationFile (path to jaas.conf)
   * @param debug (boolean)
   */
  public ActiveDirectoryValidator(String realm, 
                      String kdc, 
                      String configurationFile,
                      boolean debug)
  {
    super();
    System.setProperty("java.security.auth.login.config", configurationFile);
    System.setProperty("java.security.krb5.realm", realm);
    System.setProperty("java.security.krb5.kdc", kdc);
    if (debug)
    {
      System.setProperty("java.security.krb5.debug", "true");
    }
    else
    {
      System.setProperty("java.security.krb5.debug", "false");
    }
  }

  /**
   * validateUser accepts userName & password and returns true if credentials
   * successfully authenticate to the domain.
   * @return boolean True=valid credentials
   */
  public boolean validateUser(String userName, String password)
  {
  try
    {
      //LoginContext lc = new LoginContext("JaasConfig", new ADCallbackHandler());
      LoginContext lc = null;
      ADCallbackHandler ch = new ADCallbackHandler();
      ch.setUserId(userName);
      ch.setPassword(password);
      //lc = new LoginContext(ValidateUser.class.getName(), ch);
      lc = new LoginContext("JaasConfig", ch);
      lc.login();
      return true;
    }
    catch (LoginException le)
    {
      System.err.println("Authentication failed:");
      System.err.println("  " + le.getMessage());
      return false;
    }
    catch (NullPointerException e)
    {
    System.err.println("Authentication failed:");
    System.err.println("  " + e.getMessage());
    return false;
    }
  }
  
  public static void main(String[] args) throws Exception
  {
    ActiveDirectoryValidator validateUser = new ActiveDirectoryValidator();
    if (validateUser.validateUser(USERNAME, PASSWORD))
    {
      System.out.print("Authentication Successful");
    }
    else
    {
      System.out.print("Authentication Failed");
    }
  }

}

Next, here's the source code for a second Java / Active Directory / JAAS class that implements the CallbackHandler interface:

package com.example.authentication.activedirectory;

import javax.security.auth.callback.*;
import java.io.IOException;

/**
 * Copyright Alvin Alexander, http://devdaily.com.
 * This code is shared here under the Attribution 3.0 Unported License.
 * See this URL for details: http://creativecommons.org/licenses/by/3.0/
 *
 * This is an implementation of CallbackHandler to pass credentials to ActiveDirectoryValidator.java.
 * See JAAS documentation for more info.
 */
public class ADCallbackHandler implements CallbackHandler
{
  private String ADUserId;
  private char[] ADPassword;

  public void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException
  {
    for (int i = 0; i < callbacks.length; i++)
    {
      if (callbacks[i] instanceof NameCallback)
      {
        NameCallback cb = (NameCallback)callbacks[i];
        cb.setName(ADUserId);
      }
      else if (callbacks[i] instanceof PasswordCallback)
      {
        PasswordCallback cb = (PasswordCallback)callbacks[i];
        cb.setPassword(ADPassword);
      }
      else
      {
        throw new UnsupportedCallbackException(callbacks[i]);
      }
    }
  }

  public void setUserId(String userid)
  {
    ADUserId = userid;
  }

  public void setPassword(String password)
  {
    ADPassword = new char[password.length()];
    password.getChars(0, ADPassword.length, ADPassword, 0);
  }

  public static void main(String[] args) throws IOException, UnsupportedCallbackException
  {
    // Test handler
    ADCallbackHandler ch = new ADCallbackHandler();
    ch.setUserId("test");
    ch.setPassword("test");

    Callback[] callbacks = new Callback[] { new NameCallback("user id:"), new PasswordCallback("password:", true) };

    ch.handle(callbacks);
  }
}

If this Java source code works for you -- or doesn't work -- please leave a comment below that may help other people who are in the same Java Active Directory boat.