view src/org/eclipse/jetty/util/ssl/SslContextFactory.java @ 1002:35d04ac3fd0b

simplify ssl
author Franklin Schmidt <fschmidt@gmail.com>
date Sat, 22 Oct 2016 21:56:44 -0600
parents 8e9db0bbf4f9
children
line wrap: on
line source

//
//  ========================================================================
//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.util.ssl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.security.InvalidParameterException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CRL;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;

import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.security.CertificateUtils;
import org.eclipse.jetty.util.security.CertificateValidator;
import org.eclipse.jetty.util.security.Password;


/* ------------------------------------------------------------ */
/**
 * SslContextFactory is used to configure SSL connectors
 * as well as HttpClient. It holds all SSL parameters and
 * creates SSL context based on these parameters to be
 * used by the SSL connectors.
 */
public class SslContextFactory extends AbstractLifeCycle
{
	public final static TrustManager[] TRUST_ALL_CERTS = new X509TrustManager[]{new X509TrustManager()
	{
		public java.security.cert.X509Certificate[] getAcceptedIssuers()
		{
			return new java.security.cert.X509Certificate[]{};
		}

		public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
		{
		}

		public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
		{
		}
	}};

	private static final Logger LOG = LoggerFactory.getLogger(SslContextFactory.class);

	public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM =
		(Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ?
				"SunX509" : Security.getProperty("ssl.KeyManagerFactory.algorithm"));
	public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM =
		(Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ?
				"SunX509" : Security.getProperty("ssl.TrustManagerFactory.algorithm"));

	/** Default value for the keystore location path. */
	public static final String DEFAULT_KEYSTORE_PATH =
		System.getProperty("user.home") + File.separator + ".keystore";

	/** String name of key password property. */
	public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword";

	/** String name of keystore password property. */
	public static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password";

	/** Excluded protocols. */
	private final Set<String> _excludeProtocols = new LinkedHashSet<String>();
	/** Included protocols. */
	private Set<String> _includeProtocols = null;

	/** Excluded cipher suites. */
	private final Set<String> _excludeCipherSuites = new LinkedHashSet<String>();
	/** Included cipher suites. */
	private Set<String> _includeCipherSuites = null;

	/** Keystore path. */
	private String _keyStorePath;
	/** Keystore provider name */
	private String _keyStoreProvider;
	/** Keystore type */
	private String _keyStoreType = "JKS";
	/** Keystore input stream */
	private InputStream _keyStoreInputStream;

	/** SSL certificate alias */
	private String _certAlias;

	/** Truststore path */
	private String _trustStorePath;
	/** Truststore provider name */
	private String _trustStoreProvider;
	/** Truststore type */
	private String _trustStoreType = "JKS";
	/** Truststore input stream */
	private InputStream _trustStoreInputStream;

	/** Set to true if client certificate authentication is required */
	private boolean _needClientAuth = false;
	/** Set to true if client certificate authentication is desired */
	private boolean _wantClientAuth = false;

	/** Set to true if renegotiation is allowed */
	private boolean _allowRenegotiate = true;

	/** Keystore password */
	private transient Password _keyStorePassword;
	/** Key manager password */
	private transient Password _keyManagerPassword;
	/** Truststore password */
	private transient Password _trustStorePassword;

	/** SSL provider name */
	private String _sslProvider;
	/** SSL protocol name */
	private String _sslProtocol = "TLS";

	/** SecureRandom algorithm */
	private String _secureRandomAlgorithm;
	/** KeyManager factory algorithm */
	private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM;
	/** TrustManager factory algorithm */
	private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM;

	/** Set to true if SSL certificate validation is required */
	private boolean _validateCerts;
	/** Set to true if SSL certificate of the peer validation is required */
	private boolean _validatePeerCerts;
	/** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */
	private int _maxCertPathLength = -1;
	/** Path to file that contains Certificate Revocation List */
	private String _crlPath;
	/** Set to true to enable CRL Distribution Points (CRLDP) support */
	private boolean _enableCRLDP = false;
	/** Set to true to enable On-Line Certificate Status Protocol (OCSP) support */
	private boolean _enableOCSP = false;
	/** Location of OCSP Responder */
	private String _ocspResponderURL;

	/** SSL keystore */
	private KeyStore _keyStore;
	/** SSL truststore */
	private KeyStore _trustStore;
	/** Set to true to enable SSL Session caching */
	private boolean _sessionCachingEnabled = true;
	/** SSL session cache size */
	private int _sslSessionCacheSize;
	/** SSL session timeout */
	private int _sslSessionTimeout;

	/** SSL context */
	private SSLContext _context;

	private boolean _trustAll;

