We are writing an application that shall connect to different LDAP servers. For each server we may only accept a certain certificate. The hostname in that certificate shall not matter. This is easy, when we use LDAP and STARTTLS, because we can use StartTlsResponse.setHostnameVerifier(..-)
and use StartTlsResponse.negotiate(...)
with a matching SSLSocketFactory
. However we also need to support LDAPS connections. Java supports this natively, but only if the server certificate is trusted by the default java keystore. While we could replace that, we still cannot use different keystores for different servers.
The existing connection code is as follows:
Hashtable<String,String> env = new Hashtable<String,String>();
env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( Context.PROVIDER_URL, ( encryption == SSL ? "ldaps://" : "ldap://" ) + host + ":" + port );
if ( encryption == SSL ) {
// env.put( "java.naming.ldap.factory.socket", "CustomSocketFactory" );
}
ctx = new InitialLdapContext( env, null );
if ( encryption != START_TLS )
tls = null;
else {
tls = (StartTlsResponse) ctx.extendedOperation( new StartTlsRequest() );
tls.setHostnameVerifier( hostnameVerifier );
tls.negotiate( sslContext.getSocketFactory() );
}
We could add out own CustomSocketFactory
, but how to pass information to that?
You should pass the name of own
SSLSocketFactory
subclass and pass its fully qualified named into the"java.naming.ldap.factory.socket"
env property, as described in the "Using Custom Sockets" section of the Java LDAP/SSL guide:You can't pass any specific argument to this class, see instantiation in
com.sun.jndi.ldap.Connection.createSocket(...)
:If you want additional parameters, you may have to use static members or JNDI perhaps (usually not ideal).
As far as I can tell, there doesn't seem to be any hostname verification when using
ldaps://
in this implementation unfortunately. If you only trust one explicit certificate in your trust manager, this should compensate for the lack of hostname verification anyway.For others have the same problem: I found a very ugly solution for my case:
Using it like this:
Not nice, but it works. JNDI should be more flexible here...