views:

140

answers:

1

Hi,

I try to connect to an LDAP server by LDAPS protocol, and I define the URL using the IP of the server.

The keystore I use contain a certificate which was issued to this server, but the subject DN contain the hostname.

Shouldn't it fail?

Here is the code:

public class LdapConnection
{

    private String host = "1.2.3.4"; //the correct ip...
    private String baseDn = "dc=x,dc=y,dc=com"; //the correct base DN

    private String username = "myUsername";
    private String password = "myPassword";

    private String connectionUrl = null;

    public void connectLdaps() throws Exception
    {
        connectionUrl = "ldaps://" + host + "/" + baseDn;

        System.out.println("Trying to connect to " + connectionUrl + " using LDAPS protocol");

        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put("java.naming.ldap.derefAliases", "finding");
        env.put(Context.PROVIDER_URL, connectionUrl);
        env.put(Context.SECURITY_AUTHENTICATION, "Simple");
        env.put(Context.SECURITY_PRINCIPAL, username);
        env.put(Context.SECURITY_CREDENTIALS, password);
        env.put("java.naming.ldap.factory.socket", MySocketFactory.class.getName());

        new InitialLdapContext(env, null);

        System.out.println("Connected successfully!");
    }

    public static void main(String[] args) throws Exception
    {
        LdapConnection ldapConnection = new LdapConnection();
        ldapConnection.connectLdaps();
    }

}

public class MySocketFactory extends SocketFactory
{
    private static MySocketFactory instance = null;
    private SSLContext sslContext = null;

    private static String certFileName = "C:\\certs\\cert_by_hostname.cer";

    public static SocketFactory getDefault()
    {
        if (instance == null)
        {
            try
            {
                instance = new MySocketFactory();
                instance.initFactory();
            }
            catch (Exception e)
            {
                e.printStackTrace();
                System.out.println("Returning null socket factory");
            }
        }

        return instance;
    }

    private void initFactory() throws Exception
    {
        System.out.println("Initializing socket factory...");

        InputStream certStream = new FileInputStream(certFileName);
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        Certificate certificate = certificateFactory.generateCertificate(certStream);
        System.out.println("The certificate was generated. It is issued to " + ((X509Certificate)certificate).getSubjectDN());

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, "myPassword".toCharArray());
        new KeyStore.TrustedCertificateEntry(certificate);
        keyStore.setCertificateEntry("myCert", certificate);
        System.out.println("The Keystore was initialized.");

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
        trustManagerFactory.init(keyStore);
        System.out.println("The TrustManagerFactory was initialized.");

        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        System.out.println("The SSLContext was initialized.");

    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException
    {
        System.out.println("In create socket, host is " + host + " and port is " + port);
        return sslContext.getSocketFactory().createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException
    {
        return sslContext.getSocketFactory().createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException
    {
        return sslContext.getSocketFactory().createSocket(host, port, localHost, localPort);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
    {
        return sslContext.getSocketFactory().createSocket(address, port, localAddress, localPort);
    }

}

It is working fine, but as I explained above, I think that in this case it should fail...

What am I missing?

Thanks,

Dikla

EDIT: I want to emphasize that when I perform some small code changes and try to connect to LDAPv3 server, by using TLS, with URL which contain IP and certificate issued to hostname, I get the following exception:

Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: hostname of the server '1.2.3.4' does not match the hostname in the server's certificate.
    at com.sun.jndi.ldap.ext.StartTlsResponseImpl.verify(Unknown Source)
    at com.sun.jndi.ldap.ext.StartTlsResponseImpl.negotiate(Unknown Source)
    at ldapconnection.LdapConnection.connectTLS(LdapConnection.java:84)
    at ldapconnection.LdapConnection.main(LdapConnection.java:92)
Caused by: java.security.cert.CertificateException: No subject alternative names matching IP address 1.2.3.4 found
    at sun.security.util.HostnameChecker.matchIP(Unknown Source)
    at sun.security.util.HostnameChecker.match(Unknown Source)
    ... 4 more
A: 

When you bind to SSL via HTTP or LDAP, and use a keystore, it is the public key of the trusted root that signed your certificate that is needed. This does not matter what the hostname is.

From a Windows'y perspective, this means the trusted root that signed the certificate should be in the Windows keystore, called a Certificate Store in that instance.

For most other apps, it is a keystore of somekind. (Java based apps manipulate it with keytool)

So as long as the trusted root is known and by implication trusted, then it assumed that the cert chain is good and trustable.

So unless you are using a server certificate for mutual authentication (Unlikely in the case of plain LDAP over SSL or regular HTTP over SSL) then it should not matter, I would think.

geoffc