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

org.bouncycastle.jsse.provider.ProvSSLContextSpi Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java APIs for the TLS, including a JSSE provider. The APIs are designed primarily to be used in conjunction with the BC FIPS provider. The APIs may also be used with other providers although if being used in a FIPS context it is the responsibility of the user to ensure that any other providers used are FIPS certified and used appropriately.

There is a newer version: 2.0.19
Show newest version
package org.bouncycastle.jsse.provider;

import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;

import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.jsse.BCX509ExtendedKeyManager;
import org.bouncycastle.jsse.BCX509ExtendedTrustManager;
import org.bouncycastle.jsse.java.security.BCAlgorithmConstraints;
import org.bouncycastle.jsse.java.security.BCCryptoPrimitive;
import org.bouncycastle.tls.CipherSuite;
import org.bouncycastle.tls.ProtocolVersion;
import org.bouncycastle.tls.TlsDHUtils;
import org.bouncycastle.tls.TlsUtils;
import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto;
import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider;

class ProvSSLContextSpi
    extends SSLContextSpi
{
    private static final Logger LOG = Logger.getLogger(ProvSSLContextSpi.class.getName());

    private static final String PROPERTY_CLIENT_CIPHERSUITES = "jdk.tls.client.cipherSuites";
    private static final String PROPERTY_SERVER_CIPHERSUITES = "jdk.tls.server.cipherSuites";

    private static final String PROPERTY_CLIENT_PROTOCOLS = "jdk.tls.client.protocols";
    private static final String PROPERTY_SERVER_PROTOCOLS = "jdk.tls.server.protocols";

    private static final Set TLS_CRYPTO_PRIMITIVES_BC = JsseUtils.KEY_AGREEMENT_CRYPTO_PRIMITIVES_BC;

    /*
     * TODO[jsse] Should separate this into "understood" cipher suite int<->String maps
     * and a Set of supported cipher suite values, so we can cover TLS_NULL_WITH_NULL_NULL and
     * the SCSV values.
     */
    private static final Map SUPPORTED_CIPHERSUITE_MAP = createSupportedCipherSuiteMap();
    private static final Map SUPPORTED_CIPHERSUITE_MAP_FIPS = createSupportedCipherSuiteMapFips(SUPPORTED_CIPHERSUITE_MAP);

    private static final Map SUPPORTED_PROTOCOL_MAP = createSupportedProtocolMap();
    private static final Map SUPPORTED_PROTOCOL_MAP_FIPS = createSupportedProtocolMapFips(SUPPORTED_PROTOCOL_MAP);

    private static final List DEFAULT_CIPHERSUITE_LIST = createDefaultCipherSuiteList(SUPPORTED_CIPHERSUITE_MAP.keySet());
    private static final List DEFAULT_CIPHERSUITE_LIST_FIPS = createDefaultCipherSuiteListFips(DEFAULT_CIPHERSUITE_LIST);

    private static final List DEFAULT_PROTOCOL_LIST = createDefaultProtocolList(SUPPORTED_PROTOCOL_MAP.keySet());
    private static final List DEFAULT_PROTOCOL_LIST_FIPS = createDefaultProtocolListFips(DEFAULT_PROTOCOL_LIST);

    private static void addCipherSuite(Map cs, String name, int cipherSuite)
    {
        CipherSuiteInfo cipherSuiteInfo = CipherSuiteInfo.forCipherSuite(cipherSuite, name);

        if (null != cs.put(name, cipherSuiteInfo))
        {
            throw new IllegalStateException("Duplicate names in supported-cipher-suites");
        }
    }

    private static List createDefaultCipherSuiteList(Set supportedCipherSuiteSet)
    {
        ArrayList cs = new ArrayList();

        // TLS 1.3+

        cs.add("TLS_CHACHA20_POLY1305_SHA256");
        cs.add("TLS_AES_256_GCM_SHA384");
        cs.add("TLS_AES_128_GCM_SHA256");

        // TLS 1.2-

        cs.add("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256");
        cs.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
        cs.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");

        cs.add("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
        cs.add("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
        cs.add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");

        cs.add("TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
        cs.add("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384");
        cs.add("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384");

        cs.add("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256");
        cs.add("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256");

        cs.add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
        cs.add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");

        cs.add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256");
        cs.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256");

        cs.add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256");
        cs.add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256");

        cs.add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256");
        cs.add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256");

        // Legacy

        cs.add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA");
        cs.add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");

        cs.add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA");
        cs.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");

        cs.add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");
        cs.add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA");

        cs.add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
        cs.add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA");

        // Deprecated (not forward secret)

        cs.add("TLS_RSA_WITH_AES_256_GCM_SHA384");
        cs.add("TLS_RSA_WITH_AES_128_GCM_SHA256");
        cs.add("TLS_RSA_WITH_AES_256_CBC_SHA256");
        cs.add("TLS_RSA_WITH_AES_128_CBC_SHA256");
        cs.add("TLS_RSA_WITH_AES_256_CBC_SHA");
        cs.add("TLS_RSA_WITH_AES_128_CBC_SHA");

        cs.retainAll(supportedCipherSuiteSet);
        cs.trimToSize();
        return Collections.unmodifiableList(cs);
    }

    private static List createDefaultCipherSuiteListFips(List defaultCipherSuiteList)
    {
        ArrayList cs = new ArrayList(defaultCipherSuiteList);
        FipsUtils.removeNonFipsCipherSuites(cs);
        cs.trimToSize();
        return Collections.unmodifiableList(cs);
    }

    private static List createDefaultProtocolList(Set supportedProtocolSet)
    {
        ArrayList ps = new ArrayList();

        ps.add("TLSv1.3");
        ps.add("TLSv1.2");
        ps.add("TLSv1.1");
        ps.add("TLSv1");

        ps.retainAll(supportedProtocolSet);
        ps.trimToSize();
        return Collections.unmodifiableList(ps);
    }

    private static List createDefaultProtocolListFips(List defaultProtocolList)
    {
        ArrayList ps = new ArrayList(defaultProtocolList);
        FipsUtils.removeNonFipsProtocols(ps);
        ps.trimToSize();
        return Collections.unmodifiableList(ps);
    }

    private static Map createSupportedCipherSuiteMap()
    {
        Map cs = new TreeMap();

        // TLS 1.3+
        addCipherSuite(cs, "TLS_AES_128_CCM_8_SHA256", CipherSuite.TLS_AES_128_CCM_8_SHA256);
        addCipherSuite(cs, "TLS_AES_128_CCM_SHA256", CipherSuite.TLS_AES_128_CCM_SHA256);
        addCipherSuite(cs, "TLS_AES_128_GCM_SHA256", CipherSuite.TLS_AES_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_AES_256_GCM_SHA384", CipherSuite.TLS_AES_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_CHACHA20_POLY1305_SHA256", CipherSuite.TLS_CHACHA20_POLY1305_SHA256);

        // TLS 1.2-
        addCipherSuite(cs, "TLS_DH_anon_WITH_AES_128_CBC_SHA", CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA);
        addCipherSuite(cs, "TLS_DH_anon_WITH_AES_128_CBC_SHA256", CipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_AES_128_GCM_SHA256", CipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_AES_256_CBC_SHA", CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA);
        addCipherSuite(cs, "TLS_DH_anon_WITH_AES_256_CBC_SHA256", CipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", CipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", CipherSuite.TLS_DH_anon_WITH_ARIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", CipherSuite.TLS_DH_anon_WITH_ARIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", CipherSuite.TLS_DH_anon_WITH_ARIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", CipherSuite.TLS_DH_anon_WITH_ARIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA);
        addCipherSuite(cs, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA);
        addCipherSuite(cs, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384);

        addCipherSuite(cs, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", CipherSuite.TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256", CipherSuite.TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", CipherSuite.TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384", CipherSuite.TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384);

        addCipherSuite(cs, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_128_CCM", CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_128_CCM_8", CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_256_CCM", CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_256_CCM_8", CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256", CipherSuite.TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384", CipherSuite.TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256);

        addCipherSuite(cs, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA);

        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", CipherSuite.TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_ECDSA_WITH_NULL_SHA", CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA);

        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", CipherSuite.TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
        addCipherSuite(cs, "TLS_ECDHE_RSA_WITH_NULL_SHA", CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA);

        addCipherSuite(cs, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_128_CBC_SHA", CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_128_CBC_SHA256", CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_128_CCM", CipherSuite.TLS_RSA_WITH_AES_128_CCM);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_128_CCM_8", CipherSuite.TLS_RSA_WITH_AES_128_CCM_8);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_128_GCM_SHA256", CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_256_CBC_SHA", CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_256_CBC_SHA256", CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_256_CCM", CipherSuite.TLS_RSA_WITH_AES_256_CCM);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_256_CCM_8", CipherSuite.TLS_RSA_WITH_AES_256_CCM_8);
        addCipherSuite(cs, "TLS_RSA_WITH_AES_256_GCM_SHA384", CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_RSA_WITH_ARIA_128_CBC_SHA256", CipherSuite.TLS_RSA_WITH_ARIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_ARIA_128_GCM_SHA256", CipherSuite.TLS_RSA_WITH_ARIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_ARIA_256_CBC_SHA384", CipherSuite.TLS_RSA_WITH_ARIA_256_CBC_SHA384);
        addCipherSuite(cs, "TLS_RSA_WITH_ARIA_256_GCM_SHA384", CipherSuite.TLS_RSA_WITH_ARIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA);
        addCipherSuite(cs, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA);
        addCipherSuite(cs, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256);
        addCipherSuite(cs, "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384);
        addCipherSuite(cs, "TLS_RSA_WITH_NULL_SHA", CipherSuite.TLS_RSA_WITH_NULL_SHA);
        addCipherSuite(cs, "TLS_RSA_WITH_NULL_SHA256", CipherSuite.TLS_RSA_WITH_NULL_SHA256);

        return Collections.unmodifiableMap(cs);
    }

    private static Map createSupportedCipherSuiteMapFips(
        Map supportedCipherSuiteMap)
    {
        final Map cs = new LinkedHashMap(supportedCipherSuiteMap);
        FipsUtils.removeNonFipsCipherSuites(cs.keySet());
        return Collections.unmodifiableMap(cs);
    }

    private static Map createSupportedProtocolMap()
    {
        Map ps = new LinkedHashMap();
        ps.put("TLSv1.3", ProtocolVersion.TLSv13);
        ps.put("TLSv1.2", ProtocolVersion.TLSv12);
        ps.put("TLSv1.1", ProtocolVersion.TLSv11);
        ps.put("TLSv1", ProtocolVersion.TLSv10);
        ps.put("SSLv3", ProtocolVersion.SSLv3);
        return Collections.unmodifiableMap(ps);
    }

    private static Map createSupportedProtocolMapFips(
        Map supportedProtocolMap)
    {
        final Map ps = new LinkedHashMap(supportedProtocolMap);
        FipsUtils.removeNonFipsProtocols(ps.keySet());
        return Collections.unmodifiableMap(ps);
    }

    private static String[] getDefaultEnabledCipherSuites(Map supportedCipherSuiteMap,
        List defaultCipherSuiteList, boolean disableDHDefaultSuites, String cipherSuitesPropertyName)
    {
        List candidates = getJdkTlsCipherSuites(cipherSuitesPropertyName, defaultCipherSuiteList);

        String[] result = new String[candidates.size()];
        int count = 0;
        for (String candidate : candidates)
        {
            CipherSuiteInfo cipherSuiteInfo = supportedCipherSuiteMap.get(candidate);
            if (null == cipherSuiteInfo)
            {
                continue;
            }
            if (disableDHDefaultSuites &&
                candidates == defaultCipherSuiteList &&
                TlsDHUtils.isDHCipherSuite(cipherSuiteInfo.getCipherSuite()))
            {
                continue;
            }
            if (!ProvAlgorithmConstraints.DEFAULT.permits(TLS_CRYPTO_PRIMITIVES_BC, candidate, null))
            {
                continue;
            }

            result[count++] = candidate;
        }
        return JsseUtils.resize(result, count);
    }

    private static String[] getDefaultEnabledCipherSuitesClient(Map supportedCipherSuiteMap,
        List defaultCipherSuiteList)
    {
        boolean disableDHDefaultSuites = PropertyUtils
            .getBooleanSystemProperty("org.bouncycastle.jsse.client.dh.disableDefaultSuites", false);

        return getDefaultEnabledCipherSuites(supportedCipherSuiteMap, defaultCipherSuiteList, disableDHDefaultSuites,
            PROPERTY_CLIENT_CIPHERSUITES);
    }

    private static String[] getDefaultEnabledCipherSuitesServer(Map supportedCipherSuiteMap,
        List defaultCipherSuiteList)
    {
        boolean disableDHDefaultSuites = PropertyUtils
            .getBooleanSystemProperty("org.bouncycastle.jsse.server.dh.disableDefaultSuites", false);

        return getDefaultEnabledCipherSuites(supportedCipherSuiteMap, defaultCipherSuiteList, disableDHDefaultSuites,
            PROPERTY_SERVER_CIPHERSUITES);
    }

    private static String[] getDefaultEnabledProtocols(Map supportedProtocolMap,
        String protocolsPropertyName, List defaultProtocolList, List specifiedProtocols)
    {
        List candidates = specifiedProtocols;
        if (null == candidates)
        {
            candidates = getJdkTlsProtocols(protocolsPropertyName, defaultProtocolList);
        }

        String[] result = new String[candidates.size()];
        int count = 0;
        for (String candidate : candidates)
        {
            if (!supportedProtocolMap.containsKey(candidate))
            {
                continue;
            }
            if (!ProvAlgorithmConstraints.DEFAULT_TLS_ONLY.permits(TLS_CRYPTO_PRIMITIVES_BC, candidate, null))
            {
                continue;
            }

            result[count++] = candidate;
        }
        return JsseUtils.resize(result, count);
    }

    private static String[] getDefaultEnabledProtocolsClient(Map supportedProtocolMap,
        List defaultProtocolList, List specifiedProtocols)
    {
        return getDefaultEnabledProtocols(supportedProtocolMap, PROPERTY_CLIENT_PROTOCOLS, defaultProtocolList,
            specifiedProtocols);
    }

    private static String[] getDefaultEnabledProtocolsServer(Map supportedProtocolMap,
        List defaultProtocolList)
    {
        return getDefaultEnabledProtocols(supportedProtocolMap, PROPERTY_SERVER_PROTOCOLS, defaultProtocolList, null);
    }

    private static List getJdkTlsCipherSuites(String cipherSuitesPropertyName,
        List defaultCipherSuiteList)
    {
        String[] cipherSuites = PropertyUtils.getStringArraySystemProperty(cipherSuitesPropertyName);
        if (null == cipherSuites)
        {
            return defaultCipherSuiteList;
        }

        ArrayList result = new ArrayList(cipherSuites.length);
        for (String cipherSuite : cipherSuites)
        {
            if (result.contains(cipherSuite))
            {
                continue;
            }
            if (!SUPPORTED_CIPHERSUITE_MAP.containsKey(cipherSuite))
            {
                LOG.warning("'" + cipherSuitesPropertyName + "' contains unsupported cipher suite: " + cipherSuite);
                continue;
            }

            result.add(cipherSuite);
        }
        if (result.isEmpty())
        {
            LOG.severe("'" + cipherSuitesPropertyName + "' contained no supported cipher suites (ignoring)");
            return defaultCipherSuiteList;
        }
        return result;
    }

    private static List getJdkTlsProtocols(String protocolsPropertyName, List defaultProtocolList)
    {
        String[] protocols = PropertyUtils.getStringArraySystemProperty(protocolsPropertyName);
        if (null == protocols)
        {
            return defaultProtocolList;
        }

        ArrayList result = new ArrayList(protocols.length);
        for (String protocol : protocols)
        {
            if (result.contains(protocol))
            {
                continue;
            }
            if (!SUPPORTED_PROTOCOL_MAP.containsKey(protocol))
            {
                LOG.warning("'" + protocolsPropertyName + "' contains unsupported protocol: " + protocol);
                continue;
            }

            result.add(protocol);
        }
        if (result.isEmpty())
        {
            LOG.severe("'" + protocolsPropertyName + "' contained no supported protocols (ignoring)");
            return defaultProtocolList;
        }
        return result;
    }

    private static String[] getArray(Collection c)
    {
        return c.toArray(new String[c.size()]);
    }

    private static String[] getKeysArray(Map m)
    {
        return getArray(m.keySet());
    }

    static CipherSuiteInfo getCipherSuiteInfo(String cipherSuiteName)
    {
        return SUPPORTED_CIPHERSUITE_MAP.get(cipherSuiteName);
    }

    static String getCipherSuiteName(int cipherSuite)
    {
        // TODO[jsse] Place into "understood" cipher suite map
        if (CipherSuite.TLS_NULL_WITH_NULL_NULL == cipherSuite)
        {
            return "SSL_NULL_WITH_NULL_NULL";
        }
        if (TlsUtils.isValidUint16(cipherSuite))
        {
            // TODO[jsse] Add CipherSuiteInfo index by 'int'
            for (CipherSuiteInfo cipherSuiteInfo : SUPPORTED_CIPHERSUITE_MAP.values())
            {
                if (cipherSuiteInfo.getCipherSuite() == cipherSuite)
                {
                    return cipherSuiteInfo.getName();
                }
            }
        }
        return null;
    }

    static KeyManager[] getDefaultKeyManagers() throws Exception
    {
        KeyStoreConfig keyStoreConfig = ProvKeyManagerFactorySpi.getDefaultKeyStore();
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keyStoreConfig.keyStore, keyStoreConfig.password);
        return kmf.getKeyManagers();
    }

    static TrustManager[] getDefaultTrustManagers() throws Exception
    {
        KeyStore trustStore = ProvTrustManagerFactorySpi.getDefaultTrustStore();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);
        return tmf.getTrustManagers();
    }

    static ProtocolVersion getProtocolVersion(String protocolVersionName)
    {
        return SUPPORTED_PROTOCOL_MAP.get(protocolVersionName);
    }

    static String getProtocolVersionName(ProtocolVersion protocolVersion)
    {
        if (null != protocolVersion)
        {
            for (Map.Entry entry : SUPPORTED_PROTOCOL_MAP.entrySet())
            {
                if (entry.getValue().equals(protocolVersion))
                {
                    return entry.getKey();
                }
            }
        }
        return "NONE";
    }

    protected final boolean isInFipsMode;
    protected final JcaTlsCryptoProvider cryptoProvider;

    protected final Map supportedCipherSuites;
    protected final Map supportedProtocols;
    protected final String[] defaultCipherSuitesClient;
    protected final String[] defaultCipherSuitesServer;
    protected final String[] defaultProtocolsClient;
    protected final String[] defaultProtocolsServer;

    private ContextData contextData = null;

    ProvSSLContextSpi(boolean isInFipsMode, JcaTlsCryptoProvider cryptoProvider, List specifiedProtocolsClient)
    {
        this.isInFipsMode = isInFipsMode;
        this.cryptoProvider = cryptoProvider;

        this.supportedCipherSuites = isInFipsMode ? SUPPORTED_CIPHERSUITE_MAP_FIPS : SUPPORTED_CIPHERSUITE_MAP;
        this.supportedProtocols = isInFipsMode ? SUPPORTED_PROTOCOL_MAP_FIPS : SUPPORTED_PROTOCOL_MAP;

        List defaultCipherSuiteList = isInFipsMode ? DEFAULT_CIPHERSUITE_LIST_FIPS : DEFAULT_CIPHERSUITE_LIST;
        List defaultProtocolList = isInFipsMode ? DEFAULT_PROTOCOL_LIST_FIPS : DEFAULT_PROTOCOL_LIST;

        this.defaultCipherSuitesClient = getDefaultEnabledCipherSuitesClient(supportedCipherSuites,
            defaultCipherSuiteList);
        this.defaultCipherSuitesServer = getDefaultEnabledCipherSuitesServer(supportedCipherSuites,
            defaultCipherSuiteList);

        this.defaultProtocolsClient = getDefaultEnabledProtocolsClient(supportedProtocols, defaultProtocolList,
            specifiedProtocolsClient);
        this.defaultProtocolsServer = getDefaultEnabledProtocolsServer(supportedProtocols, defaultProtocolList);
    }

    int[] getActiveCipherSuites(JcaTlsCrypto crypto, ProvSSLParameters sslParameters,
        ProtocolVersion[] activeProtocolVersions)
    {
        String[] enabledCipherSuites = sslParameters.getCipherSuitesArray();
        BCAlgorithmConstraints algorithmConstraints = sslParameters.getAlgorithmConstraints();

        ProtocolVersion latest = ProtocolVersion.getLatestTLS(activeProtocolVersions);
        ProtocolVersion earliest = ProtocolVersion.getEarliestTLS(activeProtocolVersions);

        boolean post13Active = TlsUtils.isTLSv13(latest);
        boolean pre13Active = !TlsUtils.isTLSv13(earliest);

        int[] candidates = new int[enabledCipherSuites.length];

        int count = 0;
        for (String enabledCipherSuite : enabledCipherSuites)
        {
            CipherSuiteInfo candidate = supportedCipherSuites.get(enabledCipherSuite);
            if (null == candidate)
            {
                continue;
            }
            if (candidate.isTLSv13())
            {
                if (!post13Active)
                {
                    continue;
                }
            }
            else
            {
                if (!pre13Active)
                {
                    continue;
                }
            }
            if (!algorithmConstraints.permits(TLS_CRYPTO_PRIMITIVES_BC, enabledCipherSuite, null))
            {
                continue;
            }

            /*
             * TODO[jsse] SunJSSE also checks that the cipher suite is usable for at least one of
             * the active protocol versions. Also, if the cipher suite involves a key exchange,
             * there must be at least one suitable NamedGroup available.
             */

            candidates[count++] = candidate.getCipherSuite();
        }

        /*
         * TODO Move cipher suite management into CipherSuiteInfo (PerConnection/PerContext pattern
         * like NamedGroupInfo) to avoid unnecessary repetition of these sorts of checks.
         */
        int[] result = TlsUtils.getSupportedCipherSuites(crypto, candidates, 0, count);

        if (result.length < 1)
        {
            // TODO[jsse] Refactor so that this can be an SSLHandshakeException?
            throw new IllegalStateException("No usable cipher suites enabled");
        }

        return result;
    }

    ProtocolVersion[] getActiveProtocolVersions(ProvSSLParameters sslParameters)
    {
//        String[] enabledCipherSuites = sslParameters.getCipherSuitesArray();
        String[] enabledProtocols = sslParameters.getProtocolsArray();
        BCAlgorithmConstraints algorithmConstraints = sslParameters.getAlgorithmConstraints();

        SortedSet result = new TreeSet(new Comparator()
        {
            public int compare(ProtocolVersion o1, ProtocolVersion o2)
            {
                return o1.isLaterVersionOf(o2) ? -1 : o2.isLaterVersionOf(o1) ? 1 : 0;
            }
        });

        for (String enabledProtocol : enabledProtocols)
        {
            ProtocolVersion candidate = supportedProtocols.get(enabledProtocol);
            if (null == candidate)
            {
                continue;
            }
            if (!algorithmConstraints.permits(TLS_CRYPTO_PRIMITIVES_BC, enabledProtocol, null))
            {
                continue;
            }

            /*
             * TODO[jsse] SunJSSE also checks that there is at least one "activatable" cipher suite
             * that could be used for this protocol version.
             */

            result.add(candidate);
        }

        if (result.isEmpty())
        {
            // TODO[jsse] Refactor so that this can be an SSLHandshakeException?
            throw new IllegalStateException("No usable protocols enabled");
        }

        return result.toArray(new ProtocolVersion[result.size()]);
    }

    String[] getDefaultCipherSuites(boolean isClient)
    {
        return implGetDefaultCipherSuites(isClient).clone();
    }

    String[] getDefaultProtocols(boolean isClient)
    {
        return implGetDefaultProtocols(isClient).clone();
    }

    ProvSSLParameters getDefaultSSLParameters(boolean isClient)
    {
        return new ProvSSLParameters(this, implGetDefaultCipherSuites(isClient), implGetDefaultProtocols(isClient));
    }

    String[] getSupportedCipherSuites()
    {
        return getKeysArray(supportedCipherSuites);
    }

    String[] getSupportedCipherSuites(String[] cipherSuites)
    {
        if (null == cipherSuites)
        {
            throw new NullPointerException("'cipherSuites' cannot be null");
        }

        ArrayList result = new ArrayList(cipherSuites.length);
        for (String cipherSuite : cipherSuites)
        {
            if (TlsUtils.isNullOrEmpty(cipherSuite))
            {
                throw new IllegalArgumentException("'cipherSuites' cannot contain null or empty string elements");
            }

            if (supportedCipherSuites.containsKey(cipherSuite))
            {
                result.add(cipherSuite);
            }
        }

        // NOTE: This method must always return a copy, so no fast path when all supported
        return getArray(result);
    }

    String[] getSupportedProtocols()
    {
        return getKeysArray(supportedProtocols);
    }

    ProvSSLParameters getSupportedSSLParameters(boolean isClient)
    {
        return new ProvSSLParameters(this, getSupportedCipherSuites(), getSupportedProtocols());
    }

    boolean isFips()
    {
        return isInFipsMode;
    }

    boolean isSupportedProtocols(String[] protocols)
    {
        if (protocols == null)
        {
            return false;
        }
        for (String protocol : protocols)
        {
            if (protocol == null || !supportedProtocols.containsKey(protocol))
            {
                return false;
            }
        }
        return true;
    }

    void updateDefaultSSLParameters(ProvSSLParameters sslParameters, boolean isClient)
    {
        if (sslParameters.getCipherSuitesArray() == implGetDefaultCipherSuites(!isClient))
        {
            sslParameters.setCipherSuitesArray(implGetDefaultCipherSuites(isClient));
        }
        if (sslParameters.getProtocolsArray() == implGetDefaultProtocols(!isClient))
        {
            sslParameters.setProtocolsArray(implGetDefaultProtocols(isClient));
        }
    }

    String validateNegotiatedCipherSuite(ProvSSLParameters sslParameters, int cipherSuite)
    {
        // NOTE: The redundancy among these various checks is intentional
        String name = getCipherSuiteName(cipherSuite);
        if (null == name
            || !JsseUtils.contains(sslParameters.getCipherSuitesArray(), name)
            || !sslParameters.getAlgorithmConstraints().permits(TLS_CRYPTO_PRIMITIVES_BC, name, null)
            || !supportedCipherSuites.containsKey(name)
            || (isInFipsMode && !FipsUtils.isFipsCipherSuite(name)))
        {
            throw new IllegalStateException("SSL connection negotiated unsupported ciphersuite: " + cipherSuite);
        }
        return name;
    }

    String validateNegotiatedProtocol(ProvSSLParameters sslParameters, ProtocolVersion protocol)
    {
        // NOTE: The redundancy among these various checks is intentional
        String name = getProtocolVersionName(protocol);
        if (null == name
            || !JsseUtils.contains(sslParameters.getProtocolsArray(), name)
            || !sslParameters.getAlgorithmConstraints().permits(TLS_CRYPTO_PRIMITIVES_BC, name, null)
            || !supportedProtocols.containsKey(name)
            || (isInFipsMode && !FipsUtils.isFipsProtocol(name)))
        {
            throw new IllegalStateException("SSL connection negotiated unsupported protocol: " + protocol);
        }
        return name;
    }

    @Override
    protected synchronized SSLEngine engineCreateSSLEngine()
    {
        return SSLEngineUtil.create(getContextData());
    }

    @Override
    protected synchronized SSLEngine engineCreateSSLEngine(String host, int port)
    {
        return SSLEngineUtil.create(getContextData(), host, port);
    }

    @Override
    protected synchronized SSLSessionContext engineGetClientSessionContext()
    {
        return getContextData().getClientSessionContext();
    }

    @Override
    protected synchronized SSLSessionContext engineGetServerSessionContext()
    {
        return getContextData().getServerSessionContext();
    }

    @Override
    protected SSLServerSocketFactory engineGetServerSocketFactory()
    {
        return new ProvSSLServerSocketFactory(getContextData());
    }

    @Override
    protected SSLSocketFactory engineGetSocketFactory()
    {
        return new ProvSSLSocketFactory(getContextData());
    }

    // An SSLContextSpi method from JDK 6
    protected SSLParameters engineGetDefaultSSLParameters()
    {
        // Fail if uninitialized
        getContextData();

        // Implicitly for a client socket
        return SSLParametersUtil.getSSLParameters(getDefaultSSLParameters(true));
    }

    // An SSLContextSpi method from JDK 6
    protected SSLParameters engineGetSupportedSSLParameters()
    {
        // Fail if uninitialized
        getContextData();

        // Implicitly for a client socket
        return SSLParametersUtil.getSSLParameters(getSupportedSSLParameters(true));
    }

    @Override
    protected synchronized void engineInit(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) throws KeyManagementException
    {
        this.contextData = null;

        JcaTlsCrypto crypto = cryptoProvider.create(sr);
        JcaJceHelper helper = crypto.getHelper();

        BCX509ExtendedKeyManager x509KeyManager = selectX509KeyManager(helper, kms);
        BCX509ExtendedTrustManager x509TrustManager = selectX509TrustManager(helper, tms);

        // Trigger (possibly expensive) RNG initialization here to avoid timeout in an actual handshake
        crypto.getSecureRandom().nextInt();

        this.contextData = new ContextData(this, crypto, x509KeyManager, x509TrustManager);
    }

    protected synchronized ContextData getContextData()
    {
        if (null == contextData)
        {
            throw new IllegalStateException("SSLContext has not been initialized.");
        }

        return contextData;
    }

    protected BCX509ExtendedKeyManager selectX509KeyManager(JcaJceHelper helper, KeyManager[] kms)
        throws KeyManagementException
    {
        if (kms != null)
        {
            for (KeyManager km : kms)
            {
                if (km instanceof X509KeyManager)
                {
                    return X509KeyManagerUtil.importX509KeyManager(helper, (X509KeyManager)km);
                }
            }
        }
        return DummyX509KeyManager.INSTANCE;
    }

    protected BCX509ExtendedTrustManager selectX509TrustManager(JcaJceHelper helper, TrustManager[] tms)
        throws KeyManagementException
    {
        if (tms == null)
        {
            try
            {
                /*
                 * "[...] the installed security providers will be searched for the highest priority
                 * implementation of the appropriate factory."
                 */
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                tmf.init((KeyStore)null);
                tms = tmf.getTrustManagers();
            }
            catch (Exception e)
            {
                LOG.log(Level.WARNING, "Failed to load default trust managers", e);
            }
        }
        if (tms != null)
        {
            for (TrustManager tm : tms)
            {
                if (tm instanceof X509TrustManager)
                {
                    return X509TrustManagerUtil.importX509TrustManager(isInFipsMode, helper, (X509TrustManager)tm);
                }
            }
        }
        return DummyX509TrustManager.INSTANCE;
    }

    private String[] implGetDefaultCipherSuites(boolean isClient)
    {
        return isClient ? defaultCipherSuitesClient : defaultCipherSuitesServer;
    }

    private String[] implGetDefaultProtocols(boolean isClient)
    {
        return isClient ? defaultProtocolsClient : defaultProtocolsServer;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy