9

I'm using JNDI to make LDAP connections. Example:

Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, LDAPS_URL);
env.put(Context.SECURITY_AUTHENTICATION, "EXTERNAL");
env.put("java.naming.ldap.factory.socket", "ldaptest.CustomSocketFactory");
...

I need to pass parameters at run time to the CustomSocketFactory. Specifically a reference to a client certificate.

How can I do this? I could use thread local storage.

Is there a better way?

Conor
  • 2,419
  • 3
  • 19
  • 18

3 Answers3

3

I think that you may be looking for something like this:

env.put("javax.net.ssl.keyStore", keystorePath);
//Where keystorePath is the path to the Keys file resource

env.put("javax.net.ssl.keyStorePassword", "password");
bharath
  • 14,283
  • 16
  • 57
  • 95
icrovett
  • 435
  • 7
  • 21
  • This approach only allows one cert to be used at run time. I'm managing my own key store (with key managers and trust managers) and I need to manage and supply more than one cert to different ldap servers. – Conor Jun 21 '11 at 14:13
  • 1
    ok, that was all my knowledge on that, sorry couldnt help, i will keep an eye on this question. – icrovett Jun 21 '11 at 15:56
3

Actually thread local is only way to solve this I found so far. I posted my solution here:

jndi LDAPS custom HostnameVerifier and TrustManager

Community
  • 1
  • 1
Steffen Heil
  • 4,286
  • 3
  • 32
  • 35
1

As far as I know, all you can do is pass a class name (see icrovett's answer) and JNDI will call the getDefault() method via reflection. So you can't really pass any arguments there. So I think there is no nice and clean solution.

For my needs, I've built a SelectiveLdapSslSocketFactory, which contains a static Map mapping hosts to different SSLSocketFactories. First, you can register your custom SSLSocketFactories for different hosts. Then, when any of the createSocket(...) methods is called, it knows the host the socket is created for so the call can be delegated to the corresponding SSLSocketFactory. It also contains a defaultSslSocketFactory used for hosts without any mapping and also in the getDefaultCipherSuites() and getSupportedCipherSuites() methods. I'm not sure, if it's completely correct but it works fine for me so test it if you like:

public class SelectiveLdapSslSocketFactory extends SSLSocketFactory {

    private static SSLSocketFactory defaultSslSocketFactory;
    private static final Map<String, SSLSocketFactory> hostToSslSocketFactoryMap = new HashMap<>();
    
    {
        try {
            defaultSslSocketFactory = <yourOwnDefaultSslSocketFactory>;
        } catch (Exception ex) {
            Logger.warn(ex, "Couldn't initialize a defaultSslSocketFactory for LDAP connections!");
        }
    }

    public static SSLSocketFactory getRegisteredSslSocketFactory(String host) {
        return hostToSslSocketFactoryMap.get(host);
    }

    public static void registerSslSocketFactory(String host, SSLSocketFactory sslSocketFactory) {
        hostToSslSocketFactoryMap.put(host, sslSocketFactory);
    }

    public static void deregisterSslSocketFactory(String host) {
        hostToSslSocketFactoryMap.remove(host);
    }

    public SelectiveLdapSslSocketFactory() {
    }

    public static SocketFactory getDefault() {
        return new SelectiveLdapSslSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return defaultSslSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return defaultSslSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        SSLSocketFactory sslSocketFactory = Objects.requireNonNullElse(hostToSslSocketFactoryMap.get(host), defaultSslSocketFactory);
        return sslSocketFactory.createSocket(s, host, port, autoClose);
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        SSLSocketFactory sslSocketFactory = Objects.requireNonNullElse(hostToSslSocketFactoryMap.get(host), defaultSslSocketFactory);
        return sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        SSLSocketFactory sslSocketFactory = Objects.requireNonNullElse(hostToSslSocketFactoryMap.get(host), defaultSslSocketFactory);
        return sslSocketFactory.createSocket(host, port, localHost, localPort);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        SSLSocketFactory sslSocketFactory = getSslSocketFactory(host);
        return sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        SSLSocketFactory sslSocketFactory = getSslSocketFactory(address);
        return sslSocketFactory.createSocket(address, port, localAddress, localPort);
    }

    private SSLSocketFactory getSslSocketFactory(InetAddress inetAddress) {
        SSLSocketFactory sslSocketFactory = hostToSslSocketFactoryMap.get(Objects.requireNonNullElse(inetAddress.getCanonicalHostName(), ""));
        if (sslSocketFactory == null) {
            sslSocketFactory = hostToSslSocketFactoryMap.get(Objects.requireNonNullElse(inetAddress.getHostName(), ""));
            if (sslSocketFactory == null) {
                sslSocketFactory = hostToSslSocketFactoryMap.get(Objects.requireNonNullElse(inetAddress.getHostAddress(), ""));
                if (sslSocketFactory == null) {
                    sslSocketFactory = defaultSslSocketFactory;
                }
            }
        }
        return sslSocketFactory;
    }
}

You can then use it like:

...
SelectiveLdapSslSocketFactory.registerSslSocketFactory(host01, sslSocketFactory01);
SelectiveLdapSslSocketFactory.registerSslSocketFactory(host02, sslSocketFactory02);
SelectiveLdapSslSocketFactory.registerSslSocketFactory(host03, sslSocketFactory03);

props.put("java.naming.ldap.factory.socket", SelectiveLdapSslSocketFactory.class.getName());
tomorrow
  • 1,260
  • 1
  • 14
  • 26