	/* ------------------------------------------------------------ */
	/**
	 * Construct an instance of SslContextFactory
	 * Default constructor for use in XmlConfiguration files
	 */
	public SslContextFactory()
	{
		_trustAll=true;
	}

	/* ------------------------------------------------------------ */
	/**
	 * Construct an instance of SslContextFactory
	 * Default constructor for use in XmlConfiguration files
	 * @param trustAll whether to blindly trust all certificates
	 * @see #setTrustAll(boolean)
	 */
	public SslContextFactory(boolean trustAll)
	{
		_trustAll=trustAll;
	}

	/* ------------------------------------------------------------ */
	/**
	 * Construct an instance of SslContextFactory
	 * @param keyStorePath default keystore location
	 */
	public SslContextFactory(String keyStorePath)
	{
		_keyStorePath = keyStorePath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * Create the SSLContext object and start the lifecycle
	 * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
	 */
	@Override
	protected void doStart() throws Exception
	{
		if (_context == null)
		{
			if (_keyStore==null && _keyStoreInputStream == null && _keyStorePath == null &&
				_trustStore==null && _trustStoreInputStream == null && _trustStorePath == null )
			{
				TrustManager[] trust_managers=null;

				if (_trustAll)
				{
					LOG.debug("No keystore or trust store configured.  ACCEPTING UNTRUSTED CERTIFICATES!!!!!");
					// Create a trust manager that does not validate certificate chains
					trust_managers = TRUST_ALL_CERTS;
				}

				SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
				_context = SSLContext.getInstance(_sslProtocol);
				_context.init(null, trust_managers, secureRandom);
			}
			else
			{
				// verify that keystore and truststore
				// parameters are set up correctly
				checkKeyStore();

				KeyStore keyStore = loadKeyStore();
				KeyStore trustStore = loadTrustStore();

				Collection<? extends CRL> crls = loadCRL(_crlPath);

				if (_validateCerts && keyStore != null)
				{
					if (_certAlias == null)
					{
						List<String> aliases = Collections.list(keyStore.aliases());
						_certAlias = aliases.size() == 1 ? aliases.get(0) : null;
					}

					Certificate cert = _certAlias == null?null:keyStore.getCertificate(_certAlias);
					if (cert == null)
					{
						throw new Exception("No certificate found in the keystore" + (_certAlias==null ? "":" for alias " + _certAlias));
					}

					CertificateValidator validator = new CertificateValidator(trustStore, crls);
					validator.setMaxCertPathLength(_maxCertPathLength);
					validator.setEnableCRLDP(_enableCRLDP);
					validator.setEnableOCSP(_enableOCSP);
					validator.setOcspResponderURL(_ocspResponderURL);
					validator.validate(keyStore, cert);
				}

				KeyManager[] keyManagers = getKeyManagers(keyStore);
				TrustManager[] trustManagers = getTrustManagers(trustStore,crls);

				SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
				_context = (_sslProvider == null)?SSLContext.getInstance(_sslProtocol):SSLContext.getInstance(_sslProtocol,_sslProvider);
				_context.init(keyManagers,trustManagers,secureRandom);

				SSLEngine engine=newSslEngine();

				LOG.info("Enabled Protocols {} of {}",Arrays.asList(engine.getEnabledProtocols()),Arrays.asList(engine.getSupportedProtocols()));
				if (LOG.isDebugEnabled())
					LOG.debug("Enabled Ciphers   {} of {}",Arrays.asList(engine.getEnabledCipherSuites()),Arrays.asList(engine.getSupportedCipherSuites()));
			}
		}
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The array of protocol names to exclude from
	 * {@link SSLEngine#setEnabledProtocols(String[])}
	 */
	public String[] getExcludeProtocols()
	{
		return _excludeProtocols.toArray(new String[_excludeProtocols.size()]);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param protocols
	 *            The array of protocol names to exclude from
	 *            {@link SSLEngine#setEnabledProtocols(String[])}
	 */
	public void setExcludeProtocols(String... protocols)
	{
		checkNotStarted();

		_excludeProtocols.clear();
		_excludeProtocols.addAll(Arrays.asList(protocols));
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param protocol Protocol names to add to {@link SSLEngine#setEnabledProtocols(String[])}
	 */
	public void addExcludeProtocols(String... protocol)
	{
		checkNotStarted();
		_excludeProtocols.addAll(Arrays.asList(protocol));
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The array of protocol names to include in
	 * {@link SSLEngine#setEnabledProtocols(String[])}
	 */
	public String[] getIncludeProtocols()
	{
		return _includeProtocols.toArray(new String[_includeProtocols.size()]);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param protocols
	 *            The array of protocol names to include in
	 *            {@link SSLEngine#setEnabledProtocols(String[])}
	 */
	public void setIncludeProtocols(String... protocols)
	{
		checkNotStarted();

		_includeProtocols = new LinkedHashSet<String>(Arrays.asList(protocols));
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The array of cipher suite names to exclude from
	 * {@link SSLEngine#setEnabledCipherSuites(String[])}
	 */
	public String[] getExcludeCipherSuites()
	{
		return _excludeCipherSuites.toArray(new String[_excludeCipherSuites.size()]);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param cipherSuites
	 *            The array of cipher suite names to exclude from
	 *            {@link SSLEngine#setEnabledCipherSuites(String[])}
	 */
	public void setExcludeCipherSuites(String... cipherSuites)
	{
		checkNotStarted();
		_excludeCipherSuites.clear();
		_excludeCipherSuites.addAll(Arrays.asList(cipherSuites));
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param cipher Cipher names to add to {@link SSLEngine#setEnabledCipherSuites(String[])}
	 */
	public void addExcludeCipherSuites(String... cipher)
	{
		checkNotStarted();
		_excludeCipherSuites.addAll(Arrays.asList(cipher));
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The array of cipher suite names to include in
	 * {@link SSLEngine#setEnabledCipherSuites(String[])}
	 */
	public String[] getIncludeCipherSuites()
	{
		return _includeCipherSuites.toArray(new String[_includeCipherSuites.size()]);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param cipherSuites
	 *            The array of cipher suite names to include in
	 *            {@link SSLEngine#setEnabledCipherSuites(String[])}
	 */
	public void setIncludeCipherSuites(String... cipherSuites)
	{
		checkNotStarted();

		_includeCipherSuites = new LinkedHashSet<String>(Arrays.asList(cipherSuites));
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The file or URL of the SSL Key store.
	 */
	public String getKeyStorePath()
	{
		return _keyStorePath;
	}

	/* ------------------------------------------------------------ */
	@Deprecated
	public String getKeyStore()
	{
		return _keyStorePath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param keyStorePath
	 *            The file or URL of the SSL Key store.
	 */
	public void setKeyStorePath(String keyStorePath)
	{
		checkNotStarted();

		_keyStorePath = keyStorePath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param keyStorePath the file system path or URL of the keystore
	 * @deprecated Use {@link #setKeyStorePath(String)}
	 */
	@Deprecated
	public void setKeyStore(String keyStorePath)
	{
		checkNotStarted();

		_keyStorePath = keyStorePath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The provider of the key store
	 */
	public String getKeyStoreProvider()
	{
		return _keyStoreProvider;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param keyStoreProvider
	 *            The provider of the key store
	 */
	public void setKeyStoreProvider(String keyStoreProvider)
	{
		checkNotStarted();

		_keyStoreProvider = keyStoreProvider;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The type of the key store (default "JKS")
	 */
	public String getKeyStoreType()
	{
		return (_keyStoreType);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param keyStoreType
	 *            The type of the key store (default "JKS")
	 */
	public void setKeyStoreType(String keyStoreType)
	{
		checkNotStarted();

		_keyStoreType = keyStoreType;
	}

	/* ------------------------------------------------------------ */
	/** Get the _keyStoreInputStream.
	 * @return the _keyStoreInputStream
	 *
	 * @deprecated
	 */
	@Deprecated
	public InputStream getKeyStoreInputStream()
	{
		checkKeyStore();

		return _keyStoreInputStream;
	}

	/* ------------------------------------------------------------ */
	/** Set the keyStoreInputStream.
	 * @param keyStoreInputStream the InputStream to the KeyStore
	 *
	 * @deprecated Use {@link #setKeyStore(KeyStore)}
	 */
	@Deprecated
	public void setKeyStoreInputStream(InputStream keyStoreInputStream)
	{
		checkNotStarted();

		_keyStoreInputStream = keyStoreInputStream;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return Alias of SSL certificate for the connector
	 */
	public String getCertAlias()
	{
		return _certAlias;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param certAlias
	 *            Alias of SSL certificate for the connector
	 */
	public void setCertAlias(String certAlias)
	{
		checkNotStarted();

		_certAlias = certAlias;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The file name or URL of the trust store location
	 */
	public String getTrustStore()
	{
		return _trustStorePath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param trustStorePath
	 *            The file name or URL of the trust store location
	 */
	public void setTrustStore(String trustStorePath)
	{
		checkNotStarted();

		_trustStorePath = trustStorePath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The provider of the trust store
	 */
	public String getTrustStoreProvider()
	{
		return _trustStoreProvider;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param trustStoreProvider
	 *            The provider of the trust store
	 */
	public void setTrustStoreProvider(String trustStoreProvider)
	{
		checkNotStarted();

		_trustStoreProvider = trustStoreProvider;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The type of the trust store (default "JKS")
	 */
	public String getTrustStoreType()
	{
		return _trustStoreType;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param trustStoreType
	 *            The type of the trust store (default "JKS")
	 */
	public void setTrustStoreType(String trustStoreType)
	{
		checkNotStarted();

		_trustStoreType = trustStoreType;
	}

	/* ------------------------------------------------------------ */
	/** Get the _trustStoreInputStream.
	 * @return the _trustStoreInputStream
	 *
	 * @deprecated
	 */
	@Deprecated
	public InputStream getTrustStoreInputStream()
	{
		checkKeyStore();

		return _trustStoreInputStream;
	}

	/* ------------------------------------------------------------ */
	/** Set the _trustStoreInputStream.
	 * @param trustStoreInputStream the InputStream to the TrustStore
	 *
	 * @deprecated
	 */
	@Deprecated
	public void setTrustStoreInputStream(InputStream trustStoreInputStream)
	{
		checkNotStarted();

		_trustStoreInputStream = trustStoreInputStream;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return True if SSL needs client authentication.
	 * @see SSLEngine#getNeedClientAuth()
	 */
	public boolean getNeedClientAuth()
	{
		return _needClientAuth;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param needClientAuth
	 *            True if SSL needs client authentication.
	 * @see SSLEngine#getNeedClientAuth()
	 */
	public void setNeedClientAuth(boolean needClientAuth)
	{
		checkNotStarted();

		_needClientAuth = needClientAuth;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return True if SSL wants client authentication.
	 * @see SSLEngine#getWantClientAuth()
	 */
	public boolean getWantClientAuth()
	{
		return _wantClientAuth;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param wantClientAuth
	 *            True if SSL wants client authentication.
	 * @see SSLEngine#getWantClientAuth()
	 */
	public void setWantClientAuth(boolean wantClientAuth)
	{
		checkNotStarted();

		_wantClientAuth = wantClientAuth;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return true if SSL certificate has to be validated
	 * @deprecated
	 */
	@Deprecated
	public boolean getValidateCerts()
	{
		return _validateCerts;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return true if SSL certificate has to be validated
	 */
	public boolean isValidateCerts()
	{
		return _validateCerts;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param validateCerts
	 *            true if SSL certificates have to be validated
	 */
	public void setValidateCerts(boolean validateCerts)
	{
		checkNotStarted();

		_validateCerts = validateCerts;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return true if SSL certificates of the peer have to be validated
	 */
	public boolean isValidatePeerCerts()
	{
		return _validatePeerCerts;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param validatePeerCerts
	 *            true if SSL certificates of the peer have to be validated
	 */
	public void setValidatePeerCerts(boolean validatePeerCerts)
	{
		checkNotStarted();

		_validatePeerCerts = validatePeerCerts;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return True if SSL re-negotiation is allowed (default false)
	 */
	public boolean isAllowRenegotiate()
	{
		return _allowRenegotiate;
	}

	/* ------------------------------------------------------------ */
	/**
	 * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
	 * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
	 * does not have CVE-2009-3555 fixed, then re-negotiation should
	 * not be allowed.  CVE-2009-3555 was fixed in Sun java 1.6 with a ban
	 * of renegotiates in u19 and with RFC5746 in u22.
	 *
	 * @param allowRenegotiate
	 *            true if re-negotiation is allowed (default false)
	 */
	public void setAllowRenegotiate(boolean allowRenegotiate)
	{
		checkNotStarted();

		_allowRenegotiate = allowRenegotiate;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param password
	 *            The password for the key store
	 */
	public void setKeyStorePassword(String password)
	{
		checkNotStarted();

		_keyStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param password
	 *            The password (if any) for the specific key within the key store
	 */
	public void setKeyManagerPassword(String password)
	{
		checkNotStarted();

		_keyManagerPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param password
	 *            The password for the trust store
	 */
	public void setTrustStorePassword(String password)
	{
		checkNotStarted();

		_trustStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The SSL provider name, which if set is passed to
	 * {@link SSLContext#getInstance(String, String)}
	 */
	public String getProvider()
	{
		return _sslProvider;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param provider
	 *            The SSL provider name, which if set is passed to
	 *            {@link SSLContext#getInstance(String, String)}
	 */
	public void setProvider(String provider)
	{
		checkNotStarted();

		_sslProvider = provider;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The SSL protocol (default "TLS") passed to
	 * {@link SSLContext#getInstance(String, String)}
	 */
	public String getProtocol()
	{
		return _sslProtocol;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param protocol
	 *            The SSL protocol (default "TLS") passed to
	 *            {@link SSLContext#getInstance(String, String)}
	 */
	public void setProtocol(String protocol)
	{
		checkNotStarted();

		_sslProtocol = protocol;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The algorithm name, which if set is passed to
	 * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to
	 * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
	 */
	public String getSecureRandomAlgorithm()
	{
		return _secureRandomAlgorithm;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param algorithm
	 *            The algorithm name, which if set is passed to
	 *            {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to
	 *            {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
	 */
	public void setSecureRandomAlgorithm(String algorithm)
	{
		checkNotStarted();

		_secureRandomAlgorithm = algorithm;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
	 */
	public String getSslKeyManagerFactoryAlgorithm()
	{
		return (_keyManagerFactoryAlgorithm);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param algorithm
	 *            The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
	 */
	public void setSslKeyManagerFactoryAlgorithm(String algorithm)
	{
		checkNotStarted();

		_keyManagerFactoryAlgorithm = algorithm;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
	 */
	public String getTrustManagerFactoryAlgorithm()
	{
		return (_trustManagerFactoryAlgorithm);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return True if all certificates should be trusted if there is no KeyStore or TrustStore
	 */
	public boolean isTrustAll()
	{
		return _trustAll;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param trustAll True if all certificates should be trusted if there is no KeyStore or TrustStore
	 */
	public void setTrustAll(boolean trustAll)
	{
		_trustAll = trustAll;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param algorithm
	 *            The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
	 *            Use the string "TrustAll" to install a trust manager that trusts all.
	 */
	public void setTrustManagerFactoryAlgorithm(String algorithm)
	{
		checkNotStarted();

		_trustManagerFactoryAlgorithm = algorithm;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return Path to file that contains Certificate Revocation List
	 */
	public String getCrlPath()
	{
		return _crlPath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param crlPath
	 *            Path to file that contains Certificate Revocation List
	 */
	public void setCrlPath(String crlPath)
	{
		checkNotStarted();

		_crlPath = crlPath;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return Maximum number of intermediate certificates in
	 * the certification path (-1 for unlimited)
	 */
	public int getMaxCertPathLength()
	{
		return _maxCertPathLength;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param maxCertPathLength
	 *            maximum number of intermediate certificates in
	 *            the certification path (-1 for unlimited)
	 */
	public void setMaxCertPathLength(int maxCertPathLength)
	{
		checkNotStarted();

		_maxCertPathLength = maxCertPathLength;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return The SSLContext
	 */
	public SSLContext getSslContext()
	{
		if (!isStarted())
			throw new IllegalStateException(getState());
		return _context;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @param sslContext
	 *            Set a preconfigured SSLContext
	 */
	public void setSslContext(SSLContext sslContext)
	{
		checkNotStarted();

		_context = sslContext;
	}

	/* ------------------------------------------------------------ */
	/**
	 * Override this method to provide alternate way to load a keystore.
	 *
	 * @return the key store instance
	 * @throws Exception if the keystore cannot be loaded
	 */
	protected KeyStore loadKeyStore() throws Exception
	{
		return _keyStore != null ? _keyStore : getKeyStore(_keyStoreInputStream,
				_keyStorePath, _keyStoreType, _keyStoreProvider,
				_keyStorePassword==null? null: _keyStorePassword.toString());
	}

	/* ------------------------------------------------------------ */
	/**
	 * Override this method to provide alternate way to load a truststore.
	 *
	 * @return the key store instance
	 * @throws Exception if the truststore cannot be loaded
	 */
	protected KeyStore loadTrustStore() throws Exception
	{
		return _trustStore != null ? _trustStore : getKeyStore(_trustStoreInputStream,
				_trustStorePath, _trustStoreType,  _trustStoreProvider,
				_trustStorePassword==null? null: _trustStorePassword.toString());
	}

	/* ------------------------------------------------------------ */
	/**
	 * Loads keystore using an input stream or a file path in the same
	 * order of precedence.
	 *
	 * Required for integrations to be able to override the mechanism
	 * used to load a keystore in order to provide their own implementation.
	 *
	 * @param storeStream keystore input stream
	 * @param storePath path of keystore file
	 * @param storeType keystore type
	 * @param storeProvider keystore provider
	 * @param storePassword keystore password
	 * @return created keystore
	 * @throws Exception if the keystore cannot be obtained
	 *
	 * @deprecated
	 */
	@Deprecated
	protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
	{
		return CertificateUtils.getKeyStore(storeStream, storePath, storeType, storeProvider, storePassword);
	}

	/* ------------------------------------------------------------ */
	/**
	 * Loads certificate revocation list (CRL) from a file.
	 *
	 * Required for integrations to be able to override the mechanism used to
	 * load CRL in order to provide their own implementation.
	 *
	 * @param crlPath path of certificate revocation list file
	 * @return Collection of CRL's
	 * @throws Exception if the certificate revocation list cannot be loaded
	 */
	protected Collection<? extends CRL> loadCRL(String crlPath) throws Exception
	{
		return CertificateUtils.loadCRL(crlPath);
	}

	/* ------------------------------------------------------------ */
	protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception
	{
		KeyManager[] managers = null;

		if (keyStore != null)
		{
			KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm);
			keyManagerFactory.init(keyStore,_keyManagerPassword == null?(_keyStorePassword == null?null:_keyStorePassword.toString().toCharArray()):_keyManagerPassword.toString().toCharArray());
			managers = keyManagerFactory.getKeyManagers();

			if (_certAlias != null)
			{
				for (int idx = 0; idx < managers.length; idx++)
				{
					if (managers[idx] instanceof X509KeyManager)
					{
						managers[idx] = new AliasedX509ExtendedKeyManager(_certAlias,(X509KeyManager)managers[idx]);
					}
				}
			}
		}

		return managers;
	}

	/* ------------------------------------------------------------ */
	protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls) throws Exception
	{
		TrustManager[] managers = null;
		if (trustStore != null)
		{
			// Revocation checking is only supported for PKIX algorithm
			if (_validatePeerCerts && _trustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX"))
			{
				PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector());

				// Set maximum certification path length
				pbParams.setMaxPathLength(_maxCertPathLength);

				// Make sure revocation checking is enabled
				pbParams.setRevocationEnabled(true);

				if (crls != null && !crls.isEmpty())
				{
					pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls)));
				}

				if (_enableCRLDP)
				{
					// Enable Certificate Revocation List Distribution Points (CRLDP) support
					System.setProperty("com.sun.security.enableCRLDP","true");
				}

				if (_enableOCSP)
				{
					// Enable On-Line Certificate Status Protocol (OCSP) support
					Security.setProperty("ocsp.enable","true");

					if (_ocspResponderURL != null)
					{
						// Override location of OCSP Responder
						Security.setProperty("ocsp.responderURL", _ocspResponderURL);
					}
				}

				TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
				trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams));

				managers = trustManagerFactory.getTrustManagers();
			}
			else
			{
				TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
				trustManagerFactory.init(trustStore);

				managers = trustManagerFactory.getTrustManagers();
			}
		}

		return managers;
	}

	/* ------------------------------------------------------------ */
	/**
	 * Check KeyStore Configuration. Ensures that if keystore has been
	 * configured but there's no truststore, that keystore is
	 * used as truststore.
	 * @throws IllegalStateException if SslContextFactory configuration can't be used.
	 */
	public void checkKeyStore()
	{
		if (_context != null)
			return; //nothing to check if using preconfigured context


		if (_keyStore == null && _keyStoreInputStream == null && _keyStorePath == null)
			throw new IllegalStateException("SSL doesn't have a valid keystore");

		// if the keystore has been configured but there is no
		// truststore configured, use the keystore as the truststore
		if (_trustStore == null && _trustStoreInputStream == null && _trustStorePath == null)
		{
			_trustStore = _keyStore;
			_trustStorePath = _keyStorePath;
			_trustStoreInputStream = _keyStoreInputStream;
			_trustStoreType = _keyStoreType;
			_trustStoreProvider = _keyStoreProvider;
			_trustStorePassword = _keyStorePassword;
			_trustManagerFactoryAlgorithm = _keyManagerFactoryAlgorithm;
		}

		// It's the same stream we cannot read it twice, so read it once in memory
		if (_keyStoreInputStream != null && _keyStoreInputStream == _trustStoreInputStream)
		{
			try
			{
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				IO.copy(_keyStoreInputStream, baos);
				_keyStoreInputStream.close();

				_keyStoreInputStream = new ByteArrayInputStream(baos.toByteArray());
				_trustStoreInputStream = new ByteArrayInputStream(baos.toByteArray());
			}
			catch (Exception ex)
			{
				throw new IllegalStateException(ex);
			}
		}
	}

	/* ------------------------------------------------------------ */
	/**
	 * Select protocols to be used by the connector
	 * based on configured inclusion and exclusion lists
	 * as well as enabled and supported protocols.
	 * @param enabledProtocols Array of enabled protocols
	 * @param supportedProtocols Array of supported protocols
	 * @return Array of protocols to enable
	 */
	public String[] selectProtocols(String[] enabledProtocols, String[] supportedProtocols)
	{
		Set<String> selected_protocols = new LinkedHashSet<String>();

		// Set the starting protocols - either from the included or enabled list
		if (_includeProtocols!=null)
		{
			// Use only the supported included protocols
			for (String protocol : _includeProtocols)
				if(Arrays.asList(supportedProtocols).contains(protocol))
					selected_protocols.add(protocol);
		}
		else
			selected_protocols.addAll(Arrays.asList(enabledProtocols));


		// Remove any excluded protocols
		if (_excludeProtocols != null)
			selected_protocols.removeAll(_excludeProtocols);

		return selected_protocols.toArray(new String[selected_protocols.size()]);
	}

	/* ------------------------------------------------------------ */
	/**
	 * Select cipher suites to be used by the connector
	 * based on configured inclusion and exclusion lists
	 * as well as enabled and supported cipher suite lists.
	 * @param enabledCipherSuites Array of enabled cipher suites
	 * @param supportedCipherSuites Array of supported cipher suites
	 * @return Array of cipher suites to enable
	 */
	public String[] selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites)
	{
		Set<String> selected_ciphers = new LinkedHashSet<String>();

		// Set the starting ciphers - either from the included or enabled list
		if (_includeCipherSuites!=null)
		{
			// Use only the supported included ciphers
			for (String cipherSuite : _includeCipherSuites)
				if(Arrays.asList(supportedCipherSuites).contains(cipherSuite))
					selected_ciphers.add(cipherSuite);
		}
		else
			selected_ciphers.addAll(Arrays.asList(enabledCipherSuites));


		// Remove any excluded ciphers
		if (_excludeCipherSuites != null)
			selected_ciphers.removeAll(_excludeCipherSuites);
		return selected_ciphers.toArray(new String[selected_ciphers.size()]);
	}

	/* ------------------------------------------------------------ */
	/**
	 * Check if the lifecycle has been started and throw runtime exception
	 */
	protected void checkNotStarted()
	{
		if (isStarted())
			throw new IllegalStateException("Cannot modify configuration when "+getState());
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return true if CRL Distribution Points support is enabled
	 */
	public boolean isEnableCRLDP()
	{
		return _enableCRLDP;
	}

	/* ------------------------------------------------------------ */
	/** Enables CRL Distribution Points Support
	 * @param enableCRLDP true - turn on, false - turns off
	 */
	public void setEnableCRLDP(boolean enableCRLDP)
	{
		checkNotStarted();

		_enableCRLDP = enableCRLDP;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return true if On-Line Certificate Status Protocol support is enabled
	 */
	public boolean isEnableOCSP()
	{
		return _enableOCSP;
	}

	/* ------------------------------------------------------------ */
	/** Enables On-Line Certificate Status Protocol support
	 * @param enableOCSP true - turn on, false - turn off
	 */
	public void setEnableOCSP(boolean enableOCSP)
	{
		checkNotStarted();

		_enableOCSP = enableOCSP;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return Location of the OCSP Responder
	 */
	public String getOcspResponderURL()
	{
		return _ocspResponderURL;
	}

	/* ------------------------------------------------------------ */
	/** Set the location of the OCSP Responder.
	 * @param ocspResponderURL location of the OCSP Responder
	 */
	public void setOcspResponderURL(String ocspResponderURL)
	{
		checkNotStarted();

		_ocspResponderURL = ocspResponderURL;
	}

	/* ------------------------------------------------------------ */
	/** Set the key store.
	 * @param keyStore the key store to set
	 */
	public void setKeyStore(KeyStore keyStore)
	{
		checkNotStarted();

		_keyStore = keyStore;
	}

	/* ------------------------------------------------------------ */
	/** Set the trust store.
	 * @param trustStore the trust store to set
	 */
	public void setTrustStore(KeyStore trustStore)
	{
		checkNotStarted();

		_trustStore = trustStore;
	}

	/* ------------------------------------------------------------ */
	/** Set the key store resource.
	 * @param resource the key store resource to set
	 */
	public void setKeyStoreResource(Resource resource)
	{
		checkNotStarted();

		try
		{
			_keyStoreInputStream = resource.getInputStream();
		}
		catch (IOException e)
		{
			 throw new InvalidParameterException("Unable to get resource "+
					 "input stream for resource "+resource.toString());
		}
	}

	/* ------------------------------------------------------------ */
	/** Set the trust store resource.
	 * @param resource the trust store resource to set
	 */
	public void setTrustStoreResource(Resource resource)
	{
		checkNotStarted();

		try
		{
			_trustStoreInputStream = resource.getInputStream();
		}
		catch (IOException e)
		{
			 throw new InvalidParameterException("Unable to get resource "+
					 "input stream for resource "+resource.toString());
		}
	}

	/* ------------------------------------------------------------ */
	/**
	* @return true if SSL Session caching is enabled
	*/
	public boolean isSessionCachingEnabled()
	{
		return _sessionCachingEnabled;
	}

	/* ------------------------------------------------------------ */
	/** Set the flag to enable SSL Session caching.
	* @param enableSessionCaching the value of the flag
	*/
	public void setSessionCachingEnabled(boolean enableSessionCaching)
	{
		_sessionCachingEnabled = enableSessionCaching;
	}

	/* ------------------------------------------------------------ */
	/** Get SSL session cache size.
	 * @return SSL session cache size
	 */
	public int getSslSessionCacheSize()
	{
		return _sslSessionCacheSize;
	}

	/* ------------------------------------------------------------ */
	/** SEt SSL session cache size.
	 * @param sslSessionCacheSize SSL session cache size to set
	 */
	public void setSslSessionCacheSize(int sslSessionCacheSize)
	{
		_sslSessionCacheSize = sslSessionCacheSize;
	}

	/* ------------------------------------------------------------ */
	/** Get SSL session timeout.
	 * @return SSL session timeout
	 */
	public int getSslSessionTimeout()
	{
		return _sslSessionTimeout;
	}

	/* ------------------------------------------------------------ */
	/** Set SSL session timeout.
	 * @param sslSessionTimeout SSL session timeout to set
	 */
	public void setSslSessionTimeout(int sslSessionTimeout)
	{
		_sslSessionTimeout = sslSessionTimeout;
	}


	/* ------------------------------------------------------------ */
	public SSLServerSocket newSslServerSocket(String host,int port,int backlog) throws IOException
	{
		SSLServerSocketFactory factory = _context.getServerSocketFactory();

		SSLServerSocket socket =
			(SSLServerSocket) (host==null ?
						factory.createServerSocket(port,backlog):
						factory.createServerSocket(port,backlog,InetAddress.getByName(host)));

		if (getWantClientAuth())
			socket.setWantClientAuth(getWantClientAuth());
		if (getNeedClientAuth())
			socket.setNeedClientAuth(getNeedClientAuth());

		socket.setEnabledCipherSuites(selectCipherSuites(
											socket.getEnabledCipherSuites(),
											socket.getSupportedCipherSuites()));
		socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols()));

		return socket;
	}

	/* ------------------------------------------------------------ */
	public SSLSocket newSslSocket() throws IOException
	{
		SSLSocketFactory factory = _context.getSocketFactory();

		SSLSocket socket = (SSLSocket)factory.createSocket();

		if (getWantClientAuth())
			socket.setWantClientAuth(getWantClientAuth());
		if (getNeedClientAuth())
			socket.setNeedClientAuth(getNeedClientAuth());

		socket.setEnabledCipherSuites(selectCipherSuites(
											socket.getEnabledCipherSuites(),
											socket.getSupportedCipherSuites()));
		socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols()));

		return socket;
	}

	/* ------------------------------------------------------------ */
	public SSLEngine newSslEngine(String host,int port)
	{
		SSLEngine sslEngine=isSessionCachingEnabled()
			?_context.createSSLEngine(host, port)
			:_context.createSSLEngine();

		customize(sslEngine);
		return sslEngine;
	}

	/* ------------------------------------------------------------ */
	public SSLEngine newSslEngine()
	{
		SSLEngine sslEngine=_context.createSSLEngine();
		customize(sslEngine);
		return sslEngine;
	}

	/* ------------------------------------------------------------ */
	public void customize(SSLEngine sslEngine)
	{
		if (getWantClientAuth())
			sslEngine.setWantClientAuth(getWantClientAuth());
		if (getNeedClientAuth())
			sslEngine.setNeedClientAuth(getNeedClientAuth());

		sslEngine.setEnabledCipherSuites(selectCipherSuites(
				sslEngine.getEnabledCipherSuites(),
				sslEngine.getSupportedCipherSuites()));

		sslEngine.setEnabledProtocols(selectProtocols(sslEngine.getEnabledProtocols(),sslEngine.getSupportedProtocols()));
	}

	/* ------------------------------------------------------------ */
	public String toString()
	{
		return String.format("%s@%x(%s,%s)",
				getClass().getSimpleName(),
				hashCode(),
				_keyStorePath,
				_trustStorePath);
	}
}