Controls and Extensions |
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.
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 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 section.
Here is an example that uses Start TLS to perform secure operations and then stops TLS to do unprotected operations.
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 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.// 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();Once you have a context, you may initiate TLS at any time by first invoking the LdapContext.extendedOperation() method with an instance of StartTlsRequest. You then initiate the TLS handshake by using StartTlsResponse.negotiate(). 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() 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 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(). 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.
Using this verifier, you can now enter a name other than the fully qualified hostname. Here is an example.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 } }... 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 .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 connectionTLS with External SASL 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.
Client and Server Requirements: The LDAP server and Java client environment must satisfy the requirements detailed in the SSL and Custom Sockets section for client authentication.
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.// 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 connectionjava -Djavax.net.ssl.keyStore=MyKeystoreFile \ -Djavax.net.ssl.keyStorePassword=mysecret \ StartTlsExternalTo further customize how your application obtains its client certificates and determines trust relationships, write a custom socket factory class that implements the SSLSocketFactory abstract class. Then, use the StartTlsResponse.negotiate(SSLSocketFactory) 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:
- Continue on in this trail for information on how connections are managed.
- Go to the Frequently Asked Questions lesson to read about questions that LDAP users have when using the JNDI.
Controls and Extensions |