org.eclipse.jetty.util.ssl.SslContextFactory Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// 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.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CRL;
import java.security.cert.CertStore;
import java.security.cert.CertStoreParameters;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SNIMatcher;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.StandardConstants;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
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 parameters
* to be used by server and client connectors.
* Use {@link Server} to configure server-side connectors,
* and {@link Client} to configure HTTP or WebSocket clients.
*
* @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
*/
@ManagedObject
@Deprecated(since = "2021-05-27")
public class SslContextFactory extends AbstractLifeCycle implements Dumpable {
public static final TrustManager[] TRUST_ALL_CERTS = new X509TrustManager[] { new X509ExtendedTrustManagerWrapper(null) };
public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM = KeyManagerFactory.getDefaultAlgorithm();
public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM = TrustManagerFactory.getDefaultAlgorithm();
/**
* 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";
private static final Logger LOG = Log.getLogger(SslContextFactory.class);
private static final Logger LOG_CONFIG = LOG.getLogger("config");
/**
* Default Excluded Protocols List
*/
private static final String[] DEFAULT_EXCLUDED_PROTOCOLS = { "SSL", "SSLv2", "SSLv2Hello", "SSLv3" };
/**
* Default Excluded Cipher Suite List
*/
private static final String[] DEFAULT_EXCLUDED_CIPHER_SUITES = { // Exclude weak / insecure ciphers
"^.*_(MD5|SHA|SHA1)$", // Exclude ciphers that don't support forward secrecy
"^TLS_RSA_.*$", // The following exclusions are present to cleanup known bad cipher
// suites that may be accidentally included via include patterns.
// The default enabled cipher list in Java will not include these
// (but they are available in the supported list).
"^SSL_.*$", "^.*_NULL_.*$", "^.*_anon_.*$" };
private final Set _excludeProtocols = new LinkedHashSet<>();
private final Set _includeProtocols = new LinkedHashSet<>();
private final Set _excludeCipherSuites = new LinkedHashSet<>();
private final Set _includeCipherSuites = new LinkedHashSet<>();
private final Map _aliasX509 = new HashMap<>();
private final Map _certHosts = new HashMap<>();
private final Map _certWilds = new HashMap<>();
private String[] _selectedProtocols;
private boolean _useCipherSuitesOrder = true;
private Comparator _cipherComparator;
private String[] _selectedCipherSuites;
private Resource _keyStoreResource;
private String _keyStoreProvider;
private String _keyStoreType = "JKS";
private String _certAlias;
private Resource _trustStoreResource;
private String _trustStoreProvider;
private String _trustStoreType;
private boolean _needClientAuth = false;
private boolean _wantClientAuth = false;
private Password _keyStorePassword;
private Password _keyManagerPassword;
private Password _trustStorePassword;
private String _sslProvider;
private String _sslProtocol = "TLS";
private String _secureRandomAlgorithm;
private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM;
private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM;
private boolean _validateCerts;
private boolean _validatePeerCerts;
private int _maxCertPathLength = -1;
private String _crlPath;
private boolean _enableCRLDP = false;
private boolean _enableOCSP = false;
private String _ocspResponderURL;
private KeyStore _setKeyStore;
private KeyStore _setTrustStore;
private boolean _sessionCachingEnabled = true;
private int _sslSessionCacheSize = -1;
private int _sslSessionTimeout = -1;
private SSLContext _setContext;
private String _endpointIdentificationAlgorithm = "HTTPS";
private boolean _trustAll;
private boolean _renegotiationAllowed = true;
private int _renegotiationLimit = 5;
private Factory _factory;
private PKIXCertPathChecker _pkixCertPathChecker;
private HostnameVerifier _hostnameVerifier;
/**
* Construct an instance of SslContextFactory with the default configuration.
*
* @deprecated use {@link Client#Client()} or {@link Server#Server()} instead
*/
@Deprecated
public SslContextFactory() {
this(false);
}
/**
* Construct an instance of SslContextFactory
* Default constructor for use in XmlConfiguration files
*
* @param trustAll whether to blindly trust all certificates
* @see #setTrustAll(boolean)
* @deprecated use {@link Client#Client(boolean)} instead
*/
@Deprecated
public SslContextFactory(boolean trustAll) {
this(trustAll, null);
}
/**
* Construct an instance of SslContextFactory
*
* @param keyStorePath default keystore location
* @deprecated use {@link #setKeyStorePath(String)} instead
*/
@Deprecated
public SslContextFactory(String keyStorePath) {
this(false, keyStorePath);
}
private SslContextFactory(boolean trustAll, String keyStorePath) {
setTrustAll(trustAll);
setExcludeProtocols(DEFAULT_EXCLUDED_PROTOCOLS);
setExcludeCipherSuites(DEFAULT_EXCLUDED_CIPHER_SUITES);
if (keyStorePath != null)
setKeyStorePath(keyStorePath);
}
/**
* Creates the SSLContext object and starts the lifecycle
*/
@Override
protected void doStart() throws Exception {
super.doStart();
synchronized (this) {
load();
}
checkConfiguration();
}
protected void checkConfiguration() {
SSLEngine engine = _factory._context.createSSLEngine();
customize(engine);
SSLParameters supported = engine.getSSLParameters();
checkProtocols(supported);
checkCiphers(supported);
}
protected void checkTrustAll() {
if (isTrustAll())
LOG_CONFIG.warn("Trusting all certificates configured for {}", this);
}
protected void checkEndPointIdentificationAlgorithm() {
if (getEndpointIdentificationAlgorithm() == null)
LOG_CONFIG.warn("No Client EndPointIdentificationAlgorithm configured for {}", this);
}
protected void checkProtocols(SSLParameters supported) {
for (String protocol : supported.getProtocols()) {
for (String excluded : DEFAULT_EXCLUDED_PROTOCOLS) {
if (excluded.equals(protocol))
LOG_CONFIG.warn("Protocol {} not excluded for {}", protocol, this);
}
}
}
protected void checkCiphers(SSLParameters supported) {
for (String suite : supported.getCipherSuites()) {
for (String excludedSuiteRegex : DEFAULT_EXCLUDED_CIPHER_SUITES) {
if (suite.matches(excludedSuiteRegex))
LOG_CONFIG.warn("Weak cipher suite {} enabled for {}", suite, this);
}
}
}
private void load() throws Exception {
SSLContext context = _setContext;
KeyStore keyStore = _setKeyStore;
KeyStore trustStore = _setTrustStore;
if (context == null) {
// Is this an empty factory?
if (keyStore == null && _keyStoreResource == null && trustStore == null && _trustStoreResource == null) {
TrustManager[] trustManagers = null;
if (isTrustAll()) {
if (LOG.isDebugEnabled())
LOG.debug("No keystore or trust store configured. ACCEPTING UNTRUSTED CERTIFICATES!!!!!");
// Create a trust manager that does not validate certificate chains
trustManagers = TRUST_ALL_CERTS;
}
context = getSSLContextInstance();
context.init(null, trustManagers, getSecureRandomInstance());
} else {
if (keyStore == null)
keyStore = loadKeyStore(_keyStoreResource);
if (trustStore == null)
trustStore = loadTrustStore(_trustStoreResource);
Collection extends CRL> crls = loadCRL(getCrlPath());
// Look for X.509 certificates to create alias map
if (keyStore != null) {
for (String alias : Collections.list(keyStore.aliases())) {
Certificate certificate = keyStore.getCertificate(alias);
if (certificate != null && "X.509".equals(certificate.getType())) {
X509Certificate x509C = (X509Certificate) certificate;
// Exclude certificates with special uses
if (X509.isCertSign(x509C)) {
if (LOG.isDebugEnabled())
LOG.debug("Skipping " + x509C);
continue;
}
X509 x509 = new X509(alias, x509C);
_aliasX509.put(alias, x509);
if (isValidateCerts()) {
CertificateValidator validator = new CertificateValidator(trustStore, crls);
validator.setMaxCertPathLength(getMaxCertPathLength());
validator.setEnableCRLDP(isEnableCRLDP());
validator.setEnableOCSP(isEnableOCSP());
validator.setOcspResponderURL(getOcspResponderURL());
// TODO what about truststore?
validator.validate(keyStore, x509C);
}
LOG.info("x509={} for {}", x509, this);
for (String h : x509.getHosts()) {
_certHosts.put(h, x509);
}
for (String w : x509.getWilds()) {
_certWilds.put(w, x509);
}
}
}
}
// Instantiate key and trust managers
KeyManager[] keyManagers = getKeyManagers(keyStore);
TrustManager[] trustManagers = getTrustManagers(trustStore, crls);
// Initialize context
context = getSSLContextInstance();
context.init(keyManagers, trustManagers, getSecureRandomInstance());
}
}
// Initialize cache
SSLSessionContext serverContext = context.getServerSessionContext();
if (serverContext != null) {
if (getSslSessionCacheSize() > -1)
serverContext.setSessionCacheSize(getSslSessionCacheSize());
if (getSslSessionTimeout() > -1)
serverContext.setSessionTimeout(getSslSessionTimeout());
}
// select the protocols and ciphers
SSLParameters enabled = context.getDefaultSSLParameters();
SSLParameters supported = context.getSupportedSSLParameters();
selectCipherSuites(enabled.getCipherSuites(), supported.getCipherSuites());
selectProtocols(enabled.getProtocols(), supported.getProtocols());
_factory = new Factory(keyStore, trustStore, context);
if (LOG.isDebugEnabled()) {
LOG.debug("Selected Protocols {} of {}", Arrays.asList(_selectedProtocols), Arrays.asList(supported.getProtocols()));
LOG.debug("Selected Ciphers {} of {}", Arrays.asList(_selectedCipherSuites), Arrays.asList(supported.getCipherSuites()));
}
}
@Override
public String dump() {
return Dumpable.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException {
try {
SSLEngine sslEngine = SSLContext.getDefault().createSSLEngine();
Dumpable.dumpObjects(out, indent, this, "trustAll=" + _trustAll, new SslSelectionDump("Protocol", sslEngine.getSupportedProtocols(), sslEngine.getEnabledProtocols(), getExcludeProtocols(), getIncludeProtocols()), new SslSelectionDump("Cipher Suite", sslEngine.getSupportedCipherSuites(), sslEngine.getEnabledCipherSuites(), getExcludeCipherSuites(), getIncludeCipherSuites()));
} catch (NoSuchAlgorithmException x) {
LOG.ignore(x);
}
}
List selectionDump() throws NoSuchAlgorithmException {
/* Use a pristine SSLEngine (not one from this SslContextFactory).
* This will allow for proper detection and identification
* of JRE/lib/security/java.security level disabled features
*/
SSLEngine sslEngine = SSLContext.getDefault().createSSLEngine();
List selections = new ArrayList<>();
// protocols
selections.add(new SslSelectionDump("Protocol", sslEngine.getSupportedProtocols(), sslEngine.getEnabledProtocols(), getExcludeProtocols(), getIncludeProtocols()));
// ciphers
selections.add(new SslSelectionDump("Cipher Suite", sslEngine.getSupportedCipherSuites(), sslEngine.getEnabledCipherSuites(), getExcludeCipherSuites(), getIncludeCipherSuites()));
return selections;
}
@Override
protected void doStop() throws Exception {
synchronized (this) {
unload();
}
super.doStop();
}
private void unload() {
_factory = null;
_selectedProtocols = null;
_selectedCipherSuites = null;
_aliasX509.clear();
_certHosts.clear();
_certWilds.clear();
}
Map aliasCerts() {
return _aliasX509;
}
@ManagedAttribute(value = "The selected TLS protocol versions", readonly = true)
public String[] getSelectedProtocols() {
return Arrays.copyOf(_selectedProtocols, _selectedProtocols.length);
}
@ManagedAttribute(value = "The selected cipher suites", readonly = true)
public String[] getSelectedCipherSuites() {
return Arrays.copyOf(_selectedCipherSuites, _selectedCipherSuites.length);
}
public Comparator getCipherComparator() {
return _cipherComparator;
}
public void setCipherComparator(Comparator cipherComparator) {
if (cipherComparator != null)
setUseCipherSuitesOrder(true);
_cipherComparator = cipherComparator;
}
public Set getAliases() {
return Collections.unmodifiableSet(_aliasX509.keySet());
}
public X509 getX509(String alias) {
return _aliasX509.get(alias);
}
/**
* @return The array of protocol names to exclude from
* {@link SSLEngine#setEnabledProtocols(String[])}
*/
@ManagedAttribute("The excluded TLS protocols")
public String[] getExcludeProtocols() {
return _excludeProtocols.toArray(new String[0]);
}
/**
* You can either use the exact Protocol name or a a regular expression.
*
* @param protocols The array of protocol names to exclude from
* {@link SSLEngine#setEnabledProtocols(String[])}
*/
public void setExcludeProtocols(String... protocols) {
_excludeProtocols.clear();
_excludeProtocols.addAll(Arrays.asList(protocols));
}
/**
* You can either use the exact Protocol name or a a regular expression.
*
* @param protocol Protocol name patterns to add to {@link SSLEngine#setEnabledProtocols(String[])}
*/
public void addExcludeProtocols(String... protocol) {
_excludeProtocols.addAll(Arrays.asList(protocol));
}
/**
* @return The array of protocol name patterns to include in
* {@link SSLEngine#setEnabledProtocols(String[])}
*/
@ManagedAttribute("The included TLS protocols")
public String[] getIncludeProtocols() {
return _includeProtocols.toArray(new String[0]);
}
/**
* You can either use the exact Protocol name or a a regular expression.
*
* @param protocols The array of protocol name patterns to include in
* {@link SSLEngine#setEnabledProtocols(String[])}
*/
public void setIncludeProtocols(String... protocols) {
_includeProtocols.clear();
_includeProtocols.addAll(Arrays.asList(protocols));
}
/**
* @return The array of cipher suite name patterns to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
@ManagedAttribute("The excluded cipher suites")
public String[] getExcludeCipherSuites() {
return _excludeCipherSuites.toArray(new String[0]);
}
/**
* You can either use the exact Cipher suite name or a a regular expression.
*
* @param cipherSuites The array of cipher suite names to exclude from
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void setExcludeCipherSuites(String... cipherSuites) {
_excludeCipherSuites.clear();
_excludeCipherSuites.addAll(Arrays.asList(cipherSuites));
}
/**
* You can either use the exact Cipher suite name or a a regular expression.
*
* @param cipher Cipher names to add to {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void addExcludeCipherSuites(String... cipher) {
_excludeCipherSuites.addAll(Arrays.asList(cipher));
}
/**
* @return The array of Cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
@ManagedAttribute("The included cipher suites")
public String[] getIncludeCipherSuites() {
return _includeCipherSuites.toArray(new String[0]);
}
/**
* You can either use the exact Cipher suite name or a a regular expression.
*
* @param cipherSuites The array of cipher suite names to include in
* {@link SSLEngine#setEnabledCipherSuites(String[])}
*/
public void setIncludeCipherSuites(String... cipherSuites) {
_includeCipherSuites.clear();
_includeCipherSuites.addAll(Arrays.asList(cipherSuites));
}
@ManagedAttribute("Whether to respect the cipher suites order")
public boolean isUseCipherSuitesOrder() {
return _useCipherSuitesOrder;
}
public void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) {
_useCipherSuitesOrder = useCipherSuitesOrder;
}
/**
* @return The file or URL of the SSL Key store.
*/
@ManagedAttribute("The keyStore path")
public String getKeyStorePath() {
return Objects.toString(_keyStoreResource, null);
}
/**
* @param keyStorePath The file or URL of the SSL Key store.
*/
public void setKeyStorePath(String keyStorePath) {
try {
_keyStoreResource = Resource.newResource(keyStorePath);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
/**
* @return The provider of the key store
*/
@ManagedAttribute("The keyStore provider name")
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")
*/
@ManagedAttribute("The keyStore type")
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
*/
@ManagedAttribute("The certificate alias")
public String getCertAlias() {
return _certAlias;
}
/**
* Set the default certificate Alias.
* This can be used if there are multiple non-SNI certificates
* to specify the certificate that should be used, or with SNI
* certificates to set a certificate to try if no others match
*
*
* @param certAlias Alias of SSL certificate for the connector
*/
public void setCertAlias(String certAlias) {
_certAlias = certAlias;
}
@ManagedAttribute("The trustStore path")
public String getTrustStorePath() {
return Objects.toString(_trustStoreResource, null);
}
/**
* @param trustStorePath The file name or URL of the trust store location
*/
public void setTrustStorePath(String trustStorePath) {
try {
_trustStoreResource = Resource.newResource(trustStorePath);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
/**
* @return The provider of the trust store
*/
@ManagedAttribute("The trustStore provider name")
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
*/
@ManagedAttribute("The trustStore type")
public String getTrustStoreType() {
return _trustStoreType;
}
/**
* @param trustStoreType The type of the trust store
*/
public void setTrustStoreType(String trustStoreType) {
_trustStoreType = trustStoreType;
}
/**
* @return True if SSL needs client authentication.
* @see SSLEngine#getNeedClientAuth()
* @deprecated use {@link Server#getNeedClientAuth()} instead
*/
@ManagedAttribute("Whether client authentication is needed")
@Deprecated
public boolean getNeedClientAuth() {
return _needClientAuth;
}
/**
* @param needClientAuth True if SSL needs client authentication.
* @see SSLEngine#getNeedClientAuth()
* @deprecated use {@link Server#setNeedClientAuth(boolean)} instead
*/
@Deprecated
public void setNeedClientAuth(boolean needClientAuth) {
_needClientAuth = needClientAuth;
}
/**
* @return True if SSL wants client authentication.
* @see SSLEngine#getWantClientAuth()
* @deprecated use {@link Server#getWantClientAuth()} instead
*/
@ManagedAttribute("Whether client authentication is wanted")
@Deprecated
public boolean getWantClientAuth() {
return _wantClientAuth;
}
/**
* @param wantClientAuth True if SSL wants client authentication.
* @see SSLEngine#getWantClientAuth()
* @deprecated use {@link Server#setWantClientAuth(boolean)} instead
*/
@Deprecated
public void setWantClientAuth(boolean wantClientAuth) {
_wantClientAuth = wantClientAuth;
}
/**
* @return true if SSL certificate has to be validated
*/
@ManagedAttribute("Whether certificates are validated")
public boolean isValidateCerts() {
return _validateCerts;
}
/**
* @param validateCerts true if SSL certificates have to be validated
*/
public void setValidateCerts(boolean validateCerts) {
_validateCerts = validateCerts;
}
/**
* @return true if SSL certificates of the peer have to be validated
*/
@ManagedAttribute("Whether peer certificates are validated")
public boolean isValidatePeerCerts() {
return _validatePeerCerts;
}
/**
* @param validatePeerCerts true if SSL certificates of the peer have to be validated
*/
public void setValidatePeerCerts(boolean validatePeerCerts) {
_validatePeerCerts = validatePeerCerts;
}
/**
* @param password The password for the key store. If null is passed and
* a keystore is set, then
* the {@link #getPassword(String)} is used to
* obtain a password either from the {@value #PASSWORD_PROPERTY}
* system property or by prompting for manual entry.
*/
public void setKeyStorePassword(String password) {
if (password == null) {
if (_keyStoreResource != null)
_keyStorePassword = getPassword(PASSWORD_PROPERTY);
else
_keyStorePassword = null;
} else {
_keyStorePassword = newPassword(password);
}
}
/**
* @param password The password (if any) for the specific key within the key store.
* If null is passed and the {@value #KEYPASSWORD_PROPERTY} system property is set,
* then the {@link #getPassword(String)} is used to
* obtain a password from the {@value #KEYPASSWORD_PROPERTY} system property.
*/
public void setKeyManagerPassword(String password) {
if (password == null) {
if (System.getProperty(KEYPASSWORD_PROPERTY) != null)
_keyManagerPassword = getPassword(KEYPASSWORD_PROPERTY);
else
_keyManagerPassword = null;
} else {
_keyManagerPassword = newPassword(password);
}
}
/**
* @param password The password for the truststore. If null is passed and a truststore is set
* that is different from the keystore, then
* the {@link #getPassword(String)} is used to
* obtain a password either from the {@value #PASSWORD_PROPERTY}
* system property or by prompting for manual entry.
*/
public void setTrustStorePassword(String password) {
if (password == null) {
if (_trustStoreResource != null && !_trustStoreResource.equals(_keyStoreResource))
_trustStorePassword = getPassword(PASSWORD_PROPERTY);
else
_trustStorePassword = null;
} else {
_trustStorePassword = newPassword(password);
}
}
/**
*
* Get the optional Security Provider name.
*
*
* Security Provider name used with:
*
*
* - {@link SecureRandom#getInstance(String, String)}
* - {@link SSLContext#getInstance(String, String)}
* - {@link TrustManagerFactory#getInstance(String, String)}
* - {@link KeyManagerFactory#getInstance(String, String)}
* - {@link CertStore#getInstance(String, CertStoreParameters, String)}
* - {@link java.security.cert.CertificateFactory#getInstance(String, String)}
*
*
* @return The optional Security Provider name.
*/
@ManagedAttribute("The provider name")
public String getProvider() {
return _sslProvider;
}
/**
*
* Set the optional Security Provider name.
*
*
* Security Provider name used with:
*
*
* - {@link SecureRandom#getInstance(String, String)}
* - {@link SSLContext#getInstance(String, String)}
* - {@link TrustManagerFactory#getInstance(String, String)}
* - {@link KeyManagerFactory#getInstance(String, String)}
* - {@link CertStore#getInstance(String, CertStoreParameters, String)}
* - {@link java.security.cert.CertificateFactory#getInstance(String, String)}
*
*
* @param provider The optional Security Provider name.
*/
public void setProvider(String provider) {
_sslProvider = provider;
}
/**
* @return The SSL protocol (default "TLS") passed to
* {@link SSLContext#getInstance(String, String)}
*/
@ManagedAttribute("The TLS protocol")
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)}
*/
@ManagedAttribute("The SecureRandom algorithm")
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}
*/
@ManagedAttribute("The KeyManagerFactory algorithm")
public String getKeyManagerFactoryAlgorithm() {
return _keyManagerFactoryAlgorithm;
}
/**
* @param algorithm The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
*/
public void setKeyManagerFactoryAlgorithm(String algorithm) {
_keyManagerFactoryAlgorithm = algorithm;
}
/**
* @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
*/
@ManagedAttribute("The TrustManagerFactory algorithm")
public String getTrustManagerFactoryAlgorithm() {
return _trustManagerFactoryAlgorithm;
}
/**
* @return True if all certificates should be trusted if there is no KeyStore or TrustStore
*/
@ManagedAttribute("Whether certificates should be trusted even if they are invalid")
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;
if (trustAll)
setEndpointIdentificationAlgorithm(null);
}
/**
* @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) {
_trustManagerFactoryAlgorithm = algorithm;
}
/**
* @return whether TLS renegotiation is allowed (true by default)
*/
@ManagedAttribute("Whether renegotiation is allowed")
public boolean isRenegotiationAllowed() {
return _renegotiationAllowed;
}
/**
* @param renegotiationAllowed whether TLS renegotiation is allowed
*/
public void setRenegotiationAllowed(boolean renegotiationAllowed) {
_renegotiationAllowed = renegotiationAllowed;
}
/**
* @return The number of renegotiations allowed for this connection. When the limit
* is 0 renegotiation will be denied. If the limit is less than 0 then no limit is applied.
*/
@ManagedAttribute("The max number of renegotiations allowed")
public int getRenegotiationLimit() {
return _renegotiationLimit;
}
/**
* @param renegotiationLimit The number of renegotions allowed for this connection.
* When the limit is 0 renegotiation will be denied. If the limit is less than 0 then no limit is applied.
* Default 5.
*/
public void setRenegotiationLimit(int renegotiationLimit) {
_renegotiationLimit = renegotiationLimit;
}
/**
* @return Path to file that contains Certificate Revocation List
*/
@ManagedAttribute("The path to the certificate revocation list file")
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)
*/
@ManagedAttribute("The maximum number of intermediate certificates")
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() {
if (!isStarted())
return _setContext;
synchronized (this) {
if (_factory == null)
throw new IllegalStateException("SslContextFactory reload failed");
return _factory._context;
}
}
/**
* @param sslContext Set a preconfigured SSLContext
*/
public void setSslContext(SSLContext sslContext) {
_setContext = sslContext;
}
/**
* @return the endpoint identification algorithm
*/
@ManagedAttribute("The endpoint identification algorithm")
public String getEndpointIdentificationAlgorithm() {
return _endpointIdentificationAlgorithm;
}
/**
* When set to "HTTPS" hostname verification will be enabled.
* Deployments can be vulnerable to a man-in-the-middle attack if a EndpointIndentificationAlgorithm
* is not set.
*
* @param endpointIdentificationAlgorithm Set the endpointIdentificationAlgorithm
* @see #setHostnameVerifier(HostnameVerifier)
*/
public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
_endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
}
public PKIXCertPathChecker getPkixCertPathChecker() {
return _pkixCertPathChecker;
}
public void setPkixCertPathChecker(PKIXCertPathChecker pkixCertPatchChecker) {
_pkixCertPathChecker = pkixCertPatchChecker;
}
/**
* Override this method to provide alternate way to load a keystore.
*
* @param resource the resource to load the keystore from
* @return the key store instance
* @throws Exception if the keystore cannot be loaded
*/
protected KeyStore loadKeyStore(Resource resource) throws Exception {
String storePassword = Objects.toString(_keyStorePassword, null);
return CertificateUtils.getKeyStore(resource, getKeyStoreType(), getKeyStoreProvider(), storePassword);
}
/**
* Override this method to provide alternate way to load a truststore.
*
* @param resource the resource to load the truststore from
* @return the key store instance
* @throws Exception if the truststore cannot be loaded
*/
protected KeyStore loadTrustStore(Resource resource) throws Exception {
String type = Objects.toString(getTrustStoreType(), getKeyStoreType());
String provider = Objects.toString(getTrustStoreProvider(), getKeyStoreProvider());
Password passwd = _trustStorePassword;
if (resource == null || resource.equals(_keyStoreResource)) {
resource = _keyStoreResource;
if (passwd == null)
passwd = _keyStorePassword;
}
return CertificateUtils.getKeyStore(resource, type, provider, Objects.toString(passwd, null));
}
/**
* 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 = getKeyManagerFactoryInstance();
keyManagerFactory.init(keyStore, _keyManagerPassword == null ? (_keyStorePassword == null ? null : _keyStorePassword.toString().toCharArray()) : _keyManagerPassword.toString().toCharArray());
managers = keyManagerFactory.getKeyManagers();
if (managers != null) {
String alias = getCertAlias();
if (alias != null) {
for (int idx = 0; idx < managers.length; idx++) {
if (managers[idx] instanceof X509ExtendedKeyManager)
managers[idx] = new AliasedX509ExtendedKeyManager((X509ExtendedKeyManager) managers[idx], alias);
}
}
// Is SNI needed to select a certificate?
boolean sniRequired = (this instanceof Server) && ((Server) this).isSniRequired();
if (sniRequired || !_certWilds.isEmpty() || _certHosts.size() > 1 || (_certHosts.size() == 1 && _aliasX509.size() > 1)) {
for (int idx = 0; idx < managers.length; idx++) {
if (managers[idx] instanceof X509ExtendedKeyManager)
managers[idx] = newSniX509ExtendedKeyManager((X509ExtendedKeyManager) managers[idx]);
}
}
}
}
if (LOG.isDebugEnabled())
LOG.debug("managers={} for {}", managers, this);
return managers;
}
/**
* @deprecated use {@link SslContextFactory.Server#newSniX509ExtendedKeyManager(X509ExtendedKeyManager)} instead
*/
@Deprecated
protected X509ExtendedKeyManager newSniX509ExtendedKeyManager(X509ExtendedKeyManager keyManager) {
throw new IllegalStateException(String.format("KeyStores with multiple certificates are not supported on the base class %s. (Use %s or %s instead)", SslContextFactory.class.getName(), Server.class.getName(), Client.class.getName()));
}
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 (isValidatePeerCerts() && "PKIX".equalsIgnoreCase(getTrustManagerFactoryAlgorithm())) {
PKIXBuilderParameters pbParams = newPKIXBuilderParameters(trustStore, crls);
TrustManagerFactory trustManagerFactory = getTrustManagerFactoryInstance();
trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams));
managers = trustManagerFactory.getTrustManagers();
} else {
TrustManagerFactory trustManagerFactory = getTrustManagerFactoryInstance();
trustManagerFactory.init(trustStore);
managers = trustManagerFactory.getTrustManagers();
}
}
return managers;
}
protected PKIXBuilderParameters newPKIXBuilderParameters(KeyStore trustStore, Collection extends CRL> crls) throws Exception {
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 (_pkixCertPathChecker != null)
pbParams.addCertPathChecker(_pkixCertPathChecker);
if (crls != null && !crls.isEmpty()) {
pbParams.addCertStore(getCertStoreInstance(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);
}
}
return pbParams;
}
/**
* 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
*/
public void selectProtocols(String[] enabledProtocols, String[] supportedProtocols) {
List selectedProtocols = processIncludeExcludePatterns("Protocols", enabledProtocols, supportedProtocols, _includeProtocols, _excludeProtocols);
if (selectedProtocols.isEmpty())
LOG.warn("No selected Protocols from {}", Arrays.asList(supportedProtocols));
_selectedProtocols = selectedProtocols.toArray(new String[0]);
}
/**
* 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
*/
protected void selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites) {
List selectedCiphers = processIncludeExcludePatterns("Cipher Suite", enabledCipherSuites, supportedCipherSuites, _includeCipherSuites, _excludeCipherSuites);
if (selectedCiphers.isEmpty())
LOG.warn("No supported Cipher Suite from {}", Arrays.asList(supportedCipherSuites));
Comparator comparator = getCipherComparator();
if (comparator != null) {
if (LOG.isDebugEnabled())
LOG.debug("Sorting selected ciphers with {}", comparator);
selectedCiphers.sort(comparator);
}
_selectedCipherSuites = selectedCiphers.toArray(new String[0]);
}
private List processIncludeExcludePatterns(String type, String[] enabled, String[] supported, Set included, Set excluded) {
List selected = new ArrayList<>();
// Set the starting list - either from the included or enabled list
if (included.isEmpty()) {
selected.addAll(Arrays.asList(enabled));
} else {
// process include patterns
for (String includedItem : included) {
Pattern pattern = Pattern.compile(includedItem);
boolean added = false;
for (String supportedItem : supported) {
if (pattern.matcher(supportedItem).matches()) {
added = true;
selected.add(supportedItem);
}
}
if (!added)
LOG.info("No {} matching '{}' is supported", type, includedItem);
}
}
// process exclude patterns
for (String excludedItem : excluded) {
Pattern pattern = Pattern.compile(excludedItem);
selected.removeIf(selectedItem -> pattern.matcher(selectedItem).matches());
}
return selected;
}
/**
* @deprecated no replacement
*/
@Deprecated
protected void processIncludeCipherSuites(String[] supportedCipherSuites, List selectedCiphers) {
}
/**
* @deprecated no replacement
*/
@Deprecated
protected void removeExcludedCipherSuites(List selectedCiphers) {
}
/**
* Check if the lifecycle has been started and throw runtime exception
*/
private void checkIsStarted() {
if (!isStarted())
throw new IllegalStateException("!STARTED: " + this);
}
/**
* @return true if CRL Distribution Points support is enabled
*/
@ManagedAttribute("Whether certificate revocation list distribution points 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) {
_enableCRLDP = enableCRLDP;
}
/**
* @return true if On-Line Certificate Status Protocol support is enabled
*/
@ManagedAttribute("Whether online 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) {
_enableOCSP = enableOCSP;
}
/**
* @return Location of the OCSP Responder
*/
@ManagedAttribute("The online certificate status protocol URL")
public String getOcspResponderURL() {
return _ocspResponderURL;
}
/**
* Set the location of the OCSP Responder.
*
* @param ocspResponderURL location of the OCSP Responder
*/
public void setOcspResponderURL(String ocspResponderURL) {
_ocspResponderURL = ocspResponderURL;
}
/**
* Set the key store.
*
* @param keyStore the key store to set
*/
public void setKeyStore(KeyStore keyStore) {
_setKeyStore = keyStore;
}
public KeyStore getKeyStore() {
if (!isStarted())
return _setKeyStore;
synchronized (this) {
if (_factory == null)
throw new IllegalStateException("SslContextFactory reload failed");
return _factory._keyStore;
}
}
/**
* Set the trust store.
*
* @param trustStore the trust store to set
*/
public void setTrustStore(KeyStore trustStore) {
_setTrustStore = trustStore;
}
public KeyStore getTrustStore() {
if (!isStarted())
return _setTrustStore;
synchronized (this) {
if (_factory == null)
throw new IllegalStateException("SslContextFactory reload failed");
return _factory._trustStore;
}
}
/**
* Set the key store resource.
*
* @param resource the key store resource to set
*/
public void setKeyStoreResource(Resource resource) {
_keyStoreResource = resource;
}
public Resource getKeyStoreResource() {
return _keyStoreResource;
}
/**
* Set the trust store resource.
*
* @param resource the trust store resource to set
*/
public void setTrustStoreResource(Resource resource) {
_trustStoreResource = resource;
}
public Resource getTrustStoreResource() {
return _trustStoreResource;
}
/**
* @return true if SSL Session caching is enabled
*/
@ManagedAttribute("Whether TLS session caching is enabled")
public boolean isSessionCachingEnabled() {
return _sessionCachingEnabled;
}
/**
* Set the flag to enable SSL Session caching.
* If set to true, then the {@link SSLContext#createSSLEngine(String, int)} method is
* used to pass host and port information as a hint for session reuse. Note that
* this is only a hint and session may not be reused. Moreover, the hint is typically
* only used on client side implementations and setting this to false does not
* stop a server from accepting an offered session ID to reuse.
*
* @param enableSessionCaching the value of the flag
*/
public void setSessionCachingEnabled(boolean enableSessionCaching) {
_sessionCachingEnabled = enableSessionCaching;
}
/**
* Get SSL session cache size.
* Passed directly to {@link SSLSessionContext#setSessionCacheSize(int)}
*
* @return SSL session cache size
*/
@ManagedAttribute("The maximum TLS session cache size")
public int getSslSessionCacheSize() {
return _sslSessionCacheSize;
}
/**
* Set SSL session cache size.
* Set the max cache size to be set on {@link SSLSessionContext#setSessionCacheSize(int)}
* when this factory is started.
*
* @param sslSessionCacheSize SSL session cache size to set. A value of -1 (default) uses
* the JVM default, 0 means unlimited and positive number is a max size.
*/
public void setSslSessionCacheSize(int sslSessionCacheSize) {
_sslSessionCacheSize = sslSessionCacheSize;
}
/**
* Get SSL session timeout.
*
* @return SSL session timeout
*/
@ManagedAttribute("The TLS session cache timeout, in seconds")
public int getSslSessionTimeout() {
return _sslSessionTimeout;
}
/**
* Set SSL session timeout.
* Set the timeout in seconds to be set on {@link SSLSessionContext#setSessionTimeout(int)}
* when this factory is started.
*
* @param sslSessionTimeout SSL session timeout to set in seconds. A value of -1 (default) uses
* the JVM default, 0 means unlimited and positive number is a timeout in seconds.
*/
public void setSslSessionTimeout(int sslSessionTimeout) {
_sslSessionTimeout = sslSessionTimeout;
}
/**
* @return the HostnameVerifier used by a client to verify host names in the server certificate
*/
public HostnameVerifier getHostnameVerifier() {
return _hostnameVerifier;
}
/**
* Sets a {@code HostnameVerifier} used by a client to verify host names in the server certificate.
* The {@code HostnameVerifier} works in conjunction with {@link #setEndpointIdentificationAlgorithm(String)}.
* When {@code endpointIdentificationAlgorithm=="HTTPS"} (the default) the JDK TLS implementation
* checks that the host name indication set by the client matches the host names in the server certificate.
* If this check passes successfully, the {@code HostnameVerifier} is invoked and the application
* can perform additional checks and allow/deny the connection to the server.
* When {@code endpointIdentificationAlgorithm==null} the JDK TLS implementation will not check
* the host names, and any check is therefore performed only by the {@code HostnameVerifier.}
*
* @param hostnameVerifier the HostnameVerifier used by a client to verify host names in the server certificate
*/
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
_hostnameVerifier = hostnameVerifier;
}
/**
* Returns the password object for the given realm.
*
* @param realm the realm
* @return the Password object
*/
protected Password getPassword(String realm) {
return Password.getPassword(realm, null, null);
}
/**
* Creates a new Password object.
*
* @param password the password string
* @return the new Password object
*/
public Password newPassword(String password) {
return new Password(password);
}
public SSLServerSocket newSslServerSocket(String host, int port, int backlog) throws IOException {
checkIsStarted();
SSLContext context = getSslContext();
SSLServerSocketFactory factory = context.getServerSocketFactory();
SSLServerSocket socket = (SSLServerSocket) (host == null ? factory.createServerSocket(port, backlog) : factory.createServerSocket(port, backlog, InetAddress.getByName(host)));
socket.setSSLParameters(customize(socket.getSSLParameters()));
return socket;
}
public SSLSocket newSslSocket() throws IOException {
checkIsStarted();
SSLContext context = getSslContext();
SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket();
socket.setSSLParameters(customize(socket.getSSLParameters()));
return socket;
}
protected CertificateFactory getCertificateFactoryInstance(String type) throws CertificateException {
String provider = getProvider();
try {
if (provider != null) {
return CertificateFactory.getInstance(type, provider);
}
} catch (Throwable cause) {
LOG.info("Unable to get CertificateFactory instance for type [{}] on provider [{}], using default", type, provider);
if (LOG.isDebugEnabled())
LOG.debug(cause);
}
return CertificateFactory.getInstance(type);
}
protected CertStore getCertStoreInstance(Collection extends CRL> crls) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
String type = "Collection";
String provider = getProvider();
try {
if (provider != null) {
return CertStore.getInstance(type, new CollectionCertStoreParameters(crls), provider);
}
} catch (Throwable cause) {
LOG.info("Unable to get CertStore instance for type [{}] on provider [{}], using default", type, provider);
if (LOG.isDebugEnabled())
LOG.debug(cause);
}
return CertStore.getInstance(type, new CollectionCertStoreParameters(crls));
}
protected KeyManagerFactory getKeyManagerFactoryInstance() throws NoSuchAlgorithmException {
String algorithm = getKeyManagerFactoryAlgorithm();
String provider = getProvider();
try {
if (provider != null) {
return KeyManagerFactory.getInstance(algorithm, provider);
}
} catch (Throwable cause) {
// fall back to non-provider option
LOG.info("Unable to get KeyManagerFactory instance for algorithm [{}] on provider [{}], using default", algorithm, provider);
if (LOG.isDebugEnabled())
LOG.debug(cause);
}
return KeyManagerFactory.getInstance(algorithm);
}
protected SecureRandom getSecureRandomInstance() throws NoSuchAlgorithmException {
String algorithm = getSecureRandomAlgorithm();
if (algorithm != null) {
String provider = getProvider();
try {
if (provider != null) {
return SecureRandom.getInstance(algorithm, provider);
}
} catch (Throwable cause) {
LOG.info("Unable to get SecureRandom instance for algorithm [{}] on provider [{}], using default", algorithm, provider);
if (LOG.isDebugEnabled())
LOG.debug(cause);
}
return SecureRandom.getInstance(algorithm);
}
return null;
}
protected SSLContext getSSLContextInstance() throws NoSuchAlgorithmException {
String protocol = getProtocol();
String provider = getProvider();
try {
if (provider != null) {
return SSLContext.getInstance(protocol, provider);
}
} catch (Throwable cause) {
LOG.info("Unable to get SSLContext instance for protocol [{}] on provider [{}], using default", protocol, provider);
if (LOG.isDebugEnabled())
LOG.debug(cause);
}
return SSLContext.getInstance(protocol);
}
protected TrustManagerFactory getTrustManagerFactoryInstance() throws NoSuchAlgorithmException {
String algorithm = getTrustManagerFactoryAlgorithm();
String provider = getProvider();
try {
if (provider != null) {
return TrustManagerFactory.getInstance(algorithm, provider);
}
} catch (Throwable cause) {
LOG.info("Unable to get TrustManagerFactory instance for algorithm [{}] on provider [{}], using default", algorithm, provider);
if (LOG.isDebugEnabled()) {
LOG.debug(cause);
}
}
return TrustManagerFactory.getInstance(algorithm);
}
/**
* Factory method for "scratch" {@link SSLEngine}s, usually only used for retrieving configuration
* information such as the application buffer size or the list of protocols/ciphers.
*
* This method should not be used for creating {@link SSLEngine}s that are used in actual socket
* communication.
*
* @return a new, "scratch" {@link SSLEngine}
*/
public SSLEngine newSSLEngine() {
checkIsStarted();
SSLContext context = getSslContext();
SSLEngine sslEngine = context.createSSLEngine();
customize(sslEngine);
return sslEngine;
}
/**
* General purpose factory method for creating {@link SSLEngine}s, although creation of
* {@link SSLEngine}s on the server-side should prefer {@link #newSSLEngine(InetSocketAddress)}.
*
* @param host the remote host
* @param port the remote port
* @return a new {@link SSLEngine}
*/
public SSLEngine newSSLEngine(String host, int port) {
checkIsStarted();
SSLContext context = getSslContext();
SSLEngine sslEngine = isSessionCachingEnabled() ? context.createSSLEngine(host, port) : context.createSSLEngine();
customize(sslEngine);
return sslEngine;
}
/**
* Server-side only factory method for creating {@link SSLEngine}s.
*
* If the given {@code address} is null, it is equivalent to {@link #newSSLEngine()}, otherwise
* {@link #newSSLEngine(String, int)} is called.
*
* Clients that wish to create {@link SSLEngine} instances must use {@link #newSSLEngine(String, int)}.
*
* @param address the remote peer address
* @return a new {@link SSLEngine}
*/
public SSLEngine newSSLEngine(InetSocketAddress address) {
if (address == null)
return newSSLEngine();
return newSSLEngine(address.getHostString(), address.getPort());
}
/**
* Customize an SslEngine instance with the configuration of this factory,
* by calling {@link #customize(SSLParameters)}
*
* @param sslEngine the SSLEngine to customize
*/
public void customize(SSLEngine sslEngine) {
if (LOG.isDebugEnabled())
LOG.debug("Customize {}", sslEngine);
sslEngine.setSSLParameters(customize(sslEngine.getSSLParameters()));
}
/**
* Customize an SslParameters instance with the configuration of this factory.
*
* @param sslParams The parameters to customize
* @return The passed instance of sslParams (returned as a convenience)
*/
public SSLParameters customize(SSLParameters sslParams) {
sslParams.setEndpointIdentificationAlgorithm(getEndpointIdentificationAlgorithm());
sslParams.setUseCipherSuitesOrder(isUseCipherSuitesOrder());
if (!_certHosts.isEmpty() || !_certWilds.isEmpty())
sslParams.setSNIMatchers(Collections.singletonList(new AliasSNIMatcher()));
if (_selectedCipherSuites != null)
sslParams.setCipherSuites(_selectedCipherSuites);
if (_selectedProtocols != null)
sslParams.setProtocols(_selectedProtocols);
if (!(this instanceof Client)) {
if (getWantClientAuth())
sslParams.setWantClientAuth(true);
if (getNeedClientAuth())
sslParams.setNeedClientAuth(true);
}
return sslParams;
}
public void reload(Consumer consumer) throws Exception {
synchronized (this) {
consumer.accept(this);
unload();
load();
}
}
/**
* Obtain the X509 Certificate Chain from the provided SSLSession using this
* SslContextFactory's optional Provider specific {@link CertificateFactory}.
*
* @param sslSession the session to use for active peer certificates
* @return the certificate chain
*/
public X509Certificate[] getX509CertChain(SSLSession sslSession) {
return getX509CertChain(this, sslSession);
}
/**
* Obtain the X509 Certificate Chain from the provided SSLSession using the
* default {@link CertificateFactory} behaviors
*
* @param sslSession the session to use for active peer certificates
* @return the certificate chain
*/
public static X509Certificate[] getCertChain(SSLSession sslSession) {
return getX509CertChain(null, sslSession);
}
private static X509Certificate[] getX509CertChain(SslContextFactory sslContextFactory, SSLSession sslSession) {
try {
Certificate[] javaxCerts = sslSession.getPeerCertificates();
if (javaxCerts == null || javaxCerts.length == 0)
return null;
int length = javaxCerts.length;
X509Certificate[] javaCerts = new X509Certificate[length];
String type = "X.509";
CertificateFactory cf;
if (sslContextFactory != null) {
cf = sslContextFactory.getCertificateFactoryInstance(type);
} else {
cf = CertificateFactory.getInstance(type);
}
for (int i = 0; i < length; i++) {
byte[] bytes = javaxCerts[i].getEncoded();
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
javaCerts[i] = (X509Certificate) cf.generateCertificate(stream);
}
return javaCerts;
} catch (SSLPeerUnverifiedException pue) {
return null;
} catch (Exception e) {
LOG.warn(Log.EXCEPTION, e);
return null;
}
}
/**
* Given the name of a TLS/SSL cipher suite, return an int representing it effective stream
* cipher key strength. i.e. How much entropy material is in the key material being fed into the
* encryption routines.
*
* This is based on the information on effective key lengths in RFC 2246 - The TLS Protocol
* Version 1.0, Appendix C. CipherSuite definitions:
*
* Effective
* Cipher Type Key Bits
*
* NULL * Stream 0
* IDEA_CBC Block 128
* RC2_CBC_40 * Block 40
* RC4_40 * Stream 40
* RC4_128 Stream 128
* DES40_CBC * Block 40
* DES_CBC Block 56
* 3DES_EDE_CBC Block 168
*
*
* @param cipherSuite String name of the TLS cipher suite.
* @return int indicating the effective key entropy bit-length.
*/
public static int deduceKeyLength(String cipherSuite) {
// Roughly ordered from most common to least common.
if (cipherSuite == null)
return 0;
else if (cipherSuite.contains("WITH_AES_256_"))
return 256;
else if (cipherSuite.contains("WITH_RC4_128_"))
return 128;
else if (cipherSuite.contains("WITH_AES_128_"))
return 128;
else if (cipherSuite.contains("WITH_RC4_40_"))
return 40;
else if (cipherSuite.contains("WITH_3DES_EDE_CBC_"))
return 168;
else if (cipherSuite.contains("WITH_IDEA_CBC_"))
return 128;
else if (cipherSuite.contains("WITH_RC2_CBC_40_"))
return 40;
else if (cipherSuite.contains("WITH_DES40_CBC_"))
return 40;
else if (cipherSuite.contains("WITH_DES_CBC_"))
return 56;
else
return 0;
}
@Override
public String toString() {
return String.format("%s@%x[provider=%s,keyStore=%s,trustStore=%s]", getClass().getSimpleName(), hashCode(), _sslProvider, _keyStoreResource, _trustStoreResource);
}
// @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@Deprecated(since = "2021-05-27")
private static class Factory {
private final KeyStore _keyStore;
private final KeyStore _trustStore;
private final SSLContext _context;
Factory(KeyStore keyStore, KeyStore trustStore, SSLContext context) {
super();
_keyStore = keyStore;
_trustStore = trustStore;
_context = context;
}
}
// @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@Deprecated(since = "2021-05-27")
static class AliasSNIMatcher extends SNIMatcher {
private String _host;
AliasSNIMatcher() {
super(StandardConstants.SNI_HOST_NAME);
}
@Override
public boolean matches(SNIServerName serverName) {
if (LOG.isDebugEnabled())
LOG.debug("SNI matching for {}", serverName);
if (serverName instanceof SNIHostName) {
_host = StringUtil.asciiToLowerCase(((SNIHostName) serverName).getAsciiName());
if (LOG.isDebugEnabled())
LOG.debug("SNI host name {}", _host);
} else {
if (LOG.isDebugEnabled())
LOG.debug("No SNI host name for {}", serverName);
}
// Return true and allow the KeyManager to accept or reject when choosing a certificate.
// If we don't have a SNI host, or didn't see any certificate aliases,
// just say true as it will either somehow work or fail elsewhere.
return true;
}
public String getHost() {
return _host;
}
}
// @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@Deprecated(since = "2021-05-27")
public static class Client extends SslContextFactory {
private SniProvider sniProvider = (sslEngine, serverNames) -> serverNames;
public Client() {
this(false);
}
public Client(boolean trustAll) {
super(trustAll);
}
@Override
protected void checkConfiguration() {
checkTrustAll();
checkEndPointIdentificationAlgorithm();
super.checkConfiguration();
}
@Override
protected X509ExtendedKeyManager newSniX509ExtendedKeyManager(X509ExtendedKeyManager keyManager) {
// Client has no SNI functionality.
return keyManager;
}
@Override
public void customize(SSLEngine sslEngine) {
SSLParameters sslParameters = sslEngine.getSSLParameters();
List serverNames = sslParameters.getServerNames();
if (serverNames == null)
serverNames = Collections.emptyList();
List newServerNames = getSNIProvider().apply(sslEngine, serverNames);
if (newServerNames != null && newServerNames != serverNames) {
sslParameters.setServerNames(newServerNames);
sslEngine.setSSLParameters(sslParameters);
}
super.customize(sslEngine);
}
/**
* @return the SNI provider used to customize the SNI
*/
public SniProvider getSNIProvider() {
return sniProvider;
}
/**
* @param sniProvider the SNI provider used to customize the SNI
*/
public void setSNIProvider(SniProvider sniProvider) {
this.sniProvider = Objects.requireNonNull(sniProvider);
}
/**
* A provider for SNI names to send to the server during the TLS handshake.
* By default, the OpenJDK TLS implementation does not send SNI names when
* they are IP addresses, following what currently specified in
* TLS 1.3,
* or when they are non-domain strings such as {@code "localhost"}.
* If you need to send custom SNI, such as a non-domain SNI or an IP address SNI,
* you can set your own SNI provider or use {@link #NON_DOMAIN_SNI_PROVIDER}.
*
* @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
*/
@FunctionalInterface
@Deprecated(since = "2021-05-27")
public interface SniProvider {
/**
* An SNI provider that, if the given {@code serverNames} list is empty,
* retrieves the host via {@link SSLEngine#getPeerHost()}, converts it to
* ASCII bytes, and sends it as SNI.
* This allows to send non-domain SNI such as {@code "localhost"} or
* IP addresses.
*/
public static final SniProvider NON_DOMAIN_SNI_PROVIDER = Client::getSniServerNames;
/**
* Provides the SNI names to send to the server.
* Currently, RFC 6066 allows for different types of server names,
* but defines only one of type "host_name".
* As such, the input {@code serverNames} list and the list to be returned
* contain at most one element.
*
* @param sslEngine the SSLEngine that processes the TLS handshake
* @param serverNames the non-null immutable list of server names computed by implementation
* @return either the same {@code serverNames} list passed as parameter, or a new list
* containing the server names to send to the server
*/
public List apply(SSLEngine sslEngine, List serverNames);
}
private static List getSniServerNames(SSLEngine sslEngine, List serverNames) {
if (serverNames.isEmpty()) {
String host = sslEngine.getPeerHost();
if (host != null) {
// Must use the byte[] constructor, because the character ':' is forbidden when
// using the String constructor (but typically present in IPv6 addresses).
// Since Java 17, only letter|digit|hyphen characters are allowed, even by the byte[] constructor.
return Collections.singletonList(new SNIHostName(host.getBytes(StandardCharsets.US_ASCII)));
}
}
return serverNames;
}
}
// @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@ManagedObject
@Deprecated(since = "2021-05-27")
public static class Server extends SslContextFactory implements SniX509ExtendedKeyManager.SniSelector {
private boolean _sniRequired;
private SniX509ExtendedKeyManager.SniSelector _sniSelector;
public Server() {
setEndpointIdentificationAlgorithm(null);
}
@Override
public boolean getWantClientAuth() {
return super.getWantClientAuth();
}
public void setWantClientAuth(boolean wantClientAuth) {
super.setWantClientAuth(wantClientAuth);
}
@Override
public boolean getNeedClientAuth() {
return super.getNeedClientAuth();
}
@Override
public void setNeedClientAuth(boolean needClientAuth) {
super.setNeedClientAuth(needClientAuth);
}
/**
* Returns whether an SNI match is required when choosing the alias that
* identifies the certificate to send to the client.
* The exact logic to choose an alias given the SNI is configurable via
* {@link #setSNISelector(SniX509ExtendedKeyManager.SniSelector)}.
* The default implementation is {@link #sniSelect(String, Principal[], SSLSession, String, Collection)}
* and if SNI is not required it will delegate the TLS implementation to
* choose an alias (typically the first alias in the KeyStore).
* Note that if a non SNI handshake is accepted, requests may still be rejected
* at the HTTP level for incorrect SNI (see SecureRequestCustomizer).
*
* @return whether an SNI match is required when choosing the alias that identifies the certificate
*/
@ManagedAttribute("Whether the TLS handshake is rejected if there is no SNI host match")
public boolean isSniRequired() {
return _sniRequired;
}
/**
* Sets whether an SNI match is required when choosing the alias that
* identifies the certificate to send to the client.
* This setting may have no effect if {@link #sniSelect(String, Principal[], SSLSession, String, Collection)} is
* overridden or a custom function is passed to {@link #setSNISelector(SniX509ExtendedKeyManager.SniSelector)}.
*
* @param sniRequired whether an SNI match is required when choosing the alias that identifies the certificate
*/
public void setSniRequired(boolean sniRequired) {
_sniRequired = sniRequired;
}
@Override
protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception {
KeyManager[] managers = super.getKeyManagers(keyStore);
if (isSniRequired()) {
if (managers == null || Arrays.stream(managers).noneMatch(SniX509ExtendedKeyManager.class::isInstance))
throw new IllegalStateException("No SNI Key managers when SNI is required");
}
return managers;
}
/**
* @return the custom function to select certificates based on SNI information
*/
public SniX509ExtendedKeyManager.SniSelector getSNISelector() {
return _sniSelector;
}
/**
* Sets a custom function to select certificates based on SNI information.
*
* @param sniSelector the selection function
*/
public void setSNISelector(SniX509ExtendedKeyManager.SniSelector sniSelector) {
_sniSelector = sniSelector;
}
@Override
public String sniSelect(String keyType, Principal[] issuers, SSLSession session, String sniHost, Collection certificates) {
boolean sniRequired = isSniRequired();
if (LOG.isDebugEnabled())
LOG.debug("Selecting alias: keyType={}, sni={}, sniRequired={}, certs={}", keyType, String.valueOf(sniHost), sniRequired, certificates);
String alias;
if (sniHost == null) {
// No SNI, so reject or delegate.
alias = sniRequired ? null : SniX509ExtendedKeyManager.SniSelector.DELEGATE;
} else {
// Match the SNI host.
List matching = certificates.stream().filter(x509 -> x509.matches(sniHost)).collect(Collectors.toList());
if (matching.isEmpty()) {
// There is no match for this SNI among the certificates valid for
// this keyType; check if there is any certificate that matches this
// SNI, as we will likely be called again with a different keyType.
boolean anyMatching = aliasCerts().values().stream().anyMatch(x509 -> x509.matches(sniHost));
alias = sniRequired || anyMatching ? null : SniX509ExtendedKeyManager.SniSelector.DELEGATE;
} else {
alias = matching.get(0).getAlias();
if (matching.size() > 1) {
// Prefer strict matches over wildcard matches.
alias = matching.stream().min(Comparator.comparingInt(cert -> cert.getWilds().size())).map(X509::getAlias).orElse(alias);
}
}
}
if (LOG.isDebugEnabled())
LOG.debug("Selected alias={}", String.valueOf(alias));
return alias;
}
@Override
protected X509ExtendedKeyManager newSniX509ExtendedKeyManager(X509ExtendedKeyManager keyManager) {
return new SniX509ExtendedKeyManager(keyManager, this);
}
}
/**
* A wrapper that delegates to another (if not {@code null}) X509ExtendedKeyManager.
*
* @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
*/
@Deprecated(since = "2021-05-27")
public static class X509ExtendedKeyManagerWrapper extends X509ExtendedKeyManager {
private final X509ExtendedKeyManager keyManager;
public X509ExtendedKeyManagerWrapper(X509ExtendedKeyManager keyManager) {
this.keyManager = keyManager;
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return keyManager == null ? null : keyManager.getClientAliases(keyType, issuers);
}
@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
return keyManager == null ? null : keyManager.chooseClientAlias(keyType, issuers, socket);
}
@Override
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
return keyManager == null ? null : keyManager.chooseEngineClientAlias(keyType, issuers, engine);
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return keyManager == null ? null : keyManager.getServerAliases(keyType, issuers);
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return keyManager == null ? null : keyManager.chooseServerAlias(keyType, issuers, socket);
}
@Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
return keyManager == null ? null : keyManager.chooseEngineServerAlias(keyType, issuers, engine);
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return keyManager == null ? null : keyManager.getCertificateChain(alias);
}
@Override
public PrivateKey getPrivateKey(String alias) {
return keyManager == null ? null : keyManager.getPrivateKey(alias);
}
}
/**
* A wrapper that delegates to another (if not {@code null}) X509ExtendedTrustManager.
*
* @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
*/
@Deprecated(since = "2021-05-27")
public static class X509ExtendedTrustManagerWrapper extends X509ExtendedTrustManager {
private final X509ExtendedTrustManager trustManager;
public X509ExtendedTrustManagerWrapper(X509ExtendedTrustManager trustManager) {
this.trustManager = trustManager;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager == null ? new X509Certificate[0] : trustManager.getAcceptedIssuers();
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
if (trustManager != null)
trustManager.checkClientTrusted(certs, authType);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
if (trustManager != null)
trustManager.checkClientTrusted(chain, authType, socket);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
if (trustManager != null)
trustManager.checkClientTrusted(chain, authType, engine);
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
if (trustManager != null)
trustManager.checkServerTrusted(certs, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
if (trustManager != null)
trustManager.checkServerTrusted(chain, authType, socket);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
if (trustManager != null)
trustManager.checkServerTrusted(chain, authType, engine);
}
}
}