/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.jobs; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import netscape.ldap.LDAPException; import netscape.ldap.LDAPSocketFactory; /** * This class provides an implementation of an SSL socket factory that will use * JSSE to create the SSL socket. In addition, it will implement a trust * mechanism in such a way that it will blindly trust any certificate that the * server presents to it, regardless of what we might think is wrong with it. * * * @author Neil A. Wilson */ public class JSSEBlindTrustSocketFactory extends SSLSocketFactory implements LDAPSocketFactory, X509TrustManager { // Indicates whether debug mode will be enabled (will print a message to // standard error whenever any method is called). private boolean debugMode; // Indicates whether the SSL session should be immediately invalidated to // prevent session reuse. private boolean disableSessionReuse; // The SSL context that will be used to manage all things SSL. private SSLContext sslContext; // The SSL socket factory that will actually be used to create the sockets. private SSLSocketFactory sslSocketFactory; // The set cipher names that should be used when creating sockets. private String[] cipherNames; /** * Creates a new instance of this LDAP socket factory. * * @throws LDAPException If a problem occurs while initializing this socket * factory. */ public JSSEBlindTrustSocketFactory() throws LDAPException { this(false); } /** * Creates a new instance of this LDAP socket factory, optionally operating in * debug mode. * * @param debugMode Indicates whether to operate in debug mode. If this is * enabled, a message will be printed to standard error * any time of of the methods of this class is called. * * @throws LDAPException If a problem occurs while initializing this socket * factory. */ public JSSEBlindTrustSocketFactory(boolean debugMode) throws LDAPException { this.debugMode = debugMode; // Indicate that we will be using JSSE for the SSL-based connections. Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); // Get the default SSL context. try { sslContext = SSLContext.getInstance("SSLv3"); } catch (NoSuchAlgorithmException nsae) { throw new LDAPException("Unable to initialize the SSL context: " + nsae); } // Initialize the SSL context with our own trust manager (this class) to // use when determining whether to trust a client certificate. try { sslContext.init(null, new TrustManager[] { this }, null); } catch (KeyManagementException kme) { throw new LDAPException("Unable to register a new trust manager with " + "the SSL context: " + kme); } // Get the socket factory to use when creating the certificates. sslSocketFactory = sslContext.getSocketFactory(); // Set the values of the remaining instance variables. disableSessionReuse = false; cipherNames = sslSocketFactory.getDefaultCipherSuites(); // If we are in debug mode, indicate that the socket factory has been // created. if (debugMode) { System.err.println("New JSSEBlindTrustSocketFactory created"); } } /** * Determines whether the provided client certificate should be trusted. In * this case, the certificate will always be trusted. * * @param chain The peer certificate chain. * @param authType The authentication type based on the client certificate. */ public void checkClientTrusted(X509Certificate[] chain, String authType) { // No implementation required. If we don't throw an exception, then there // is no problem with the cert. if (debugMode) { System.err.println("checkClientTrusted() invoked"); } } /** * Determines whether the provided server certificate should be trusted. In * this case, the certificate will always be trusted. * * @param chain The peer certificate chain. * @param authType The authentication type based on the server certificate. */ public void checkServerTrusted(X509Certificate[] chain, String authType) { // No implementation required. If we don't throw an exception, then there // is no problem with the cert. if (debugMode) { System.err.println("checkServerTrusted() invoked"); } } /** * Retrieves an array of CA certificates that are trusted for authenticating * peers. * * @return An empty array, because we don't care about any list of CAs. */ public X509Certificate[] getAcceptedIssuers() { if (debugMode) { System.err.println("getAcceptedIssuers() invoked"); } return new X509Certificate[0]; } /** * Establishes an SSL socket to the provided host and port that can be used by * the LDAP SDK for Java for communicating with an LDAP directory server. * * @param host The address of the server to which the connection is to be * established. * @param port The port number of the server to which the connection is to * be established. * * @return The SSL socket that may be used for communicating with the * directory server. * * @throws LDAPException If a problem occurs while trying to establish the * connection. */ public Socket makeSocket(String host, int port) throws LDAPException { if (debugMode) { System.err.println("makeSocket(" + host + ',' + port + ") invoked"); } try { SSLSocket s = (SSLSocket) sslSocketFactory.createSocket(host, port); s.setEnabledCipherSuites(cipherNames); s.setKeepAlive(true); s.setSoLinger(true, 0); s.setReuseAddress(true); s.setTcpNoDelay(true); if (disableSessionReuse) { s.getSession().invalidate(); } return s; } catch (Exception e) { throw new LDAPException("Unable to establish the SSL connection: " + e); } } /** * Creates a new SSL socket connected to the specified host and port. * * @param host The address of the system to which the SSL socket should be * connected. * @param port The port on the target system to which the SSL socket should * be connected. * * @return The created SSL socket. * * @throws IOException If a problem occurs while creating the SSL socket. */ public Socket createSocket(String host, int port) throws IOException { SSLSocket s = (SSLSocket) sslSocketFactory.createSocket(host, port); s.setEnabledCipherSuites(cipherNames); s.setKeepAlive(true); s.setSoLinger(true, 0); s.setReuseAddress(true); s.setTcpNoDelay(true); if (disableSessionReuse) { s.getSession().invalidate(); } return s; } /** * Creates a new SSL socket connected to the specified host and port. * * @param host The address of the system to which the SSL socket should * be connected. * @param port The port on the target system to which the SSL socket * should be connected. * @param localHost The address on the local system from which the socket * should originate. * @param localPort The port on the local system from which the socket * should originate. * * @return The created SSL socket. * * @throws IOException If a problem occurs while creating the SSL socket. */ public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { SSLSocket s = (SSLSocket) sslSocketFactory.createSocket(host, port, localHost, localPort); s.setEnabledCipherSuites(cipherNames); s.setKeepAlive(true); s.setSoLinger(true, 0); s.setReuseAddress(true); s.setTcpNoDelay(true); if (disableSessionReuse) { s.getSession().invalidate(); } return s; } /** * Creates a new SSL socket connected to the specified host and port. * * @param host The address of the system to which the SSL socket should be * connected. * @param port The port on the target system to which the SSL socket should * be connected. * * @return The created SSL socket. * * @throws IOException If a problem occurs while creating the SSL socket. */ public Socket createSocket(InetAddress host, int port) throws IOException { SSLSocket s = (SSLSocket) sslSocketFactory.createSocket(host, port); s.setEnabledCipherSuites(cipherNames); s.setKeepAlive(true); s.setSoLinger(true, 0); s.setReuseAddress(true); s.setTcpNoDelay(true); if (disableSessionReuse) { s.getSession().invalidate(); } return s; } /** * Creates a new SSL socket connected to the specified host and port. * * @param host The address of the system to which the SSL socket * should be connected. * @param port The port on the target system to which the SSL socket * should be connected. * @param localAddress The address on the local system from which the socket * should originate. * @param localPort The port on the local system from which the socket * should originate. * * @return The created SSL socket. * * @throws IOException If a problem occurs while creating the SSL socket. */ public Socket createSocket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException { SSLSocket s = (SSLSocket) sslSocketFactory.createSocket(host, port, localAddress, localPort); s.setEnabledCipherSuites(cipherNames); s.setKeepAlive(true); s.setSoLinger(true, 0); s.setReuseAddress(true); s.setTcpNoDelay(true); if (disableSessionReuse) { s.getSession().invalidate(); } return s; } /** * Converts the provided socket to an SSL socket using this socket factory. * * @param socket The socket to convert to an SSL socket. * @param host The host to which the socket is connected. * @param port The port to which the socket is connected. * @param autoClose Indicates whether the underlying socket should be closed * when the returned SSL socket is closed. * * @return The created SSL socket. * * @throws IOException If a problem occurs while creating the SSL socket. */ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { SSLSocket s = (SSLSocket) sslSocketFactory.createSocket(socket, host, port, autoClose); s.setEnabledCipherSuites(cipherNames); s.setKeepAlive(true); s.setSoLinger(true, 0); s.setReuseAddress(true); s.setTcpNoDelay(true); if (disableSessionReuse) { s.getSession().invalidate(); } return s; } /** * Retrieves the names of the ciphers that should be used for SSL sockets * created by this socket factory. * * @return The names of the ciphers that should be used for SSL sockets * created by this socket factory. */ public String[] getCiphers() { return cipherNames; } /** * Specifies the name of the cipher that should be used for SSL sockets * created by this socket factory. * * @param cipherName The name of the cipher that should be used for SSL * sockets created by this socket factory. */ public void setCipher(String cipherName) { if (cipherName == null) { cipherNames = sslSocketFactory.getDefaultCipherSuites(); } else { cipherNames = new String[] { cipherName }; } } /** * Specifies the names of the cipher that should be used for SSL sockets * created by this socket factory. * * @param cipherNames The names of the cipher that should be used for SSL * sockets created by this socket factory. */ public void setCiphers(String[] cipherNames) { if (cipherNames == null) { this.cipherNames = sslSocketFactory.getDefaultCipherSuites(); } else { this.cipherNames = cipherNames; } } /** * Retrieves the set of cipher suites that are enabled by default. * * @return The set of cipher suites that are enabled by default. */ public String[] getDefaultCipherSuites() { return cipherNames; } /** * Retrieves the set of cipher suites that can be used to create SSL sockets. * * @return The set of cipher suites that can be used to create SSL sockets. */ public String[] getSupportedCipherSuites() { return cipherNames; } /** * Indicates whether SSL sessions may be reused across multiple connections. * * @return <CODE>true</CODE> if SSL sessions may be reused across multiple * connections, or <CODE>false</CODE> if not. */ public boolean getDisableSessionReuse() { return disableSessionReuse; } /** * Specifies whether to disable SSL session reuse across multiple connections. * * @param disableSessionReuse Indicates whether to disable SSL session * reuse across multiple connections. */ public void setDisableSessionReuse(boolean disableSessionReuse) { this.disableSessionReuse = disableSessionReuse; } }