/*
 * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.security.ssl;

import java.net.Socket;

import java.io.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;

import javax.net.ssl.*;

import sun.security.provider.certpath.AlgorithmChecker;
import sun.security.action.GetPropertyAction;
import sun.security.validator.Validator;

public abstract class SSLContextImpl extends SSLContextSpi {

    private static final Debug debug = Debug.getInstance("ssl");

    private final EphemeralKeyManager ephemeralKeyManager;
    private final SSLSessionContextImpl clientCache;
    private final SSLSessionContextImpl serverCache;

    private boolean isInitialized;

    private X509ExtendedKeyManager keyManager;
    private X509TrustManager trustManager;
    private SecureRandom secureRandom;

    // DTLS cookie exchange manager
    private volatile HelloCookieManager helloCookieManager;

    private final boolean clientEnableStapling = Debug.getBooleanProperty(
            "jdk.tls.client.enableStatusRequestExtension", true);
    private final boolean serverEnableStapling = Debug.getBooleanProperty(
            "jdk.tls.server.enableStatusRequestExtension", false);
    private final static Collection<CipherSuite> clientCustomizedCipherSuites =
            getCustomizedCipherSuites("jdk.tls.client.cipherSuites");
    private final static Collection<CipherSuite> serverCustomizedCipherSuites =
            getCustomizedCipherSuites("jdk.tls.server.cipherSuites");

    private volatile StatusResponseManager statusResponseManager;

    SSLContextImpl() {
        ephemeralKeyManager = new EphemeralKeyManager();
        clientCache = new SSLSessionContextImpl();
        serverCache = new SSLSessionContextImpl();
    }

    @Override
    protected void engineInit(KeyManager[] km, TrustManager[] tm,
                                SecureRandom sr) throws KeyManagementException {
        isInitialized = false;
        keyManager = chooseKeyManager(km);

        if (tm == null) {
            try {
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                        TrustManagerFactory.getDefaultAlgorithm());
                tmf.init((KeyStore)null);
                tm = tmf.getTrustManagers();
            } catch (Exception e) {
                // eat
            }
        }
        trustManager = chooseTrustManager(tm);

        if (sr == null) {
            secureRandom = JsseJce.getSecureRandom();
        } else {
            if (SunJSSE.isFIPS() &&
                        (sr.getProvider() != SunJSSE.cryptoProvider)) {
                throw new KeyManagementException
                    ("FIPS mode: SecureRandom must be from provider "
                    + SunJSSE.cryptoProvider.getName());
            }
            secureRandom = sr;
        }

        /*
         * The initial delay of seeding the random number generator
         * could be long enough to cause the initial handshake on our
         * first connection to timeout and fail. Make sure it is
         * primed and ready by getting some initial output from it.
         */
        if (debug != null && Debug.isOn("sslctx")) {
            System.out.println("trigger seeding of SecureRandom");
        }
        secureRandom.nextInt();
        if (debug != null && Debug.isOn("sslctx")) {
            System.out.println("done seeding SecureRandom");
        }

        isInitialized = true;
    }

    private X509TrustManager chooseTrustManager(TrustManager[] tm)
            throws KeyManagementException {
        // We only use the first instance of X509TrustManager passed to us.
        for (int i = 0; tm != null && i < tm.length; i++) {
            if (tm[i] instanceof X509TrustManager) {
                if (SunJSSE.isFIPS() &&
                        !(tm[i] instanceof X509TrustManagerImpl)) {
                    throw new KeyManagementException
                        ("FIPS mode: only SunJSSE TrustManagers may be used");
                }

                if (tm[i] instanceof X509ExtendedTrustManager) {
                    return (X509TrustManager)tm[i];
                } else {
                    return new AbstractTrustManagerWrapper(
                                        (X509TrustManager)tm[i]);
                }
            }
        }

        // nothing found, return a dummy X509TrustManager.
        return DummyX509TrustManager.INSTANCE;
    }

    private X509ExtendedKeyManager chooseKeyManager(KeyManager[] kms)
            throws KeyManagementException {
        for (int i = 0; kms != null && i < kms.length; i++) {
            KeyManager km = kms[i];
            if (!(km instanceof X509KeyManager)) {
                continue;
            }
            if (SunJSSE.isFIPS()) {
                // In FIPS mode, require that one of SunJSSE's own keymanagers
                // is used. Otherwise, we cannot be sure that only keys from
                // the FIPS token are used.
                if ((km instanceof X509KeyManagerImpl)
                            || (km instanceof SunX509KeyManagerImpl)) {
                    return (X509ExtendedKeyManager)km;
                } else {
                    // throw exception, we don't want to silently use the
                    // dummy keymanager without telling the user.
                    throw new KeyManagementException
                        ("FIPS mode: only SunJSSE KeyManagers may be used");
                }
            }
            if (km instanceof X509ExtendedKeyManager) {
                return (X509ExtendedKeyManager)km;
            }
            if (debug != null && Debug.isOn("sslctx")) {
                System.out.println(
                    "X509KeyManager passed to " +
                    "SSLContext.init():  need an " +
                    "X509ExtendedKeyManager for SSLEngine use");
            }
            return new AbstractKeyManagerWrapper((X509KeyManager)km);
        }

        // nothing found, return a dummy X509ExtendedKeyManager
        return DummyX509KeyManager.INSTANCE;
    }

    abstract SSLEngine createSSLEngineImpl();
    abstract SSLEngine createSSLEngineImpl(String host, int port);

    @Override
    protected SSLEngine engineCreateSSLEngine() {
        if (!isInitialized) {
            throw new IllegalStateException("SSLContext is not initialized");
        }
        return createSSLEngineImpl();
    }

    @Override
    protected SSLEngine engineCreateSSLEngine(String host, int port) {
        if (!isInitialized) {
            throw new IllegalStateException("SSLContext is not initialized");
        }
        return createSSLEngineImpl(host, port);
    }

    @Override
    protected SSLSocketFactory engineGetSocketFactory() {
        if (!isInitialized) {
            throw new IllegalStateException("SSLContext is not initialized");
        }
       return new SSLSocketFactoryImpl(this);
    }

    @Override
    protected SSLServerSocketFactory engineGetServerSocketFactory() {
        if (!isInitialized) {
            throw new IllegalStateException("SSLContext is not initialized");
        }
        return new SSLServerSocketFactoryImpl(this);
    }

    @Override
    protected SSLSessionContext engineGetClientSessionContext() {
        return clientCache;
    }

    @Override
    protected SSLSessionContext engineGetServerSessionContext() {
        return serverCache;
    }

    SecureRandom getSecureRandom() {
        return secureRandom;
    }

    X509ExtendedKeyManager getX509KeyManager() {
        return keyManager;
    }

    X509TrustManager getX509TrustManager() {
        return trustManager;
    }

    EphemeralKeyManager getEphemeralKeyManager() {
        return ephemeralKeyManager;
    }

    // Used for DTLS in server mode only, see ServerHandshaker.
    HelloCookieManager getHelloCookieManager() {
        if (!isInitialized) {
            throw new IllegalStateException("SSLContext is not initialized");
        }

        if (helloCookieManager != null) {
            return helloCookieManager;
        }

        synchronized (this) {
            if (helloCookieManager == null) {
                helloCookieManager = getHelloCookieManager(secureRandom);
            }
        }

        return helloCookieManager;
    }

    HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
        throw new UnsupportedOperationException(
                "Cookie exchange applies to DTLS only");
    }

    StatusResponseManager getStatusResponseManager() {
        if (serverEnableStapling && statusResponseManager == null) {
            synchronized (this) {
                if (statusResponseManager == null) {
                    if (debug != null && Debug.isOn("sslctx")) {
                        System.out.println(
                                "Initializing StatusResponseManager");
                    }
                    statusResponseManager = new StatusResponseManager();
                }
            }
        }

        return statusResponseManager;
    }

    // Get supported ProtocolList.
    abstract ProtocolList getSuportedProtocolList();

    // Get default ProtocolList for server mode.
    abstract ProtocolList getServerDefaultProtocolList();

    // Get default ProtocolList for client mode.
    abstract ProtocolList getClientDefaultProtocolList();

    // Get supported CipherSuiteList.
    abstract CipherSuiteList getSupportedCipherSuiteList();

    // Get default CipherSuiteList for server mode.
    abstract CipherSuiteList getServerDefaultCipherSuiteList();

    // Get default CipherSuiteList for client mode.
    abstract CipherSuiteList getClientDefaultCipherSuiteList();

    // Get default ProtocolList.
    ProtocolList getDefaultProtocolList(boolean roleIsServer) {
        return roleIsServer ? getServerDefaultProtocolList()
                            : getClientDefaultProtocolList();
    }

    // Get default CipherSuiteList.
    CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) {
        return roleIsServer ? getServerDefaultCipherSuiteList()
                            : getClientDefaultCipherSuiteList();
    }

    
