All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.mklinger.qetcher.client.jetty.util.ssl.SslContextFactory Maven / Gradle / Ivy

There is a newer version: 2.0.42.rc
Show newest version
//
//  ========================================================================
//  Copyright (c) 1995-2018 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.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
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.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.Iterator;
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.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.CertPathTrustManagerParameters;
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.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.ContainerLifeCycle;
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 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.
 */
@ManagedObject
public class SslContextFactory extends AbstractLifeCycle implements Dumpable
{
    public final static TrustManager[] TRUST_ALL_CERTS = new X509TrustManager[]{new X509TrustManager()
    {
        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers()
        {
            return new java.security.cert.X509Certificate[]{};
        }

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

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

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

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

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

    /** 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 final Set _excludeProtocols = new LinkedHashSet<>();
    private final Set _includeProtocols = new LinkedHashSet<>();
    private final Set _excludeCipherSuites = new LinkedHashSet<>();
    private final List _includeCipherSuites = new ArrayList<>();
    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 = null;
    private boolean _trustAll;
    private boolean _renegotiationAllowed = true;
    private int _renegotiationLimit = 5;
    private Factory _factory;
    private PKIXCertPathChecker _pkixCertPathChecker;

    /**
     * Construct an instance of SslContextFactory
     * Default constructor for use in XmlConfiguration files
     */
    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)
     */
    public SslContextFactory(boolean trustAll)
    {
        this(trustAll, null);
    }

    /**
     * Construct an instance of SslContextFactory
     *
     * @param keyStorePath default keystore location
     */
    public SslContextFactory(String keyStorePath)
    {
        this(false, keyStorePath);
    }

    private SslContextFactory(boolean trustAll, String keyStorePath)
    {
        setTrustAll(trustAll);
        addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
        setExcludeCipherSuites("^.*_(MD5|SHA|SHA1)$");
        if (keyStorePath != null)
            setKeyStorePath(keyStorePath);
    }

    /**
     * Creates the SSLContext object and starts the lifecycle
     */
    @Override
    protected void doStart() throws Exception
    {
        super.doStart();
        synchronized (this)
        {
            load();
        }
    }

    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[] trust_managers = 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
                    trust_managers = TRUST_ALL_CERTS;
                }

