blob: 90f9e408c230dfdf782f2bf3168b8ff182f2e33b [file] [log] [blame]
package org.eclipse.jetty.exssl;
import java.io.File;
import java.io.InputStream;
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.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
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.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import org.eclipse.jetty.http.security.Password;
import org.eclipse.jetty.util.resource.Resource;
public class SslParameters
{
public static final String DEFAULT_KEYSTORE_ALGORITHM =
(Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ?
"SunX509" : Security.getProperty("ssl.KeyManagerFactory.algorithm"));
public static final String DEFAULT_TRUSTSTORE_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 =
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 cipher suites. */
private String _excludeCipherSuites[] = null;
/** Included cipher suites. */
private String _includeCipherSuites[] = null;
/** KeyStore path. */
private String _keystorePath = DEFAULT_KEYSTORE;
/** KeyStore provider name */
private String _keystoreProvider;
/** KeyStore type */
private String _keystoreType = "JKS";
/** SSL certificate alias */
private String _certAlias;
/** TrustStore path */
private String _truststorePath;
/** TrustStore provider name */
private String _truststoreProvider;
/** TrustStore type */
private String _truststoreType = "JKS";
/** 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 SSL certificate validation is required */
private boolean _validateCerts;
/** Set to true if renegotiation is allowed */
private boolean _allowRenegotiate = false;
/** KeyStore password */
private transient Password _password;
/** Key password */
private transient Password _keyPassword;
/** TrustStore password */
private transient Password _trustPassword;
/** SSL Provider name */
private String _sslProvider;
/** SSL Protocol name */
private String _sslProtocol = "TLS";
/** SecureRandom algorithm */
private String _secureRandomAlgorithm;
/** KeyManager factory algorithm */
private String _sslKeyManagerFactoryAlgorithm = DEFAULT_KEYSTORE_ALGORITHM;
/** TrustManager factory algorithm */
private String _sslTrustManagerFactoryAlgorithm = DEFAULT_TRUSTSTORE_ALGORITHM;
/** Path to file that contains Certificate Revocation List */
private String _crlPath;
/** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */
private int _maxCertPathLength = -1;
/** SSL context */
private SSLContext _context;
/* ------------------------------------------------------------ */
/**
* @return The array of cipher suite names to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public String[] getExcludeCipherSuites()
{
return _excludeCipherSuites;
}
/* ------------------------------------------------------------ */
/**
* @param cipherSuites
* The array of cipher suite names to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void setExcludeCipherSuites(String[] cipherSuites)
{
_excludeCipherSuites = cipherSuites;
}
/* ------------------------------------------------------------ */
/**
* @return The array of cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public String[] getIncludeCipherSuites()
{
return _includeCipherSuites;
}
/* ------------------------------------------------------------ */
/**
* @param cipherSuites
* The array of cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void setIncludeCipherSuites(String[] cipherSuites)
{
_includeCipherSuites = cipherSuites;
}
/* ------------------------------------------------------------ */
/**
* @return The file or URL of the SSL Key store.
*/
public String getKeystore()
{
return _keystorePath;
}
/* ------------------------------------------------------------ */
/**
* @param keystore
* The file or URL of the SSL Key store.
*/
public void setKeystore(String keystore)
{
_keystorePath = keystore;
}
/* ------------------------------------------------------------ */
/**
* @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)
{
_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)
{
_keystoreType = keystoreType;
}
/* ------------------------------------------------------------ */
/**
* @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)
{
_certAlias = certAlias;
}
/* ------------------------------------------------------------ */
/**
* @return The file name or URL of the trust store location
*/
public String getTruststore()
{
return _truststorePath;
}
/* ------------------------------------------------------------ */
/**
* @param truststore
* The file name or URL of the trust store location
*/
public void setTruststore(String truststore)
{
_truststorePath = truststore;
}
/* ------------------------------------------------------------ */
/**
* @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)
{
_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)
{
_truststoreType = truststoreType;
}
/* ------------------------------------------------------------ */
/**
* @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)
{
_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)
{
_wantClientAuth = wantClientAuth;
}
/* ------------------------------------------------------------ */
/**
* @return true if SSL certificate has to be validated
*/
public boolean getValidateCerts()
{
return _validateCerts;
}
/* ------------------------------------------------------------ */
/**
* @param validateServerCert
* true if SSL certificate has to be validated
*/
public void setValidateCerts(boolean validateCerts)
{
_validateCerts = validateCerts;
}
/* ------------------------------------------------------------ */
/**
* @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.
*
* @param allowRenegotiate
* true if re-negotiation is allowed (default false)
*/
public void setAllowRenegotiate(boolean allowRenegotiate)
{
_allowRenegotiate = allowRenegotiate;
}
/* ------------------------------------------------------------ */
/**
* @param password
* The password for the key store
*/
public void setPassword(String password)
{
_password = Password.getPassword(PASSWORD_PROPERTY,password,null);
}
/* ------------------------------------------------------------ */
/**
* @param password
* The password (if any) for the specific key within the key store
*/
public void setKeyPassword(String password)
{
_keyPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
}
/* ------------------------------------------------------------ */
/**
* @param password
* The password for the trust store
*/
public void setTrustPassword(String password)
{
_trustPassword = 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)
{
_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)
{
_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)
{
_secureRandomAlgorithm = algorithm;
}
/* ------------------------------------------------------------ */
/**
* @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
*/
public String getSslKeyManagerFactoryAlgorithm()
{
return (_sslKeyManagerFactoryAlgorithm);
}
/* ------------------------------------------------------------ */
/**
* @param algorithm
* The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
*/
public void setSslKeyManagerFactoryAlgorithm(String algorithm)
{
_sslKeyManagerFactoryAlgorithm = algorithm;
}
/* ------------------------------------------------------------ */
/**
* @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
*/
public String getSslTrustManagerFactoryAlgorithm()
{
return (_sslTrustManagerFactoryAlgorithm);
}
/* ------------------------------------------------------------ */
/**
* @param algorithm
* The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
*/
public void setSslTrustManagerFactoryAlgorithm(String algorithm)
{
_sslTrustManagerFactoryAlgorithm = 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)
{
_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)
{
_maxCertPathLength = maxCertPathLength;
}
/* ------------------------------------------------------------ */
/**
* @return The SSLContext
*/
public SSLContext getSslContext()
{
try
{
if (_context == null)
_context = createSSLContext();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return _context;
}
/* ------------------------------------------------------------ */
/**
* @param sslContext
* Set a preconfigured SSLContext
*/
public void setSslContext(SSLContext sslContext)
{
_context = sslContext;
}
/* ------------------------------------------------------------ */
protected SSLContext createSSLContext() throws Exception
{
KeyStore keyStore = getKeyStore(_keystorePath,_keystoreType,_keystoreProvider,_password == null?null:_password.toString());
KeyStore trustStore = getTrustStore(_truststorePath,_truststoreType,_truststoreProvider,_trustPassword == null?null:_trustPassword.toString());
Collection<? extends CRL> crls = loadCRL(_crlPath);
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));
}
if (_validateCerts)
{
CertificateValidator validator = new CertificateValidator(keyStore,trustStore,crls);
validator.validate(cert);
}
KeyManager[] keyManagers = getKeyManagers(keyStore);
TrustManager[] trustManagers = getTrustManagers(trustStore,crls);
SecureRandom secureRandom = _secureRandomAlgorithm == null?null:SecureRandom.getInstance(_secureRandomAlgorithm);
SSLContext context = _sslProvider == null?SSLContext.getInstance(_sslProtocol):SSLContext.getInstance(_sslProtocol,_sslProvider);
context.init(keyManagers,trustManagers,secureRandom);
return context;
}
/* ------------------------------------------------------------ */
public KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception
{
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_sslKeyManagerFactoryAlgorithm);
keyManagerFactory.init(keyStore,_keyPassword == null?(_password == null?null:_password.toString().toCharArray()):_keyPassword.toString().toCharArray());
KeyManager[] managers = keyManagerFactory.getKeyManagers();
if (_certAlias != null)
{
for (int idx = 0; idx < managers.length; idx++)
{
if (managers[idx] instanceof X509KeyManager)
{
managers[idx] = new SslExtendedKeyManager(_certAlias,(X509KeyManager)managers[idx]);
}
}
}
return managers;
}
/* ------------------------------------------------------------ */
public TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls) throws Exception
{
TrustManager[] managers = null;
if (trustStore != null && _validateCerts)
{
// Revocation checking is only supported for PKIX algorithm
if (_sslTrustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX"))
{
PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector());
// Enable revocation checking
pbParams.setRevocationEnabled(true);
// Set maximum certification path length
pbParams.setMaxPathLength(_maxCertPathLength);
if (crls != null && !crls.isEmpty())
{
pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls)));
}
// Enable On-Line Certificate Status Protocol (OCSP) support
Security.setProperty("ocsp.enable","true");
// Enable Certificate Revocation List Distribution Points (CRLDP) support
System.setProperty("com.sun.security.enableCRLDP","true");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm);
trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams));
managers = trustManagerFactory.getTrustManagers();
}
else
{
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm);
trustManagerFactory.init(trustStore);
managers = trustManagerFactory.getTrustManagers();
}
}
return managers;
}
/* ------------------------------------------------------------ */
public KeyStore getKeyStore(String storePath, String storeType, String storeProvider, String storePassword) throws Exception
{
if (storePath == null)
return null;
KeyStore keystore = null;
InputStream inStream = null;
try
{
if (storeProvider != null)
{
keystore = KeyStore.getInstance(storeType,storeProvider);
}
else
{
keystore = KeyStore.getInstance(storeType);
}
inStream = Resource.newResource(storePath).getInputStream();
keystore.load(inStream,storePassword == null?null:storePassword.toCharArray());
return keystore;
}
finally
{
if (inStream != null)
{
inStream.close();
}
}
}
/* ------------------------------------------------------------ */
public KeyStore getTrustStore(String trustPath, String trustType, String trustProvider, String trustPassword) throws Exception
{
if (trustPath == null)
{
trustPath = System.getProperty("javax.net.ssl.trustStore");
trustType = System.getProperty("javax.net.ssl.trustStoreType");
trustProvider = System.getProperty("javax.net.ssl.trustStoreProvider");
trustPassword = System.getProperty("javax.net.ssl.trustStorePassword");
}
if (trustPath == null)
{
trustPath = _keystorePath;
trustType = _keystoreType;
trustProvider = _keystoreProvider;
trustPassword = _password.toString();
_sslTrustManagerFactoryAlgorithm = _sslKeyManagerFactoryAlgorithm;
}
return getKeyStore(trustPath,trustType,trustProvider,trustPassword);
}
/* ------------------------------------------------------------ */
private Collection<? extends CRL> loadCRL(String crlPath) throws Exception
{
Collection<? extends CRL> crlList = null;
if (crlPath != null)
{
InputStream in = null;
try
{
in = Resource.newResource(crlPath).getInputStream();
crlList = CertificateFactory.getInstance("X.509").generateCRLs(in);
}
finally
{
if (in != null)
{
in.close();
}
}
}
return crlList;
}
public String[] selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites)
{
List<String> selectedCipherSuites = null;
if (enabledCipherSuites != null)
{
selectedCipherSuites = new ArrayList<String>(Arrays.asList(enabledCipherSuites));
}
else
{
selectedCipherSuites = new ArrayList<String>();
}
if ((supportedCipherSuites != null && supportedCipherSuites.length > 0) && (_includeCipherSuites != null && _includeCipherSuites.length > 0))
{
List<String> supportedCSList = Arrays.asList(supportedCipherSuites);
List<String> includedCSList = Arrays.asList(_includeCipherSuites);
for (String cipherName : includedCSList)
{
if ((!selectedCipherSuites.contains(cipherName)) && supportedCSList.contains(cipherName))
{
selectedCipherSuites.add(cipherName);
}
}
}
if (_excludeCipherSuites != null && _excludeCipherSuites.length > 0)
{
List<String> excludedCSList = Arrays.asList(_excludeCipherSuites);
for (String cipherName : excludedCSList)
{
if (selectedCipherSuites.contains(cipherName))
{
selectedCipherSuites.remove(cipherName);
}
}
}
return selectedCipherSuites.toArray(new String[0]);
}
}