Return whether a protocol list is the original default enabled protocols. See: SSLSocket/SSLEngine.setEnabledProtocols()
/** * Return whether a protocol list is the original default enabled * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */
boolean isDefaultProtocolList(ProtocolList protocols) { return (protocols == getServerDefaultProtocolList()) || (protocols == getClientDefaultProtocolList()); }
Return whether a protocol list is the original default enabled protocols. See: SSLSocket/SSLEngine.setEnabledProtocols()
/** * Return whether a protocol list is the original default enabled * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */
boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) { return (cipherSuites == getServerDefaultCipherSuiteList()) || (cipherSuites == getClientDefaultCipherSuiteList()); }
Return whether client or server side stapling has been enabled for this SSLContextImpl
Params:
  • isClient – true if the caller is operating in a client side role, false if acting as a server.
Returns:true if stapling has been enabled for the specified role, false otherwise.
/** * Return whether client or server side stapling has been enabled * for this SSLContextImpl * @param isClient true if the caller is operating in a client side role, * false if acting as a server. * @return true if stapling has been enabled for the specified role, false * otherwise. */
boolean isStaplingEnabled(boolean isClient) { return isClient ? clientEnableStapling : serverEnableStapling; } /* * Return the list of all available CipherSuites that are supported * using currently installed providers. */ private static CipherSuiteList getApplicableSupportedCipherSuiteList( ProtocolList protocols) { return getApplicableCipherSuiteList( CipherSuite.allowedCipherSuites(), protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY); } /* * Return the list of all available CipherSuites that are default enabled * in client or server side. */ private static CipherSuiteList getApplicableEnabledCipherSuiteList( ProtocolList protocols, boolean isClient) { if (isClient) { if (!clientCustomizedCipherSuites.isEmpty()) { return getApplicableCipherSuiteList( clientCustomizedCipherSuites, protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY); } } else { if (!serverCustomizedCipherSuites.isEmpty()) { return getApplicableCipherSuiteList( serverCustomizedCipherSuites, protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY); } } return getApplicableCipherSuiteList( CipherSuite.allowedCipherSuites(), protocols, CipherSuite.DEFAULT_SUITES_PRIORITY); } /* * Return the list of available CipherSuites which are applicable to * the specified protocols. */ private static CipherSuiteList getApplicableCipherSuiteList( Collection<CipherSuite> allowedCipherSuites, ProtocolList protocols, int minPriority) { TreeSet<CipherSuite> suites = new TreeSet<>(); if (!(protocols.collection().isEmpty()) && protocols.min.v != ProtocolVersion.NONE.v) { for (CipherSuite suite : allowedCipherSuites) { if (!suite.allowed || suite.priority < minPriority) { continue; } if (suite.isAvailable() && !protocols.min.obsoletes(suite) && protocols.max.supports(suite)) { if (SSLAlgorithmConstraints.DEFAULT.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); } else { if (debug != null && Debug.isOn("sslctx") && Debug.isOn("verbose")) { System.out.println( "Ignoring disabled cipher suite: " + suite.name); } } } else if (debug != null && Debug.isOn("sslctx") && Debug.isOn("verbose")) { if (protocols.min.obsoletes(suite)) { System.out.println( "Ignoring obsoleted cipher suite: " + suite); } else if (!protocols.max.supports(suite)) { System.out.println( "Ignoring unsupported cipher suite: " + suite); } else { System.out.println( "Ignoring unavailable cipher suite: " + suite); } } } } return new CipherSuiteList(suites); } /* * Get the customized cipher suites specified by the given system property. */ private static Collection<CipherSuite> getCustomizedCipherSuites( String propertyName) { String property = GetPropertyAction.privilegedGetProperty(propertyName); if (debug != null && Debug.isOn("sslctx")) { System.out.println( "System property " + propertyName + " is set to '" + property + "'"); } if (property != null && property.length() != 0) { // remove double quote marks from beginning/end of the property if (property.length() > 1 && property.charAt(0) == '"' && property.charAt(property.length() - 1) == '"') { property = property.substring(1, property.length() - 1); } } if (property != null && property.length() != 0) { String[] cipherSuiteNames = property.split(","); Collection<CipherSuite> cipherSuites = new ArrayList<>(cipherSuiteNames.length); for (int i = 0; i < cipherSuiteNames.length; i++) { cipherSuiteNames[i] = cipherSuiteNames[i].trim(); if (cipherSuiteNames[i].isEmpty()) { continue; } CipherSuite suite; try { suite = CipherSuite.valueOf(cipherSuiteNames[i]); } catch (IllegalArgumentException iae) { if (debug != null && Debug.isOn("sslctx")) { System.out.println( "Unknown or unsupported cipher suite name: " + cipherSuiteNames[i]); } continue; } if (suite.isAvailable()) { cipherSuites.add(suite); } else { if (debug != null && Debug.isOn("sslctx")) { System.out.println( "The current installed providers do not " + "support cipher suite: " + cipherSuiteNames[i]); } } } return cipherSuites; } return Collections.emptyList(); } private static String[] getAvailableProtocols( ProtocolVersion[] protocolCandidates) { List<String> availableProtocols = Collections.<String>emptyList(); if (protocolCandidates != null && protocolCandidates.length != 0) { availableProtocols = new ArrayList<>(protocolCandidates.length); for (ProtocolVersion p : protocolCandidates) { if (ProtocolVersion.availableProtocols.contains(p)) { availableProtocols.add(p.name); } } } return availableProtocols.toArray(new String[0]); } /* * The SSLContext implementation for SSL/(D)TLS algorithm * * SSL/TLS protocols specify the forward compatibility and version * roll-back attack protections, however, a number of SSL/TLS server * vendors did not implement these aspects properly, and some current * SSL/TLS servers may refuse to talk to a TLS 1.1 or later client. * * Considering above interoperability issues, SunJSSE will not set * TLS 1.1 and TLS 1.2 as the enabled protocols for client by default. * * For SSL/TLS servers, there is no such interoperability issues as * SSL/TLS clients. In SunJSSE, TLS 1.1 or later version will be the * enabled protocols for server by default. * * We may change the behavior when popular TLS/SSL vendors support TLS * forward compatibility properly. * * SSLv2Hello is no longer necessary. This interoperability option was * put in place in the late 90's when SSLv3/TLS1.0 were relatively new * and there were a fair number of SSLv2-only servers deployed. Because * of the security issues in SSLv2, it is rarely (if ever) used, as * deployments should now be using SSLv3 and TLSv1. * * Considering the issues of SSLv2Hello, we should not enable SSLv2Hello * by default. Applications still can use it by enabling SSLv2Hello with * the series of setEnabledProtocols APIs. */ /* * The base abstract SSLContext implementation for the Transport Layer * Security (TLS) protocols. * * This abstract class encapsulates supported and the default server * SSL/TLS parameters. * * @see SSLContext */ private abstract static class AbstractTLSContext extends SSLContextImpl { private static final ProtocolList supportedProtocolList; private static final ProtocolList serverDefaultProtocolList; private static final CipherSuiteList supportedCipherSuiteList; private static final CipherSuiteList serverDefaultCipherSuiteList; static { if (SunJSSE.isFIPS()) { supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.TLS10.name, ProtocolVersion.TLS11.name, ProtocolVersion.TLS12.name }); serverDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 })); } else { supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.SSL20Hello.name, ProtocolVersion.SSL30.name, ProtocolVersion.TLS10.name, ProtocolVersion.TLS11.name, ProtocolVersion.TLS12.name }); serverDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL20Hello, ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 })); } supportedCipherSuiteList = getApplicableSupportedCipherSuiteList( supportedProtocolList); serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( serverDefaultProtocolList, false); } @Override ProtocolList getSuportedProtocolList() { return supportedProtocolList; } @Override CipherSuiteList getSupportedCipherSuiteList() { return supportedCipherSuiteList; } @Override ProtocolList getServerDefaultProtocolList() { return serverDefaultProtocolList; } @Override CipherSuiteList getServerDefaultCipherSuiteList() { return serverDefaultCipherSuiteList; } @Override SSLEngine createSSLEngineImpl() { return new SSLEngineImpl(this, false); } @Override SSLEngine createSSLEngineImpl(String host, int port) { return new SSLEngineImpl(this, host, port, false); } } /* * The SSLContext implementation for SSLv3 and TLS10 algorithm * * @see SSLContext */ public static final class TLS10Context extends AbstractTLSContext { private static final ProtocolList clientDefaultProtocolList; private static final CipherSuiteList clientDefaultCipherSuiteList; static { if (SunJSSE.isFIPS()) { clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10 })); } else { clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10 })); } clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( clientDefaultProtocolList, true); } @Override ProtocolList getClientDefaultProtocolList() { return clientDefaultProtocolList; } @Override CipherSuiteList getClientDefaultCipherSuiteList() { return clientDefaultCipherSuiteList; } } /* * The SSLContext implementation for TLS11 algorithm * * @see SSLContext */ public static final class TLS11Context extends AbstractTLSContext { private static final ProtocolList clientDefaultProtocolList; private static final CipherSuiteList clientDefaultCipherSuiteList; static { if (SunJSSE.isFIPS()) { clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11 })); } else { clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11 })); } clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( clientDefaultProtocolList, true); } @Override ProtocolList getClientDefaultProtocolList() { return clientDefaultProtocolList; } @Override CipherSuiteList getClientDefaultCipherSuiteList() { return clientDefaultCipherSuiteList; } } /* * The SSLContext implementation for TLS12 algorithm * * @see SSLContext */ public static final class TLS12Context extends AbstractTLSContext { private static final ProtocolList clientDefaultProtocolList; private static final CipherSuiteList clientDefaultCipherSuiteList; static { if (SunJSSE.isFIPS()) { clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 })); } else { clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 })); } clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( clientDefaultProtocolList, true); } @Override ProtocolList getClientDefaultProtocolList() { return clientDefaultProtocolList; } @Override CipherSuiteList getClientDefaultCipherSuiteList() { return clientDefaultCipherSuiteList; } } /* * The interface for the customized SSL/(D)TLS SSLContext. * * @see SSLContext */ private static class CustomizedSSLProtocols { private static final String PROPERTY_NAME = "jdk.tls.client.protocols"; static IllegalArgumentException reservedException = null; static ArrayList<ProtocolVersion> customizedProtocols = new ArrayList<>(); // Don't want a java.lang.LinkageError for illegal system property. // // Please don't throw exception in this static block. Otherwise, // java.lang.LinkageError may be thrown during the instantiation of // the provider service. Instead, please handle the initialization // exception in the caller's constructor. static { String property = GetPropertyAction .privilegedGetProperty(PROPERTY_NAME); if (property != null && property.length() != 0) { // remove double quote marks from beginning/end of the property if (property.length() > 1 && property.charAt(0) == '"' && property.charAt(property.length() - 1) == '"') { property = property.substring(1, property.length() - 1); } } if (property != null && property.length() != 0) { String[] protocols = property.split(","); for (int i = 0; i < protocols.length; i++) { protocols[i] = protocols[i].trim(); // Is it a supported protocol name? try { ProtocolVersion pro = ProtocolVersion.valueOf(protocols[i]); if (SunJSSE.isFIPS() && ((pro.v == ProtocolVersion.SSL30.v) || (pro.v == ProtocolVersion.SSL20Hello.v))) { reservedException = new IllegalArgumentException( PROPERTY_NAME + ": " + pro + " is not FIPS compliant"); break; } // ignore duplicated protocols if (!customizedProtocols.contains(pro)) { customizedProtocols.add(pro); } } catch (IllegalArgumentException iae) { reservedException = new IllegalArgumentException( PROPERTY_NAME + ": " + protocols[i] + " is not a standard SSL protocol name", iae); } } } } } /* * The SSLContext implementation for customized TLS protocols * * @see SSLContext */ private static class CustomizedTLSContext extends AbstractTLSContext { private static final ProtocolList clientDefaultProtocolList; private static final CipherSuiteList clientDefaultCipherSuiteList; private static IllegalArgumentException reservedException = null; // Don't want a java.lang.LinkageError for illegal system property. // // Please don't throw exception in this static block. Otherwise, // java.lang.LinkageError may be thrown during the instantiation of // the provider service. Instead, let's handle the initialization // exception in constructor. static { reservedException = CustomizedSSLProtocols.reservedException; if (reservedException == null) { ArrayList<ProtocolVersion> customizedTLSProtocols = new ArrayList<>(); for (ProtocolVersion protocol : CustomizedSSLProtocols.customizedProtocols) { if (!protocol.isDTLSProtocol()) { customizedTLSProtocols.add(protocol); } } // candidates for available protocols ProtocolVersion[] candidates; if (customizedTLSProtocols.isEmpty()) { // Use the default enabled client protocols if no // customized TLS protocols. if (SunJSSE.isFIPS()) { candidates = new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } else { candidates = new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } } else { // Use the customized TLS protocols. candidates = new ProtocolVersion[customizedTLSProtocols.size()]; candidates = customizedTLSProtocols.toArray(candidates); } clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(candidates)); clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( clientDefaultProtocolList, true); } else { clientDefaultProtocolList = null; // unlikely to be used clientDefaultCipherSuiteList = null; // unlikely to be used } } protected CustomizedTLSContext() { if (reservedException != null) { throw reservedException; } } @Override ProtocolList getClientDefaultProtocolList() { return clientDefaultProtocolList; } @Override CipherSuiteList getClientDefaultCipherSuiteList() { return clientDefaultCipherSuiteList; } } /* * The SSLContext implementation for default "TLS" algorithm * * @see SSLContext */ public static final class TLSContext extends CustomizedTLSContext { // use the default constructor and methods } // lazy initialization holder class idiom for static default parameters // // See Effective Java Second Edition: Item 71. private static final class DefaultManagersHolder { private static final String NONE = "NONE"; private static final String P11KEYSTORE = "PKCS11"; private static final TrustManager[] trustManagers; private static final KeyManager[] keyManagers; static Exception reservedException = null; static { TrustManager[] tmMediator; try { tmMediator = getTrustManagers(); } catch (Exception e) { reservedException = e; tmMediator = new TrustManager[0]; } trustManagers = tmMediator; if (reservedException == null) { KeyManager[] kmMediator; try { kmMediator = getKeyManagers(); } catch (Exception e) { reservedException = e; kmMediator = new KeyManager[0]; } keyManagers = kmMediator; } else { keyManagers = new KeyManager[0]; } } private static TrustManager[] getTrustManagers() throws Exception { TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); if ("SunJSSE".equals(tmf.getProvider().getName())) { // The implementation will load the default KeyStore // automatically. Cached trust materials may be used // for performance improvement. tmf.init((KeyStore)null); } else { // Use the explicitly specified KeyStore for third party's // TrustManagerFactory implementation. KeyStore ks = TrustStoreManager.getTrustedKeyStore(); tmf.init(ks); } return tmf.getTrustManagers(); } private static KeyManager[] getKeyManagers() throws Exception { final Map<String,String> props = new HashMap<>(); AccessController.doPrivileged( new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { props.put("keyStore", System.getProperty( "javax.net.ssl.keyStore", "")); props.put("keyStoreType", System.getProperty( "javax.net.ssl.keyStoreType", KeyStore.getDefaultType())); props.put("keyStoreProvider", System.getProperty( "javax.net.ssl.keyStoreProvider", "")); props.put("keyStorePasswd", System.getProperty( "javax.net.ssl.keyStorePassword", "")); return null; } }); final String defaultKeyStore = props.get("keyStore"); String defaultKeyStoreType = props.get("keyStoreType"); String defaultKeyStoreProvider = props.get("keyStoreProvider"); if (debug != null && Debug.isOn("defaultctx")) { System.out.println("keyStore is : " + defaultKeyStore); System.out.println("keyStore type is : " + defaultKeyStoreType); System.out.println("keyStore provider is : " + defaultKeyStoreProvider); } if (P11KEYSTORE.equals(defaultKeyStoreType) && !NONE.equals(defaultKeyStore)) { throw new IllegalArgumentException("if keyStoreType is " + P11KEYSTORE + ", then keyStore must be " + NONE); } FileInputStream fs = null; KeyStore ks = null; char[] passwd = null; try { if (defaultKeyStore.length() != 0 && !NONE.equals(defaultKeyStore)) { fs = AccessController.doPrivileged( new PrivilegedExceptionAction<FileInputStream>() { @Override public FileInputStream run() throws Exception { return new FileInputStream(defaultKeyStore); } }); } String defaultKeyStorePassword = props.get("keyStorePasswd"); if (defaultKeyStorePassword.length() != 0) { passwd = defaultKeyStorePassword.toCharArray(); } /** * Try to initialize key store. */ if ((defaultKeyStoreType.length()) != 0) { if (debug != null && Debug.isOn("defaultctx")) { System.out.println("init keystore"); } if (defaultKeyStoreProvider.length() == 0) { ks = KeyStore.getInstance(defaultKeyStoreType); } else { ks = KeyStore.getInstance(defaultKeyStoreType, defaultKeyStoreProvider); } // if defaultKeyStore is NONE, fs will be null ks.load(fs, passwd); } } finally { if (fs != null) { fs.close(); fs = null; } } /* * Try to initialize key manager. */ if (debug != null && Debug.isOn("defaultctx")) { System.out.println("init keymanager of type " + KeyManagerFactory.getDefaultAlgorithm()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); if (P11KEYSTORE.equals(defaultKeyStoreType)) { kmf.init(ks, null); // do not pass key passwd if using token } else { kmf.init(ks, passwd); } return kmf.getKeyManagers(); } } // lazy initialization holder class idiom for static default parameters // // See Effective Java Second Edition: Item 71. private static final class DefaultSSLContextHolder { private static final SSLContextImpl sslContext; static Exception reservedException = null; static { SSLContextImpl mediator = null; if (DefaultManagersHolder.reservedException != null) { reservedException = DefaultManagersHolder.reservedException; } else { try { mediator = new DefaultSSLContext(); } catch (Exception e) { reservedException = e; } } sslContext = mediator; } } /* * The SSLContext implementation for default "Default" algorithm * * @see SSLContext */ public static final class DefaultSSLContext extends CustomizedTLSContext { // public constructor for SSLContext.getInstance("Default") public DefaultSSLContext() throws Exception { if (DefaultManagersHolder.reservedException != null) { throw DefaultManagersHolder.reservedException; } try { super.engineInit(DefaultManagersHolder.keyManagers, DefaultManagersHolder.trustManagers, null); } catch (Exception e) { if (debug != null && Debug.isOn("defaultctx")) { System.out.println("default context init failed: " + e); } throw e; } } @Override protected void engineInit(KeyManager[] km, TrustManager[] tm, SecureRandom sr) throws KeyManagementException { throw new KeyManagementException ("Default SSLContext is initialized automatically"); } static SSLContextImpl getDefaultImpl() throws Exception { if (DefaultSSLContextHolder.reservedException != null) { throw DefaultSSLContextHolder.reservedException; } return DefaultSSLContextHolder.sslContext; } } /* * The base abstract SSLContext implementation for the Datagram Transport * Layer Security (DTLS) protocols. * * This abstract class encapsulates supported and the default server DTLS * parameters. * * @see SSLContext */ private abstract static class AbstractDTLSContext extends SSLContextImpl { private static final ProtocolList supportedProtocolList; private static final ProtocolList serverDefaultProtocolList; private static final CipherSuiteList supportedCipherSuiteList; private static final CipherSuiteList serverDefaultCipherSuiteList; static { // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode. supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.DTLS10.name, ProtocolVersion.DTLS12.name }); // available protocols for server mode serverDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.DTLS10, ProtocolVersion.DTLS12 })); supportedCipherSuiteList = getApplicableSupportedCipherSuiteList( supportedProtocolList); serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( serverDefaultProtocolList, false); } @Override ProtocolList getSuportedProtocolList() { return supportedProtocolList; } @Override CipherSuiteList getSupportedCipherSuiteList() { return supportedCipherSuiteList; } @Override ProtocolList getServerDefaultProtocolList() { return serverDefaultProtocolList; } @Override CipherSuiteList getServerDefaultCipherSuiteList() { return serverDefaultCipherSuiteList; } @Override SSLEngine createSSLEngineImpl() { return new SSLEngineImpl(this, true); } @Override SSLEngine createSSLEngineImpl(String host, int port) { return new SSLEngineImpl(this, host, port, true); } @Override HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) { return new HelloCookieManager(secureRandom); } } /* * The SSLContext implementation for DTLSv1.0 algorithm. * * @see SSLContext */ public static final class DTLS10Context extends AbstractDTLSContext { private static final ProtocolList clientDefaultProtocolList; private static final CipherSuiteList clientDefaultCipherSuiteList; static { // available protocols for client mode clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.DTLS10 })); clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( clientDefaultProtocolList, true); } @Override ProtocolList getClientDefaultProtocolList() { return clientDefaultProtocolList; } @Override CipherSuiteList getClientDefaultCipherSuiteList() { return clientDefaultCipherSuiteList; } } /* * The SSLContext implementation for DTLSv1.2 algorithm. * * @see SSLContext */ public static final class DTLS12Context extends AbstractDTLSContext { private static final ProtocolList clientDefaultProtocolList; private static final CipherSuiteList clientDefaultCipherSuiteList; static { // available protocols for client mode clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.DTLS10, ProtocolVersion.DTLS12 })); clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( clientDefaultProtocolList, true); } @Override ProtocolList getClientDefaultProtocolList() { return clientDefaultProtocolList; } @Override CipherSuiteList getClientDefaultCipherSuiteList() { return clientDefaultCipherSuiteList; } } /* * The SSLContext implementation for customized TLS protocols * * @see SSLContext */ private static class CustomizedDTLSContext extends AbstractDTLSContext { private static final ProtocolList clientDefaultProtocolList; private static final CipherSuiteList clientDefaultCipherSuiteList; private static IllegalArgumentException reservedException = null; // Don't want a java.lang.LinkageError for illegal system property. // // Please don't throw exception in this static block. Otherwise, // java.lang.LinkageError may be thrown during the instantiation of // the provider service. Instead, let's handle the initialization // exception in constructor. static { reservedException = CustomizedSSLProtocols.reservedException; if (reservedException == null) { ArrayList<ProtocolVersion> customizedDTLSProtocols = new ArrayList<>(); for (ProtocolVersion protocol : CustomizedSSLProtocols.customizedProtocols) { if (protocol.isDTLSProtocol()) { customizedDTLSProtocols.add(protocol); } } // candidates for available protocols ProtocolVersion[] candidates; if (customizedDTLSProtocols.isEmpty()) { // Use the default enabled client protocols if no // customized TLS protocols. // // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode. candidates = new ProtocolVersion[] { ProtocolVersion.DTLS10, ProtocolVersion.DTLS12 }; } else { // Use the customized TLS protocols. candidates = new ProtocolVersion[customizedDTLSProtocols.size()]; candidates = customizedDTLSProtocols.toArray(candidates); } clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(candidates)); clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( clientDefaultProtocolList, true); } else { clientDefaultProtocolList = null; // unlikely to be used clientDefaultCipherSuiteList = null; // unlikely to be used } } protected CustomizedDTLSContext() { if (reservedException != null) { throw reservedException; } } @Override ProtocolList getClientDefaultProtocolList() { return clientDefaultProtocolList; } @Override CipherSuiteList getClientDefaultCipherSuiteList() { return clientDefaultCipherSuiteList; } } /* * The SSLContext implementation for default "DTLS" algorithm * * @see SSLContext */ public static final class DTLSContext extends CustomizedDTLSContext { // use the default constructor and methods } } final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager implements X509TrustManager { // the delegated trust manager private final X509TrustManager tm; AbstractTrustManagerWrapper(X509TrustManager tm) { this.tm = tm; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { tm.checkClientTrusted(chain, authType); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { tm.checkServerTrusted(chain, authType); } @Override public X509Certificate[] getAcceptedIssuers() { return tm.getAcceptedIssuers(); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { tm.checkClientTrusted(chain, authType); checkAdditionalTrust(chain, authType, socket, true); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { tm.checkServerTrusted(chain, authType); checkAdditionalTrust(chain, authType, socket, false); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { tm.checkClientTrusted(chain, authType); checkAdditionalTrust(chain, authType, engine, true); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { tm.checkServerTrusted(chain, authType); checkAdditionalTrust(chain, authType, engine, false); } private void checkAdditionalTrust(X509Certificate[] chain, String authType, Socket socket, boolean isClient) throws CertificateException { if (socket != null && socket.isConnected() && socket instanceof SSLSocket) { SSLSocket sslSocket = (SSLSocket)socket; SSLSession session = sslSocket.getHandshakeSession(); if (session == null) { throw new CertificateException("No handshake session"); } // check endpoint identity String identityAlg = sslSocket.getSSLParameters(). getEndpointIdentificationAlgorithm(); if (identityAlg != null && identityAlg.length() != 0) { String hostname = session.getPeerHost(); X509TrustManagerImpl.checkIdentity( hostname, chain[0], identityAlg); } // try the best to check the algorithm constraints ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); AlgorithmConstraints constraints = null; if (protocolVersion.useTLS12PlusSpec()) { if (session instanceof ExtendedSSLSession) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; String[] peerSupportedSignAlgs = extSession.getLocalSupportedSignatureAlgorithms(); constraints = new SSLAlgorithmConstraints( sslSocket, peerSupportedSignAlgs, true); } else { constraints = new SSLAlgorithmConstraints(sslSocket, true); } } else { constraints = new SSLAlgorithmConstraints(sslSocket, true); } checkAlgorithmConstraints(chain, constraints, isClient); } } private void checkAdditionalTrust(X509Certificate[] chain, String authType, SSLEngine engine, boolean isClient) throws CertificateException { if (engine != null) { SSLSession session = engine.getHandshakeSession(); if (session == null) { throw new CertificateException("No handshake session"); } // check endpoint identity String identityAlg = engine.getSSLParameters(). getEndpointIdentificationAlgorithm(); if (identityAlg != null && identityAlg.length() != 0) { String hostname = session.getPeerHost(); X509TrustManagerImpl.checkIdentity( hostname, chain[0], identityAlg); } // try the best to check the algorithm constraints ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); AlgorithmConstraints constraints = null; if (protocolVersion.useTLS12PlusSpec()) { if (session instanceof ExtendedSSLSession) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; String[] peerSupportedSignAlgs = extSession.getLocalSupportedSignatureAlgorithms(); constraints = new SSLAlgorithmConstraints( engine, peerSupportedSignAlgs, true); } else { constraints = new SSLAlgorithmConstraints(engine, true); } } else { constraints = new SSLAlgorithmConstraints(engine, true); } checkAlgorithmConstraints(chain, constraints, isClient); } } private void checkAlgorithmConstraints(X509Certificate[] chain, AlgorithmConstraints constraints, boolean isClient) throws CertificateException { try { // Does the certificate chain end with a trusted certificate? int checkedLength = chain.length - 1; Collection<X509Certificate> trustedCerts = new HashSet<>(); X509Certificate[] certs = tm.getAcceptedIssuers(); if ((certs != null) && (certs.length > 0)){ Collections.addAll(trustedCerts, certs); } if (trustedCerts.contains(chain[checkedLength])) { checkedLength--; } // A forward checker, need to check from trust to target if (checkedLength >= 0) { AlgorithmChecker checker = new AlgorithmChecker(constraints, null, (isClient ? Validator.VAR_TLS_CLIENT : Validator.VAR_TLS_SERVER)); checker.init(false); for (int i = checkedLength; i >= 0; i--) { Certificate cert = chain[i]; // We don't care about the unresolved critical extensions. checker.check(cert, Collections.<String>emptySet()); } } } catch (CertPathValidatorException cpve) { throw new CertificateException( "Certificates do not conform to algorithm constraints", cpve); } } } // Dummy X509TrustManager implementation, rejects all peer certificates. // Used if the application did not specify a proper X509TrustManager. final class DummyX509TrustManager extends X509ExtendedTrustManager implements X509TrustManager { static final X509TrustManager INSTANCE = new DummyX509TrustManager(); private DummyX509TrustManager() { // empty } /* * Given the partial or complete certificate chain * provided by the peer, build a certificate path * to a trusted root and return if it can be * validated and is trusted for client SSL authentication. * If not, it throws an exception. */ @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation avaiable"); } /* * Given the partial or complete certificate chain * provided by the peer, build a certificate path * to a trusted root and return if it can be * validated and is trusted for server SSL authentication. * If not, it throws an exception. */ @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } /* * Return an array of issuer certificates which are trusted * for authenticating peers. */ @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } } /* * A wrapper class to turn a X509KeyManager into an X509ExtendedKeyManager */ final class AbstractKeyManagerWrapper extends X509ExtendedKeyManager { private final X509KeyManager km; AbstractKeyManagerWrapper(X509KeyManager km) { this.km = km; } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return km.getClientAliases(keyType, issuers); } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return km.chooseClientAlias(keyType, issuers, socket); } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return km.getServerAliases(keyType, issuers); } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return km.chooseServerAlias(keyType, issuers, socket); } @Override public X509Certificate[] getCertificateChain(String alias) { return km.getCertificateChain(alias); } @Override public PrivateKey getPrivateKey(String alias) { return km.getPrivateKey(alias); } // Inherit chooseEngineClientAlias() and chooseEngineServerAlias() from // X509ExtendedKeymanager. It defines them to return null; } // Dummy X509KeyManager implementation, never returns any certificates/keys. // Used if the application did not specify a proper X509TrustManager. final class DummyX509KeyManager extends X509ExtendedKeyManager { static final X509ExtendedKeyManager INSTANCE = new DummyX509KeyManager(); private DummyX509KeyManager() { // empty } /* * Get the matching aliases for authenticating the client side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return null; } /* * Choose an alias to authenticate the client side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { return null; } /* * Choose an alias to authenticate the client side of an * engine given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseEngineClientAlias( String[] keyTypes, Principal[] issuers, SSLEngine engine) { return null; } /* * Get the matching aliases for authenticating the server side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return null; } /* * Choose an alias to authenticate the server side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return null; } /* * Choose an alias to authenticate the server side of an engine * given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseEngineServerAlias( String keyType, Principal[] issuers, SSLEngine engine) { return null; }
Returns the certificate chain associated with the given alias.
Params:
  • alias – the alias name
Returns:the certificate chain (ordered with the user's certificate first and the root certificate authority last)
/** * Returns the certificate chain associated with the given alias. * * @param alias the alias name * * @return the certificate chain (ordered with the user's certificate first * and the root certificate authority last) */
@Override public X509Certificate[] getCertificateChain(String alias) { return null; } /* * Returns the key associated with the given alias, using the given * password to recover it. * * @param alias the alias name * * @return the requested key */ @Override public PrivateKey getPrivateKey(String alias) { return null; } }