English 中文(简体)
LDAPS - shouldn t certificate be assign to exactly the same hostname to which I try to connect?
原标题:

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
问题回答

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.





相关问题
How to avoid plain password in mod_ldap

There are a plain password store in mod_ldap. Is there any way to encrypt the password? AuthLDAPBindPassword password Thank You.

How to specify a baseDN when connecting to LDAP via python?

I want to connect to a ldap server with python-ldap using a specific baseDN. import ldap baseDN="ou=unit,o=org.c=xx" # doesn t work #baseDN="" # works host="ldaps://test.org.xx:636" userDN="cn=...

LDAP Active Directory path

I am trying to add a user to Active Directory through an MPS Web Service. I ve been trying a long time to find the correct LDAP-url to use to tell it to add the new user to the Users group. I ve ...

Using JavaScript to get an LDAP multi-valued string attribute

I am trying to retrieve an object attribute in Active Directory that appears to be a multi-valued string (See canonicalName). After performing a search: var conn; conn.Open = Provider=ADsDSOObject; ...

Authenticate against Active Directory/ISA from php [closed]

I have a complicated problem, exacerbated by the fact I don t really know where to start! Over the last few years, I ve developed a number of php web-based systems. When I built them, our network was ...

热门标签