/* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive.ssl; import java.net.Socket; import java.util.Arrays; import javax.net.ssl.SSLSocket; import org.ldaptive.Connection; import org.ldaptive.ConnectionConfig; import org.ldaptive.DefaultConnectionFactory; import org.ldaptive.LdapURL; import org.ldaptive.SearchOperation; import org.ldaptive.SearchRequest; import org.ldaptive.provider.Provider; import org.testng.AssertJUnit; import org.testng.annotations.Parameters; import org.testng.annotations.Test; /** * Unit test for {@link TLSSocketFactory}. * * @author Middleware Services */ public class TLSSocketFactoryTest { /** List of ciphers. */ public static final String[] CIPHERS = new String[] { "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", /* GCM ciphers only supported in Java 8 "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_256_GCM_SHA384", */ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", /* GCM ciphers only supported in Java 8 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", */ "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", /* GCM ciphers only supported in Java 8 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", */ }; /** List of ciphers. */ public static final String[] UNKNOWN_CIPHERS = new String[] { /* one valid cipher, three invalid */ "TLS_DH_anon_WITH_AES_128_CBC_SHA", "TLS_DH_anon_WITH_3DES_256_CBC_SHA", "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", "SSL_DH_anon_WITH_RC4_128_MD5", }; /** List of protocols. */ public static final String[] ALL_PROTOCOLS = new String[] { "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", }; /** List of protocols. */ public static final String[] PROTOCOLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}; /** List of protocols. */ public static final String[] FAIL_PROTOCOLS = new String[] {"SSLv2Hello", }; /** List of protocols. */ public static final String[] UNKNOWN_PROTOCOLS = new String[] { /* one invalid protocol, two valid */ "SSLv3Hello", "SSLv2Hello", "TLSv1", }; /** * @return ssl config * * @throws Exception On configuration error. */ public SslConfig createSslConfig() throws Exception { final X509CredentialConfig config = new X509CredentialConfig(); config.setTrustCertificates("file:target/test-classes/ldaptive.trust.crt"); return new SslConfig(config); } /** * @param url to connect to * * @return connection configuration * * @throws Exception On connection failure. */ public ConnectionConfig createTLSConnectionConfig(final String url) throws Exception { final ConnectionConfig cc = new ConnectionConfig(url); cc.setUseStartTLS(true); cc.setSslConfig(createSslConfig()); return cc; } /** * @param url to connect to * * @return connection configuration * * @throws Exception On connection failure. */ public ConnectionConfig createSSLConnectionConfig(final String url) throws Exception { final ConnectionConfig cc = new ConnectionConfig(url); cc.setUseSSL(true); cc.setSslConfig(createSslConfig()); return cc; } /** * @param url to connect to * * @throws Exception On test failure. */ @Parameters("ldapTestHost") @Test(groups = {"ssl"}) public void connectTLS(final String url) throws Exception { // with no trusted certificates, connection should fail final ConnectionConfig cc = createTLSConnectionConfig(url); cc.setSslConfig(null); try (Connection conn = DefaultConnectionFactory.getConnection(cc)) { // some providers won't report errors until an operation is // executed conn.open(); final SearchOperation op = new SearchOperation(conn); op.execute(SearchRequest.newObjectScopeSearchRequest("")); AssertJUnit.fail("Should have thrown Exception, no exception thrown"); } catch (Exception e) { AssertJUnit.assertNotNull(e); } } /** * @param url to connect to * * @throws Exception On test failure. */ @Parameters("ldapSslTestHost") @Test(groups = {"ssl"}) public void connectSSL(final String url) throws Exception { // with no trusted certificates, connection should fail final ConnectionConfig cc = createSSLConnectionConfig(url); cc.setSslConfig(null); try (Connection conn = DefaultConnectionFactory.getConnection(cc)) { conn.open(); // some providers won't perform the handshake until an operation is // executed final SearchOperation op = new SearchOperation(conn); op.execute(SearchRequest.newObjectScopeSearchRequest("")); AssertJUnit.fail("Should have thrown Exception, no exception thrown"); } catch (Exception e) { AssertJUnit.assertNotNull(e); } } /** * @param url to connect to * * @throws Exception On test failure. */ @Parameters("ldapTestHost") @Test(groups = {"ssl"}) public void setEnabledCipherSuites(final String url) throws Exception { final ConnectionConfig cc = createTLSConnectionConfig(url); final TLSSocketFactory sf = new TLSSocketFactory(); sf.setSslConfig(cc.getSslConfig()); sf.initialize(); AssertJUnit.assertEquals( Arrays.asList(((SSLSocket) sf.createSocket()).getEnabledCipherSuites()), Arrays.asList(sf.getDefaultCipherSuites())); AssertJUnit.assertNotSame(Arrays.asList(sf.getDefaultCipherSuites()), Arrays.asList(CIPHERS)); cc.getSslConfig().setEnabledCipherSuites(UNKNOWN_CIPHERS); final Connection conn = DefaultConnectionFactory.getConnection(cc); try { conn.open(); AssertJUnit.fail("Should have thrown Exception, no exception thrown"); } catch (Exception e) { AssertJUnit.assertNotNull(e); } sf.getSslConfig().setEnabledCipherSuites(CIPHERS); sf.initialize(); AssertJUnit.assertEquals( Arrays.asList(((SSLSocket) sf.createSocket()).getEnabledCipherSuites()), Arrays.asList(CIPHERS)); } /** * @param url to connect to * * @throws Exception On test failure. */ @Parameters("ldapTestHost") @Test(groups = {"ssl"}) public void setEnabledProtocols(final String url) throws Exception { final Provider<?> p = DefaultConnectionFactory.getDefaultProvider(); final ConnectionConfig cc = createTLSConnectionConfig(url); final TLSSocketFactory sf = new TLSSocketFactory(); sf.setSslConfig(cc.getSslConfig()); sf.initialize(); AssertJUnit.assertNotSame( Arrays.asList(((SSLSocket) sf.createSocket()).getEnabledProtocols()), Arrays.asList(PROTOCOLS)); cc.getSslConfig().setEnabledProtocols(FAIL_PROTOCOLS); Connection conn = DefaultConnectionFactory.getConnection(cc); try { conn.open(); AssertJUnit.fail("Should have thrown Exception, no exception thrown"); } catch (Exception e) { AssertJUnit.assertNotNull(e); } if ("org.ldaptive.provider.opendj.OpenDJProvider".equals(p.getClass().getName())) { // grizzly ignores unknown protocols AssertJUnit.assertTrue(true); } else { cc.getSslConfig().setEnabledProtocols(UNKNOWN_PROTOCOLS); conn = DefaultConnectionFactory.getConnection(cc); try { conn.open(); AssertJUnit.fail("Should have thrown Exception, no exception thrown"); } catch (Exception e) { AssertJUnit.assertNotNull(e); } } sf.getSslConfig().setEnabledProtocols(PROTOCOLS); sf.initialize(); AssertJUnit.assertEquals( Arrays.asList(((SSLSocket) sf.createSocket()).getEnabledProtocols()), Arrays.asList(PROTOCOLS)); } /** * @param url to connect to * * @throws Exception On test failure. */ @Parameters("ldapSslTestHost") @Test(groups = {"ssl"}) public void setSocketConfig(final String url) throws Exception { final SocketConfig sc = new SocketConfig(); sc.setKeepAlive(true); sc.setReuseAddress(true); sc.setTcpNoDelay(true); sc.setReceiveBufferSize(256); sc.setSendBufferSize(256); sc.setSoLinger(100); sc.setSoTimeout(500); sc.setTrafficClass(0x10); final LdapURL ldapUrl = new LdapURL(url); final TLSSocketFactory sf = new TLSSocketFactory(); sf.setSocketConfig(sc); sf.initialize(); try (Socket s = sf.createSocket(ldapUrl.getEntry().getHostname(), ldapUrl.getEntry().getPort())) { AssertJUnit.assertNotNull(s); } } }