| Controls and Extensions

## The Start TLS Extension

> > Server Requirements and Setup: The LDAP server must support the Start TLS extension ("1.3.6.1.4.1.1466.20037") and be set up with an X.509 certificate. Typically, you must first obtain a signed certificate for the server from a certificate authority (CA). Then, follow the instructions from your vendor on how to enable SSL. Different vendors have different tools for doing this.

> > For the i-Planet Directory Server, v5, use the Manage Certificates tool in the Administration Console to add the certificate to the server's list of certificates. Also install the CA's certificate if it is not already in the server's list of trusted CAs. Enable SSL by using the Configuration tab in the Administration Console. Select the server in the left pane. Select the Encryption tab in the right pane. Click the checkboxes for "Enable SSL for this server" and "Use this cipher family: RSA", ensuring that the server certificate you have added is in the list of certificates.

> > Client Requirements and Setup: The examples in this section requires the Java 2 SDK, v1.4.

> > You must install the server's certificate in your Java Runtime (JRE)'s database of trusted certificates. Here is an example. JAVA_HOME is is the directory that contains the Java Runtime.

> >     # cd JAVA_HOME/lib/security
> >     # keytool -import -file server_cert.cer -keystore jssecacerts

> > For information on how to use the security tools, see the Java 2 Platform Security trail of the Java Tutorial.

> 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` 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()` 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.

> > Client Issue: Closing down the TLS connection doesn't work in the Java 2 SDK, v 1.4 beta. To stop this example from hanging, comment out the `tls.close()` statement.

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

> > The OpenLDAP server, upon receiving the `tls.close()`, will shutdown 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.

> >     class SampleVerifier implements CertificateHostnameVerifier {
> >         public boolean verify(String hostname, Certificate[] cert) {
> >     	System.out.println("Checking: " + hostname + " in");
> >     	for (int i = 0; i < cert.length; i++) {
> >     	    System.out.println(cert[i]);
> >     	}

> >     	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 or the External SASL mechanism if you want the server to use the TLS client credentials.

> 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

> For TLS, because the LDAP server already has 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

> There are several configuration steps that you must complete before running this example. We provider some details here for client side configuration but more comprehensive documentation and examples are available in the JSSE Reference Guide.

> First, you must obtain a client certificate from a CA. The certificate's certificate chain must end with a CA that the directory server trusts. It would also be useful if the subject DN in the certificate names a user in the directory server's namespace; otherwise, you need to map the DN to an identity that the directory server can use to perform access control.

> You then must import the certificate into a keystore using the `keytool` utility. Here is an example of how to create a keystore file named `cuserStore` that contains a certificate in PKCS12 format.

> >     # keytool -import -trustcacerts -alias cuser -file cuser.cert	\
> >     	-keystore cuserStore -keypass secret -storetype pkcs12

> You are then ready to perform configuration on the directory server. First, you must ensure that the client certificate chain ends with a CA that is on the the directory server's list of trusted CAs. This step is performed by using the directory server's administration tool and typically involves importing the CA's certificate into the server's list of trusted CAs. If the certificate's DN does not name an entry in the directory server's database, then you must provider a mapping between the DN and an identity that the directory server uses for performing access control. Again, this step is performed by using the directory server's administration tool.

> After both client and server configuration have been completed, you may run the example as follows if you are using Sun's JSSE security provider.

> >     #java -Djavax.net.ssl.keyStore=cuserStore
> >       -Djavax.net.ssl.keyStoreType=pkcs12
> >       -Djavax.net.ssl.keyStorePassword=secret \
> >       StartTlsExternal

> If you are using another JSSE security provider, check whether the `javax.net.ssl` properties are supported. If not, you need to use the `StartTlsResponse.negotiate(SSLSocketFactory)` method and supply your own implementation of `SSLSocketFactory` that uses the appropriate key and trust managers for supporting client authentication. See the JSSE Reference Guide for details.

> > Note: This example does not work on the Java 2 SDK, v 1.4 beta because the `StartTlsResponse` implementation in Sun's LDAP service provider ignores the `javax.net.ssl` properties.

> Controls and Extensions: End of Lesson

> What's next? Now you can:

> - Continue on in this trail to read about questions that LDAP users have when using the JNDI.
> - Go to the Storing Objects in the Directory lesson to learn how to store Java objects in the directory.
> - Go to the Reading Objects from the Directory lesson to learn how to read Java objects from the directory.
> - Go to the Representation in the Directory lesson to read about the physical representation of Java objects in the LDAP directory.

| Controls and Extensions