                String algorithm = getSecureRandomAlgorithm();
                SecureRandom secureRandom = algorithm == null ? null : SecureRandom.getInstance(algorithm);
                context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
                context.init(null, trust_managers, secureRandom);
            }
            else
            {
                if (keyStore == null)
                    keyStore = loadKeyStore(_keyStoreResource);
                if (trustStore == null)
                    trustStore = loadTrustStore(_trustStoreResource);

                Collection 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());
                                validator.validate(keyStore, x509C); // TODO what about truststore?
                            }

                            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
                SecureRandom secureRandom = (_secureRandomAlgorithm == null) ? null : SecureRandom.getInstance(_secureRandomAlgorithm);
                context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
                context.init(keyManagers, trustManagers, secureRandom);
            }
        }

        // 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 ContainerLifeCycle.dump(this);
    }
    
    @Override
    public void dump(Appendable out, String indent) throws IOException
    {
        out.append(String.valueOf(this)).append(" trustAll=").append(Boolean.toString(_trustAll)).append(System.lineSeparator());
    
        try
        {
            /* 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()));
            
            ContainerLifeCycle.dump(out, indent, selections);
        }
        catch (NoSuchAlgorithmException ignore)
        {
            LOG.ignore(ignore);
        }
    }
    
    @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();
    }

    @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]);
    }

    /**
     * @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));
    }

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

    /**
     * @return The array of protocol names to include in
     * {@link SSLEngine#setEnabledProtocols(String[])}
     */
    @ManagedAttribute("The included TLS protocols")
    public String[] getIncludeProtocols()
    {
        return _includeProtocols.toArray(new String[0]);
    }

    /**
     * @param protocols The array of protocol names 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 names 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));
    }

    /**
     * @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() */ @ManagedAttribute("Whether client authentication is needed") 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() */ @ManagedAttribute("Whether client authentication is wanted") 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 */ @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); } } /** * @return The SSL provider name, which if set is passed to * {@link SSLContext#getInstance(String, String)} */ @ManagedAttribute("The provider name") 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)} */ @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) { 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 * * @param endpointIdentificationAlgorithm Set the endpointIdentificationAlgorithm */ 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()); String passwd = Objects.toString(_trustStorePassword, Objects.toString(_keyStorePassword, null)); if (resource == null) resource = _keyStoreResource; return CertificateUtils.getKeyStore(resource, type, provider, passwd); } /** * 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 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(getKeyManagerFactoryAlgorithm()); 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); } } if (!_certWilds.isEmpty() || _certHosts.size()>1) { for (int idx = 0; idx < managers.length; idx++) { if (managers[idx] instanceof X509ExtendedKeyManager) managers[idx] = new SniX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx]); } } } } if (LOG.isDebugEnabled()) LOG.debug("managers={} for {}", managers, this); return managers; } protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection 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 = 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; } protected PKIXBuilderParameters newPKIXBuilderParameters(KeyStore trustStore, Collection 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(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); } } 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) { Set selected_protocols = new LinkedHashSet<>(); // Set the starting protocols - either from the included or enabled list if (!_includeProtocols.isEmpty()) { // Use only the supported included protocols for (String protocol : _includeProtocols) { if (Arrays.asList(supportedProtocols).contains(protocol)) selected_protocols.add(protocol); else LOG.info("Protocol {} not supported in {}", protocol, Arrays.asList(supportedProtocols)); } } else selected_protocols.addAll(Arrays.asList(enabledProtocols)); // Remove any excluded protocols selected_protocols.removeAll(_excludeProtocols); if (selected_protocols.isEmpty()) LOG.warn("No selected protocols from {}", Arrays.asList(supportedProtocols)); _selectedProtocols = selected_protocols.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 selected_ciphers = new ArrayList<>(); // Set the starting ciphers - either from the included or enabled list if (_includeCipherSuites.isEmpty()) selected_ciphers.addAll(Arrays.asList(enabledCipherSuites)); else processIncludeCipherSuites(supportedCipherSuites, selected_ciphers); removeExcludedCipherSuites(selected_ciphers); if (selected_ciphers.isEmpty()) LOG.warn("No supported ciphers from {}", Arrays.asList(supportedCipherSuites)); Comparator comparator = getCipherComparator(); if (comparator != null) { if (LOG.isDebugEnabled()) LOG.debug("Sorting selected ciphers with {}", comparator); selected_ciphers.sort(comparator); } _selectedCipherSuites = selected_ciphers.toArray(new String[0]); } protected void processIncludeCipherSuites(String[] supportedCipherSuites, List selected_ciphers) { for (String cipherSuite : _includeCipherSuites) { Pattern p = Pattern.compile(cipherSuite); boolean added = false; for (String supportedCipherSuite : supportedCipherSuites) { Matcher m = p.matcher(supportedCipherSuite); if (m.matches()) { added = true; selected_ciphers.add(supportedCipherSuite); } } if (!added) LOG.info("No Cipher matching '{}' is supported", cipherSuite); } } protected void removeExcludedCipherSuites(List selected_ciphers) { for (String excludeCipherSuite : _excludeCipherSuites) { Pattern excludeCipherPattern = Pattern.compile(excludeCipherSuite); for (Iterator i = selected_ciphers.iterator(); i.hasNext(); ) { String selectedCipherSuite = i.next(); Matcher m = excludeCipherPattern.matcher(selectedCipherSuite); if (m.matches()) i.remove(); } } } /** * 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) { 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) { 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; } /** * 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; } /** * 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 (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(); } } public static X509Certificate[] getCertChain(SSLSession sslSession) { try { Certificate[] javaxCerts = sslSession.getPeerCertificates(); if (javaxCerts == null || javaxCerts.length == 0) return null; int length = javaxCerts.length; X509Certificate[] javaCerts = new X509Certificate[length]; java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X.509"); 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); } 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; } } class AliasSNIMatcher extends SNIMatcher { private String _host; private X509 _x509; 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) { String host = _host = ((SNIHostName)serverName).getAsciiName(); host = StringUtil.asciiToLowerCase(host); // Try an exact match _x509 = _certHosts.get(host); // Else try an exact wild match if (_x509 == null) { _x509 = _certWilds.get(host); // Else try an 1 deep wild match if (_x509 == null) { int dot = host.indexOf('.'); if (dot >= 0) { String domain = host.substring(dot + 1); _x509 = _certWilds.get(domain); } } } if (LOG.isDebugEnabled()) LOG.debug("SNI matched {}->{}", host, _x509); } else { if (LOG.isDebugEnabled()) LOG.debug("SNI no match 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; } public X509 getX509() { return _x509; } } }