Ldap Authentication using TLS in Java

by Mike

I was talking to Brophy about this a couple of weeks back and he told me that if I didn’t get it to work right away that I should give up and move on. I have to say, getting this authentication to work in Java is no fun, but I figure I would post some instructions up here and the pertinent code snippets as to give someone else a hand in the future.

First thing you have to know, is that you need a certificate for the TLS part. Get the certificate for the server from either the sys-admins of the system your doing this on, or grab it from its location on the server(I can’t really tell you this location because I went with option one).

Okay, so now you have your Certificate, the second thing you need on your server is Java’s keytool( see here ). The keytool manages a database of private keys and certificates and you need this to add your certificate to Java’s truststore so you can create a connection using your certificate.To use the keytool the command is:

 keytool -import -file cert -keystore .truststore

(cert being your certificate) It will then prompt you for a password, so enter one, and don’t forget it.

Okay, so now that we’ve got this far, lets get to the code. You can grab it here .
(I just updated this and haven’t tested it, so if you have any problems leave a comment)

import javax.naming.*;
import java.security.cert.*;
import java.util.Hashtable;
import javax.naming.ldap.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Security;
import java.io.UnsupportedEncodingException;
 
public class LdapAuth{
    private final String LDAPSERVER = "ldap://yourldapserver";
    private final String TSLOCATION = "filesystemlocationofyourtruststore";
    private final String TSPASSWORD = "passwordofyourtruststore";
 
    public LdapAuth(){}
 
    public boolean authenticate(String user, String pass){
        if((user == null) || (pass == null)){
            return false;
        }
        try {
            //set up the system property for trustStore
           System.setProperty("javax.net.ssl.trustStore", TSLOCATION);
           System.setProperty("javax.net.ssl.trustStorePassword", TSPASSWORD);
 
           Hashtable env = new Hashtable(11);
           env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
           env.put(Context.PROVIDER_URL, LDAPSERVER);
 
           //create initial LDAP context
           LdapContext ctx = new InitialLdapContext(env, null);
 
           //Use TLS to create a connection
           StartTlsResponse tls =
                (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
           tls.negotiate();
 
           //authenticate as user, with pass
           String bind = "uid=" + user; //other ldap settings would go here too
           ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
           ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, bind);
           ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, pass);
 
           /** for it to actually do the authentication you have to do something
               with the connection so we are going to get attributes here
               if you are not authenticated this throws an exception so it is
               a good test
           */
           ctx.getAttributes(bind);
 
           //stop TLS
           tls.close();
           //close LDAP Context
           ctx.close();
           //we got here so we are authenticated
           return true;
 
        }
        catch(AuthenticationException e){
            //you probably want to log this (e.toString)
            return false;
 
        }
        catch(NamingException e){
            //probably want to log this (e.toString)
            return false;
        }
        catch(IOException e){
            //and you probably want to log this
            return false;
        }
}
}

So really there isn’t much else to it, but I do want to mention just one thing.
These lines of code:

 System.setProperty("javax.net.ssl.trustStore",TSLOCATION);
System.setProperty("javax.net.ssl.trustStorePassword",TSPASS);

can be removed if you like and as a substitute it by running LdapAuth.java with:

 java -Djavax.net.ssl.trustStore=PATH_TO_CERTIFICATE/.truststore -Djavax.net.ssl.trustStorePassword=PASSWD_FROM_KEYTOOL_COMMAND LdapAuth

And I guess thats about it, all your authentication problems solved(we hope!). In the big scheme of things Java doesn't handle Authenticating against Ldap too well, but in retrospect it was much easier than I was expecting.

If you see any errors in the code or have any suggestions/questions leave a comment and let me know.

Stay Good,
Mike