Previous | Next | Trail Map | Tips for LDAP Users | Controls and Extensions

The Start TLS Extension


Server Requirements: The LDAP server must support the Start TLS extension ("1.3.6.1.4.1.1466.20037"). In addition, it must satisfy the server requirements detailed in the SSL and Custom Sockets (in the Tips for LDAP Users trail) section.

Client Requirements: The examples in this section requires the Java 2 SDK, v1.4. In addition, the Java client environment must satisfy the client requirements detailed in the SSL and Custom Sockets (in the Tips for LDAP Users trail) section.


The Start TLS extension allows an application to serialize secure and plain requests against an LDAP server on a single connection. For example, an application might use secure requests to make modifications to the directory and use plain requests to read parts of the directory that are open for unauthenticated browsing. To achieve this, the application uses the Start TLS extension to turn security on and off on demand.

Here is an example that uses Start TLS to perform secure operations and then stops TLS to do unprotected operations.

// Set up environment for creating initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// Must use the name of the server that is found in its certificate
env.put(Context.PROVIDER_URL, "ldap://ldap.jnditutorial.org:389/o=JNDITutorial");

// Create initial context
LdapContext ctx = new InitialLdapContext(env, null);

// Start TLS
StartTlsResponse tls =
    (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
SSLSession sess = tls.negotiate();

// ... do something useful with ctx that requires secure connection

// Stop TLS
tls.close();

// ... do something useful with ctx that doesn't require security

// Close the context when we're done
ctx.close();
You first set up the environment properties to use the LDAP service provider and to name the LDAP server. You must use the hostname that is found in the server's certificate as the hostname in the Context.PROVIDER_URL(in the API reference documentation) property; otherwise, the Start TLS negotiation will fail. (We show you how to loosen this restriction later in this section.) You then create the initial context using the two properties.

Once you have a context, you may initiate TLS at any time by first invoking the LdapContext.extendedOperation()(in the API reference documentation) method with an instance of StartTlsRequest(in the API reference documentation). You then initiate the TLS handshake by using StartTlsResponse.negotiate()(in the API reference documentation). If this method returns successfully, then TLS has been started on the context's connection. Any method you invoke on the context will use the security layer just negotiated to communicate with the LDAP server. After you are done with the TLS session, call StartTlsResponse.close()(in the API reference documentation) to terminate the TLS without closing the underlying network connection. Subsequently, you may continue to use the context to communicate with the LDAP server. Any such communication is not secure. You may reestablish secure communications by submitting another StartTlsRequest.

The StartTlsRequest and StartTlsResponse classes are closely tied to the Java Secure Socket Extension (JSSE). Familiarity with the JSSE will help you understand how to best use the methods in the StartTlsResponse class.


Note 1: The i-Planet Directory Server, v5.0, does not respond to the tls.close(), leaving the client blocked.

Note 2: The OpenLDAP server, upon receiving the tls.close(), will shut down the connection instead of downgrading it to a plain connection.


Hostname Verification

The Start TLS implementation uses a default verifier for checking that the server's certificate being used for TLS belongs to the LDAP server. The verifier matches the hostname of the server against the certificate subject's alternative names by ignoring case and taking into account wildcards in DNS names. For example, the hostname, "foo.bar.com", will match the certificate subject's alternative name, "*.BAR.COM". If none of the alternative names matches, the verifier does a case-ignore comparison of the hostname with the common name component of the certificate subject's distinguished name (DN).

The LDAP server's hostname is the hostname component of the URL specified in the Context.PROVIDER_URL(in the API reference documentation) property. Because the hostname found in a certificate is usually the fully qualified DNS hostname, you should supply in the URL the LDAP server's fully qualified DNS hostname. However, there may be times when you have no control over the form of hostname that you can use. Or, you might want to use matching rules different from those set forth by the default verifier. For example, you might need to use the LDAP server's IP address because the hostname is not resolvable by the naming server. To use your own verifier with Start TLS, you must create an instance of the verifier and install it by using StartTlsResponse.setHostnameVerifier()(in the API reference documentation). Here is an example of a verifier that prints its arguments and always returns true. This example is useful only for debugging and illustrative purposes and should never be used in a real program.

class SampleVerifier implements HostnameVerifier {
    public boolean verify(String hostname, SSLSession session) {
	System.out.println("Checking: " + hostname + " in");
	try {
	    Certificate[] cert = session.getPeerCertificates();
	    for (int i = 0; i < cert.length; i++) {
	        System.out.println(cert[i]);
	    }
        } catch (SSLPeerUnverifiedException e) {
	    return false;
        }

	return true; 	    // Never do this
    }
}
Using this verifier, you can now enter a name other than the fully qualified hostname. Here is an example.
...
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");

// Create initial context
LdapContext ctx = new InitialLdapContext(env, null);

// Start TLS
StartTlsResponse tls =
    (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());

// Install hostname verifier
tls.setHostnameVerifier(new SampleVerifier());

// Perform TLS negotiations
tls.negotiate();

TLS with Simple Authentication

In the examples shown thus far, only the LDAP server is being authenticated. To identify the client to the LDAP server, you may use any of the authentication mechanisms described in the Security Lesson (in the Tips for LDAP Users trail).

Here is an example that illustrates how to perform simple authentication. Note that the username and cleartext password are now encrypted because the authentication is being performed after establishment of the TLS session.

// Set up environment for creating initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// Must use the name of the server that is found in its certificate
env.put(Context.PROVIDER_URL, "ldap://ldap.jnditutorial.org:389/o=JNDITutorial");

// Create initial context
LdapContext ctx = new InitialLdapContext(env, null);

// Start TLS
StartTlsResponse tls =
    (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
tls.negotiate();

// Perform simple client authentication
// Authenticate as S. User and password "mysecret"
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, 
    "cn=S. User, ou=NewHires, o=JNDITutorial");
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, "mysecret");

// ... do something useful with ctx that requires secure connection

TLS with External SASL Authentication


Client and Server Requirements: The LDAP server and Java client environment must satisfy the requirements detailed in the SSL and Custom Sockets (in the Tips for LDAP Users trail) section for client authentication.
For TLS, because the LDAP server might already have a database of certificates, sometimes it is convenient to use certificates for client authentication. To use the TLS client's credentials for authentication, you use the External SASL mechanism. Here is an example.
// Set up environment for creating initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// Must use the name of the server that is found in its certificate
env.put(Context.PROVIDER_URL, "ldap://ldap.jnditutorial.org:389/o=JNDITutorial");

// Create initial context
LdapContext ctx = new InitialLdapContext(env, null);

// Start TLS
StartTlsResponse tls =
    (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
tls.negotiate();

// Perform client authentication using TLS credentials
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "EXTERNAL");

// ... do something useful with ctx that requires secure connection
To run this program, you need to specify the location and password of the keystore file that contains the client's certificate. Here is an example of how to run the program.
java -Djavax.net.ssl.keyStore=MyKeystoreFile \
    -Djavax.net.ssl.keyStorePassword=mysecret \
    StartTlsExternal

To further customize how your application obtains its client certificates and determines trust relationships, write a custom socket factory class that implements the SSLSocketFactory(in the API reference documentation) abstract class. Then, use the StartTlsResponse.negotiate(SSLSocketFactory)(in the API reference documentation) method to make use of the custom socket factory. See the JSSE Reference Guide for details on keystores, trust stores, and socket factories.

Controls and Extensions: End of Lesson

What's next? Now you can:


Previous | Next | Trail Map | Tips for LDAP Users | Controls and Extensions