package com.hwlcn.ldap.util.ssl;
import java.lang.reflect.Method;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import com.hwlcn.ldap.util.Debug;
import com.hwlcn.core.annotation.ThreadSafety;
import com.hwlcn.ldap.util.ThreadSafetyLevel;
import static com.hwlcn.ldap.util.Validator.*;
/**
* This class provides a simple interface for creating {@code SSLContext} and
* {@code SSLSocketFactory} instances, which may be used to create SSL-based
* connections, or secure existing connections with StartTLS.
* <BR><BR>
* <H2>Example 1</H2>
* The following example demonstrates the use of the SSL helper to create an
* SSL-based LDAP connection that will blindly trust any certificate that the
* server presents:
* <PRE>
* SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
*
* LDAPConnection connection =
* new LDAPConnection(sslUtil.createSSLSocketFactory());
* connection.connect("server.example.com", 636);
* </PRE>
* <BR>
* <H2>Example 2</H2>
* The following example demonstrates the use of the SSL helper to create a
* non-secure LDAP connection and then use the StartTLS extended operation to
* secure it. It will use a trust store to determine whether to trust the
* server certificate.
* <PRE>
* LDAPConnection connection = new LDAPConnection();
* connection.connect("server.example.com", 389);
*
* String trustStoreFile = "/path/to/trust/store/file";
* SSLUtil sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStoreFile));
*
* ExtendedResult extendedResult = connection.processExtendedOperation(
* new StartTLSExtendedRequest(sslUtil.createSSLContext()));
* </PRE>
*/
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class SSLUtil
{
private static final AtomicReference<String> DEFAULT_SSL_PROTOCOL =
new AtomicReference<String>("TLSv1");
static
{
try
{
final Method getDefaultMethod =
SSLContext.class.getMethod("getDefault");
final SSLContext defaultContext =
(SSLContext) getDefaultMethod.invoke(null);
final Method getSupportedParamsMethod =
SSLContext.class.getMethod("getSupportedSSLParameters");
final Object paramsObj = getSupportedParamsMethod.invoke(defaultContext);
final Class<?> sslParamsClass =
Class.forName("javax.net.ssl.SSLParameters");
final Method getProtocolsMethod =
sslParamsClass.getMethod("getProtocols");
final String[] supportedProtocols =
(String[]) getProtocolsMethod.invoke(paramsObj);
final HashSet<String> protocolMap =
new HashSet<String>(Arrays.asList(supportedProtocols));
if (protocolMap.contains("TLSv1.2"))
{
DEFAULT_SSL_PROTOCOL.set("TLSv1.2");
}
else if (protocolMap.contains("TLSv1.1"))
{
DEFAULT_SSL_PROTOCOL.set("TLSv1.1");
}
else if (protocolMap.contains("TLSv1"))
{
DEFAULT_SSL_PROTOCOL.set("TLSv1");
}
}
catch (final Exception e)
{
Debug.debugException(e);
}
}
private final KeyManager[] keyManagers;
private final TrustManager[] trustManagers;
public SSLUtil()
{
keyManagers = null;
trustManagers = null;
}
public SSLUtil(final TrustManager trustManager)
{
keyManagers = null;
if (trustManager == null)
{
trustManagers = null;
}
else
{
trustManagers = new TrustManager[] { trustManager };
}
}
public SSLUtil(final TrustManager[] trustManagers)
{
keyManagers = null;
if ((trustManagers == null) || (trustManagers.length == 0))
{
this.trustManagers = null;
}
else
{
this.trustManagers = trustManagers;
}
}
public SSLUtil(final KeyManager keyManager, final TrustManager trustManager)
{
if (keyManager == null)
{
keyManagers = null;
}
else
{
keyManagers = new KeyManager[] { keyManager };
}
if (trustManager == null)
{
trustManagers = null;
}
else
{
trustManagers = new TrustManager[] { trustManager };
}
}
public SSLUtil(final KeyManager[] keyManagers,
final TrustManager[] trustManagers)
{
if ((keyManagers == null) || (keyManagers.length == 0))
{
this.keyManagers = null;
}
else
{
this.keyManagers = keyManagers;
}
if ((trustManagers == null) || (trustManagers.length == 0))
{
this.trustManagers = null;
}
else
{
this.trustManagers = trustManagers;
}
}
public KeyManager[] getKeyManagers()
{
return keyManagers;
}
public TrustManager[] getTrustManagers()
{
return trustManagers;
}
public SSLContext createSSLContext()
throws GeneralSecurityException
{
return createSSLContext(DEFAULT_SSL_PROTOCOL.get());
}
public SSLContext createSSLContext(final String protocol)
throws GeneralSecurityException
{
ensureNotNull(protocol);
final SSLContext sslContext = SSLContext.getInstance(protocol);
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}
public SSLContext createSSLContext(final String protocol,
final String provider)
throws GeneralSecurityException
{
ensureNotNull(protocol, provider);
final SSLContext sslContext = SSLContext.getInstance(protocol, provider);
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}
public SSLSocketFactory createSSLSocketFactory()
throws GeneralSecurityException
{
return createSSLContext().getSocketFactory();
}
public SSLSocketFactory createSSLSocketFactory(final String protocol)
throws GeneralSecurityException
{
return createSSLContext(protocol).getSocketFactory();
}
public SSLSocketFactory createSSLSocketFactory(final String protocol,
final String provider)
throws GeneralSecurityException
{
return createSSLContext(protocol, provider).getSocketFactory();
}
public SSLServerSocketFactory createSSLServerSocketFactory()
throws GeneralSecurityException
{
return createSSLContext().getServerSocketFactory();
}
public SSLServerSocketFactory createSSLServerSocketFactory(
final String protocol)
throws GeneralSecurityException
{
return createSSLContext(protocol).getServerSocketFactory();
}
public SSLServerSocketFactory createSSLServerSocketFactory(
final String protocol,
final String provider)
throws GeneralSecurityException
{
return createSSLContext(protocol, provider).getServerSocketFactory();
}
public static String getDefaultSSLProtocol()
{
return DEFAULT_SSL_PROTOCOL.get();
}
public static void setDefaultSSLProtocol(final String defaultSSLProtocol)
{
ensureNotNull(defaultSSLProtocol);
DEFAULT_SSL_PROTOCOL.set(defaultSSLProtocol);
}
}