diff --git a/src/share/classes/sun/security/ssl/CipherBox.java b/src/share/classes/sun/security/ssl/CipherBox.java index fa11bce..ec5880b 100644 --- a/src/share/classes/sun/security/ssl/CipherBox.java +++ b/src/share/classes/sun/security/ssl/CipherBox.java @@ -1044,40 +1044,6 @@ final class CipherBox { return nonce; } - /* - * Is this cipher available? - * - * This method can only be called by CipherSuite.BulkCipher.isAvailable() - * to test the availability of a cipher suites. Please DON'T use it in - * other places, otherwise, the behavior may be unexpected because we may - * initialize AEAD cipher improperly in the method. - */ - Boolean isAvailable() { - // We won't know whether a cipher for a particular key size is - // available until the cipher is successfully initialized. - // - // We do not initialize AEAD cipher in the constructor. Need to - // initialize the cipher to ensure that the AEAD mode for a - // particular key size is supported. - if (cipherType == AEAD_CIPHER) { - try { - Authenticator authenticator = - new Authenticator(protocolVersion); - byte[] nonce = authenticator.sequenceNumber(); - byte[] iv = Arrays.copyOf(fixedIv, - fixedIv.length + nonce.length); - System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length); - GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); - - cipher.init(mode, key, spec, random); - } catch (Exception e) { - return Boolean.FALSE; - } - } // Otherwise, we have initialized the cipher in the constructor. - - return Boolean.TRUE; - } - /** * Sanity check the length of a fragment before decryption. * diff --git a/src/share/classes/sun/security/ssl/CipherSuite.java b/src/share/classes/sun/security/ssl/CipherSuite.java index 740cdd6..677a7e1 100644 --- a/src/share/classes/sun/security/ssl/CipherSuite.java +++ b/src/share/classes/sun/security/ssl/CipherSuite.java @@ -75,12 +75,6 @@ final class CipherSuite implements Comparable { // minimum priority for default enabled CipherSuites final static int DEFAULT_SUITES_PRIORITY = 300; - // Flag indicating if CipherSuite availability can change dynamically. - // This is the case when we rely on a JCE cipher implementation that - // may not be available in the installed JCE providers. - // It is true because we might not have an ECC implementation. - final static boolean DYNAMIC_AVAILABILITY = true; - private final static boolean ALLOW_ECC = Debug.getBooleanProperty ("com.sun.net.ssl.enableECC", true); @@ -186,9 +180,6 @@ final class CipherSuite implements Comparable { * Return whether this CipherSuite is available for use. A * CipherSuite may be unavailable even if it is supported * (i.e. allowed == true) if the required JCE cipher is not installed. - * In some configuration, this situation may change over time, call - * CipherSuiteList.clearAvailableCache() before this method to obtain - * the most current status. */ boolean isAvailable() { return allowed && keyExchange.isAvailable() && cipher.isAvailable(); @@ -404,10 +395,6 @@ final class CipherSuite implements Comparable { */ final static class BulkCipher { - // Map BulkCipher -> Boolean(available) - private final static Map availableCache = - new HashMap<>(8); - // descriptive name including key size, e.g. AES/128 final String description; @@ -451,6 +438,9 @@ final class CipherSuite implements Comparable { // The secure random used to detect the cipher availability. private final static SecureRandom secureRandom; + // runtime availability + private final boolean isAvailable; + static { try { secureRandom = JsseJce.getSecureRandom(); @@ -475,6 +465,17 @@ final class CipherSuite implements Comparable { this.expandedKeySize = expandedKeySize; this.exportable = true; + + // availability of this bulk cipher + // + // Currently all supported ciphers except AES are always available + // via the JSSE internal implementations. We also assume AES/128 of + // CBC mode is always available since it is shipped with the SunJCE + // provider. However, AES/256 is unavailable when the default JCE + // policy jurisdiction files are installed because of key length + // restrictions. + this.isAvailable = + allowed ? isUnlimited(keySize, transformation) : false; } BulkCipher(String transformation, CipherType cipherType, int keySize, @@ -491,6 +492,17 @@ final class CipherSuite implements Comparable { this.expandedKeySize = keySize; this.exportable = false; + + // availability of this bulk cipher + // + // Currently all supported ciphers except AES are always available + // via the JSSE internal implementations. We also assume AES/128 of + // CBC mode is always available since it is shipped with the SunJCE + // provider. However, AES/256 is unavailable when the default JCE + // policy jurisdiction files are installed because of key length + // restrictions. + this.isAvailable = + allowed ? isUnlimited(keySize, transformation) : false; } /** @@ -508,84 +520,27 @@ final class CipherSuite implements Comparable { /** * Test if this bulk cipher is available. For use by CipherSuite. - * - * Currently all supported ciphers except AES are always available - * via the JSSE internal implementations. We also assume AES/128 of - * CBC mode is always available since it is shipped with the SunJCE - * provider. However, AES/256 is unavailable when the default JCE - * policy jurisdiction files are installed because of key length - * restrictions, and AEAD is unavailable when the underlying providers - * do not support AEAD/GCM mode. */ boolean isAvailable() { - if (allowed == false) { - return false; - } - - if ((this == B_AES_256) || - (this.cipherType == CipherType.AEAD_CIPHER)) { - return isAvailable(this); - } - - // always available - return true; + return this.isAvailable; } - // for use by CipherSuiteList.clearAvailableCache(); - static synchronized void clearAvailableCache() { - if (DYNAMIC_AVAILABILITY) { - availableCache.clear(); - } - } + private static boolean isUnlimited(int keySize, String transformation) { + int keySizeInBits = keySize * 8; + if (keySizeInBits > 128) { // need the JCE unlimited + // strength jurisdiction policy + try { + if (Cipher.getMaxAllowedKeyLength( + transformation) < keySizeInBits) { - private static synchronized boolean isAvailable(BulkCipher cipher) { - Boolean b = availableCache.get(cipher); - if (b == null) { - int keySizeInBits = cipher.keySize * 8; - if (keySizeInBits > 128) { // need the JCE unlimited - // strength jurisdiction policy - try { - if (Cipher.getMaxAllowedKeyLength( - cipher.transformation) < keySizeInBits) { - b = Boolean.FALSE; - } - } catch (Exception e) { - b = Boolean.FALSE; + return false; } + } catch (Exception e) { + return false; } - - if (b == null) { - b = Boolean.FALSE; // may be reset to TRUE if - // the cipher is available - CipherBox temporary = null; - try { - SecretKey key = new SecretKeySpec( - new byte[cipher.expandedKeySize], - cipher.algorithm); - IvParameterSpec iv; - if (cipher.cipherType == CipherType.AEAD_CIPHER) { - iv = new IvParameterSpec( - new byte[cipher.fixedIvSize]); - } else { - iv = new IvParameterSpec(new byte[cipher.ivSize]); - } - temporary = cipher.newCipher( - ProtocolVersion.DEFAULT, - key, iv, secureRandom, true); - b = temporary.isAvailable(); - } catch (NoSuchAlgorithmException e) { - // not available - } finally { - if (temporary != null) { - temporary.dispose(); - } - } - } - - availableCache.put(cipher, b); } - return b.booleanValue(); + return true; } @Override diff --git a/src/share/classes/sun/security/ssl/CipherSuiteList.java b/src/share/classes/sun/security/ssl/CipherSuiteList.java index 19dc90f..491bffa 100644 --- a/src/share/classes/sun/security/ssl/CipherSuiteList.java +++ b/src/share/classes/sun/security/ssl/CipherSuiteList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2015, 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 @@ -74,24 +74,12 @@ final class CipherSuiteList { throw new IllegalArgumentException("CipherSuites may not be null"); } cipherSuites = new ArrayList(names.length); - // refresh available cache once if a CipherSuite is not available - // (maybe new JCE providers have been installed) - boolean refreshed = false; for (int i = 0; i < names.length; i++) { String suiteName = names[i]; CipherSuite suite = CipherSuite.valueOf(suiteName); if (suite.isAvailable() == false) { - if (refreshed == false) { - // clear the cache so that the isAvailable() call below - // does a full check - clearAvailableCache(); - refreshed = true; - } - // still missing? - if (suite.isAvailable() == false) { - throw new IllegalArgumentException("Cannot support " - + suiteName + " with currently installed providers"); - } + throw new IllegalArgumentException("Cannot support " + + suiteName + " with currently installed providers"); } cipherSuites.add(suite); } @@ -195,16 +183,4 @@ final class CipherSuiteList { } s.putBytes16(suiteBytes); } - - /** - * Clear cache of available ciphersuites. If we support all ciphers - * internally, there is no need to clear the cache and calling this - * method has no effect. - */ - static synchronized void clearAvailableCache() { - if (CipherSuite.DYNAMIC_AVAILABILITY) { - CipherSuite.BulkCipher.clearAvailableCache(); - JsseJce.clearEcAvailable(); - } - } } diff --git a/src/share/classes/sun/security/ssl/JsseJce.java b/src/share/classes/sun/security/ssl/JsseJce.java index 4c98772..923ae8e 100644 --- a/src/share/classes/sun/security/ssl/JsseJce.java +++ b/src/share/classes/sun/security/ssl/JsseJce.java @@ -55,11 +55,6 @@ final class JsseJce { private final static ProviderList fipsProviderList; - // Flag indicating whether EC crypto is available. - // If null, then we have not checked yet. - // If yes, then all the EC based crypto we need is available. - private static Boolean ecAvailable; - // Flag indicating whether Kerberos crypto is available. // If true, then all the Kerberos-based crypto we need is available. private final static boolean kerberosAvailable; @@ -195,24 +190,8 @@ final class JsseJce { // no instantiation of this class } - synchronized static boolean isEcAvailable() { - if (ecAvailable == null) { - try { - JsseJce.getSignature(SIGNATURE_ECDSA); - JsseJce.getSignature(SIGNATURE_RAWECDSA); - JsseJce.getKeyAgreement("ECDH"); - JsseJce.getKeyFactory("EC"); - JsseJce.getKeyPairGenerator("EC"); - ecAvailable = true; - } catch (Exception e) { - ecAvailable = false; - } - } - return ecAvailable; - } - - synchronized static void clearEcAvailable() { - ecAvailable = null; + static boolean isEcAvailable() { + return EcAvailability.isAvailable; } static boolean isKerberosAvailable() { @@ -414,4 +393,27 @@ final class JsseJce { } } + + // lazy initialization holder class idiom for static default parameters + // + // See Effective Java Second Edition: Item 71. + private static class EcAvailability { + // Is EC crypto available? + private final static boolean isAvailable; + + static { + boolean mediator = true; + try { + JsseJce.getSignature(SIGNATURE_ECDSA); + JsseJce.getSignature(SIGNATURE_RAWECDSA); + JsseJce.getKeyAgreement("ECDH"); + JsseJce.getKeyFactory("EC"); + JsseJce.getKeyPairGenerator("EC"); + } catch (Exception e) { + mediator = false; + } + + isAvailable = mediator; + } + } } diff --git a/src/share/classes/sun/security/ssl/SSLContextImpl.java b/src/share/classes/sun/security/ssl/SSLContextImpl.java index b00a26d..c0cab21 100644 --- a/src/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/src/share/classes/sun/security/ssl/SSLContextImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -52,16 +52,6 @@ public abstract class SSLContextImpl extends SSLContextSpi { private X509TrustManager trustManager; private SecureRandom secureRandom; - // supported and default protocols - private ProtocolList defaultServerProtocolList; - private ProtocolList defaultClientProtocolList; - private ProtocolList supportedProtocolList; - - // supported and default cipher suites - private CipherSuiteList defaultServerCipherSuiteList; - private CipherSuiteList defaultClientCipherSuiteList; - private CipherSuiteList supportedCipherSuiteList; - SSLContextImpl() { ephemeralKeyManager = new EphemeralKeyManager(); clientCache = new SSLSessionContextImpl(); @@ -191,6 +181,8 @@ public abstract class SSLContextImpl extends SSLContextSpi { } return new SSLServerSocketFactoryImpl(this); } + abstract SSLEngine createSSLEngineImpl(); + abstract SSLEngine createSSLEngineImpl(String host, int port); @Override protected SSLEngine engineCreateSSLEngine() { @@ -198,7 +190,7 @@ public abstract class SSLContextImpl extends SSLContextSpi { throw new IllegalStateException( "SSLContextImpl is not initialized"); } - return new SSLEngineImpl(this); + return createSSLEngineImpl(); } @Override @@ -207,7 +199,7 @@ public abstract class SSLContextImpl extends SSLContextSpi { throw new IllegalStateException( "SSLContextImpl is not initialized"); } - return new SSLEngineImpl(this, host, port); + return createSSLEngineImpl(host, port); } @Override @@ -236,78 +228,35 @@ public abstract class SSLContextImpl extends SSLContextSpi { return ephemeralKeyManager; } - abstract SSLParameters getDefaultServerSSLParams(); - abstract SSLParameters getDefaultClientSSLParams(); - abstract SSLParameters getSupportedSSLParams(); // Get supported ProtocolList. - ProtocolList getSuportedProtocolList() { - if (supportedProtocolList == null) { - supportedProtocolList = - new ProtocolList(getSupportedSSLParams().getProtocols()); - } + abstract ProtocolList getSuportedProtocolList(); - return supportedProtocolList; - } + // Get default ProtocolList for server mode. + abstract ProtocolList getServerDefaultProtocolList(); - // Get default ProtocolList. - ProtocolList getDefaultProtocolList(boolean roleIsServer) { - if (roleIsServer) { - if (defaultServerProtocolList == null) { - defaultServerProtocolList = new ProtocolList( - getDefaultServerSSLParams().getProtocols()); - } + // Get default ProtocolList for client mode. + abstract ProtocolList getClientDefaultProtocolList(); - return defaultServerProtocolList; - } else { - if (defaultClientProtocolList == null) { - defaultClientProtocolList = new ProtocolList( - getDefaultClientSSLParams().getProtocols()); - } + // Get supported CipherSuiteList. + abstract CipherSuiteList getSupportedCipherSuiteList(); - return defaultClientProtocolList; - } - } + // Get default CipherSuiteList for server mode. + abstract CipherSuiteList getServerDefaultCipherSuiteList(); - // Get supported CipherSuiteList. - CipherSuiteList getSupportedCipherSuiteList() { - // The maintenance of cipher suites needs to be synchronized. - synchronized (this) { - // Clear cache of available ciphersuites. - clearAvailableCache(); - - if (supportedCipherSuiteList == null) { - supportedCipherSuiteList = getApplicableCipherSuiteList( - getSuportedProtocolList(), false); - } + // Get default CipherSuiteList for client mode. + abstract CipherSuiteList getClientDefaultCipherSuiteList(); - return supportedCipherSuiteList; - } + // Get default ProtocolList. + ProtocolList getDefaultProtocolList(boolean roleIsServer) { + return roleIsServer ? getServerDefaultProtocolList() + : getClientDefaultProtocolList(); } // Get default CipherSuiteList. CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) { - // The maintenance of cipher suites needs to be synchronized. - synchronized (this) { - // Clear cache of available ciphersuites. - clearAvailableCache(); - - if (roleIsServer) { - if (defaultServerCipherSuiteList == null) { - defaultServerCipherSuiteList = getApplicableCipherSuiteList( - getDefaultProtocolList(true), true); - } - - return defaultServerCipherSuiteList; - } else { - if (defaultClientCipherSuiteList == null) { - defaultClientCipherSuiteList = getApplicableCipherSuiteList( - getDefaultProtocolList(false), true); - } - - return defaultClientCipherSuiteList; - } - } + return roleIsServer ? getServerDefaultCipherSuiteList() + : getClientDefaultCipherSuiteList(); } /** @@ -315,16 +264,24 @@ public abstract class SSLContextImpl extends SSLContextSpi { * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */ boolean isDefaultProtocolList(ProtocolList protocols) { - return (protocols == defaultServerProtocolList) || - (protocols == defaultClientProtocolList); + return (protocols == getServerDefaultProtocolList()) || + (protocols == getClientDefaultProtocolList()); } + /** + * 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 the list of all available CipherSuites with a priority of * minPriority or above. */ - private CipherSuiteList getApplicableCipherSuiteList( + private static CipherSuiteList getApplicableCipherSuiteList( ProtocolList protocols, boolean onlyEnabled) { int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY; @@ -370,22 +327,20 @@ public abstract class SSLContextImpl extends SSLContextSpi { return new CipherSuiteList(suites); } - /** - * Clear cache of available ciphersuites. If we support all ciphers - * internally, there is no need to clear the cache and calling this - * method has no effect. - * - * Note that every call to clearAvailableCache() and the maintenance of - * cipher suites need to be synchronized with this instance. - */ - private void clearAvailableCache() { - if (CipherSuite.DYNAMIC_AVAILABILITY) { - supportedCipherSuiteList = null; - defaultServerCipherSuiteList = null; - defaultClientCipherSuiteList = null; - CipherSuite.BulkCipher.clearAvailableCache(); - JsseJce.clearEcAvailable(); + private static String[] getAvailableProtocols( + ProtocolVersion[] protocolCandidates) { + + List availableProtocols = Collections.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]); } /* @@ -418,39 +373,37 @@ public abstract class SSLContextImpl extends SSLContextSpi { */ /* - * The base abstract SSLContext implementation. + * The base abstract SSLContext implementation for the Transport Layer + * Security (TLS) protocols. * * This abstract class encapsulates supported and the default server - * SSL parameters. + * SSL/TLS parameters. * * @see SSLContext */ - private abstract static class AbstractSSLContext extends SSLContextImpl { - // parameters - private static final SSLParameters defaultServerSSLParams; - private static final SSLParameters supportedSSLParams; - - static { - // supported SSL parameters - supportedSSLParams = new SSLParameters(); + private abstract static class AbstractTLSContext extends SSLContextImpl { + private static final ProtocolList supportedProtocolList; + private static final ProtocolList serverDefaultProtocolList; - // candidates for available protocols - ProtocolVersion[] candidates; + private static final CipherSuiteList supportedCipherSuiteList; + private static final CipherSuiteList serverDefaultCipherSuiteList; + static { if (SunJSSE.isFIPS()) { - supportedSSLParams.setProtocols(new String[] { + supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.TLS10.name, ProtocolVersion.TLS11.name, ProtocolVersion.TLS12.name }); - candidates = new ProtocolVersion[] { + serverDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } else { - supportedSSLParams.setProtocols(new String[] { + supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.SSL20Hello.name, ProtocolVersion.SSL30.name, ProtocolVersion.TLS10.name, @@ -458,44 +411,50 @@ public abstract class SSLContextImpl extends SSLContextSpi { ProtocolVersion.TLS12.name }); - candidates = new ProtocolVersion[] { + serverDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL20Hello, ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } - defaultServerSSLParams = new SSLParameters(); - defaultServerSSLParams.setProtocols( - getAvailableProtocols(candidates).toArray(new String[0])); + supportedCipherSuiteList = getApplicableCipherSuiteList( + supportedProtocolList, false); // all supported + serverDefaultCipherSuiteList = getApplicableCipherSuiteList( + serverDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultServerSSLParams() { - return defaultServerSSLParams; + ProtocolList getSuportedProtocolList() { + return supportedProtocolList; } @Override - SSLParameters getSupportedSSLParams() { - return supportedSSLParams; + CipherSuiteList getSupportedCipherSuiteList() { + return supportedCipherSuiteList; } - static List getAvailableProtocols( - ProtocolVersion[] protocolCandidates) { + @Override + ProtocolList getServerDefaultProtocolList() { + return serverDefaultProtocolList; + } - List availableProtocols = Collections.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); - } - } - } + @Override + CipherSuiteList getServerDefaultCipherSuiteList() { + return serverDefaultCipherSuiteList; + } - return availableProtocols; + @Override + SSLEngine createSSLEngineImpl() { + return new SSLEngineImpl(this); + } + + @Override + SSLEngine createSSLEngineImpl(String host, int port) { + return new SSLEngineImpl(this, host, port); } } @@ -504,31 +463,36 @@ public abstract class SSLContextImpl extends SSLContextSpi { * * @see SSLContext */ - public static final class TLS10Context extends AbstractSSLContext { - private static final SSLParameters defaultClientSSLParams; + public static final class TLS10Context extends AbstractTLSContext { + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10 - }; + })); } else { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10 - }; + })); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates).toArray(new String[0])); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only + } + + @Override + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -537,33 +501,38 @@ public abstract class SSLContextImpl extends SSLContextSpi { * * @see SSLContext */ - public static final class TLS11Context extends AbstractSSLContext { - private static final SSLParameters defaultClientSSLParams; + public static final class TLS11Context extends AbstractTLSContext { + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11 - }; + })); } else { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11 - }; + })); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates).toArray(new String[0])); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -572,135 +541,182 @@ public abstract class SSLContextImpl extends SSLContextSpi { * * @see SSLContext */ - public static final class TLS12Context extends AbstractSSLContext { - private static final SSLParameters defaultClientSSLParams; + public static final class TLS12Context extends AbstractTLSContext { + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } else { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates).toArray(new String[0])); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only + } + + @Override + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } /* - * The SSLContext implementation for customized TLS protocols + * The interface for the customized SSL/(D)TLS SSLContext. * * @see SSLContext */ - private static class CustomizedSSLContext extends AbstractSSLContext { + private static class CustomizedSSLProtocols { private static final String PROPERTY_NAME = "jdk.tls.client.protocols"; - private static final SSLParameters defaultClientSSLParams; - private static IllegalArgumentException reservedException = null; + static IllegalArgumentException reservedException = null; + static ArrayList + 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, let's handle the initialization - // exception in constructor. + // the provider service. Instead, please handle the initialization + // exception in the caller's constructor. static { - // candidates for available protocols - ProtocolVersion[] candidates; - String property = AccessController.doPrivileged( new GetPropertyAction(PROPERTY_NAME)); - if (property == null || property.length() == 0) { - // the default enabled client 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 { + 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); } + } - String[] protocols = null; - if (property != null && property.length() != 0) { - protocols = property.split(","); - } else { - reservedException = new IllegalArgumentException( - "No protocol specified in " + - PROPERTY_NAME + " system property"); - protocols = new String[0]; - } - - candidates = new ProtocolVersion[protocols.length]; + 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 { - candidates[i] = ProtocolVersion.valueOf(protocols[i]); - } catch (IllegalArgumentException iae) { - reservedException = new IllegalArgumentException( - PROPERTY_NAME + ": " + protocols[i] + - " is not a standard SSL/TLS protocol name", iae); - break; - } - } + ProtocolVersion pro = + ProtocolVersion.valueOf(protocols[i]); - if ((reservedException == null) && SunJSSE.isFIPS()) { - for (ProtocolVersion protocolVersion : candidates) { - if (ProtocolVersion.SSL20Hello.v == protocolVersion.v || - ProtocolVersion.SSL30.v == protocolVersion.v) { + if (SunJSSE.isFIPS() && + ((pro.v == ProtocolVersion.SSL30.v) || + (pro.v == ProtocolVersion.SSL20Hello.v))) { reservedException = new IllegalArgumentException( - PROPERTY_NAME + ": " + protocolVersion + - " is not FIPS compliant"); + 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); } } } + } + } - defaultClientSSLParams = new SSLParameters(); + /* + * 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) { - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates).toArray(new String[0])); + ArrayList + customizedTLSProtocols = new ArrayList<>(); + for (ProtocolVersion protocol : + CustomizedSSLProtocols.customizedProtocols) { + 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 = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only + } else { + clientDefaultProtocolList = null; // unlikely to be used + clientDefaultCipherSuiteList = null; // unlikely to be used } } - protected CustomizedSSLContext() { + protected CustomizedTLSContext() { if (reservedException != null) { throw reservedException; } } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -709,75 +725,57 @@ public abstract class SSLContextImpl extends SSLContextSpi { * * @see SSLContext */ - public static final class TLSContext extends CustomizedSSLContext { + public static final class TLSContext extends CustomizedTLSContext { // use the default constructor and methods } - /* - * The SSLContext implementation for default "Default" algorithm - * - * @see SSLContext - */ - public static final class DefaultSSLContext extends CustomizedSSLContext { + // 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 volatile SSLContextImpl defaultImpl; + private static final TrustManager[] trustManagers; + private static final KeyManager[] keyManagers; - private static TrustManager[] defaultTrustManagers; - private static KeyManager[] defaultKeyManagers; + static Exception reservedException = null; - public DefaultSSLContext() throws Exception { + static { + TrustManager[] tmMediator; try { - super.engineInit(getDefaultKeyManager(), - getDefaultTrustManager(), null); + tmMediator = getTrustManagers(); } catch (Exception e) { - if (debug != null && Debug.isOn("defaultctx")) { - System.out.println("default context init failed: " + e); - } - throw e; - } - - if (defaultImpl == null) { - defaultImpl = this; + reservedException = e; + tmMediator = new TrustManager[0]; } - } + trustManagers = tmMediator; - @Override - protected void engineInit(KeyManager[] km, TrustManager[] tm, - SecureRandom sr) throws KeyManagementException { - throw new KeyManagementException - ("Default SSLContext is initialized automatically"); - } - - static synchronized SSLContextImpl getDefaultImpl() throws Exception { - if (defaultImpl == null) { - new DefaultSSLContext(); + if (reservedException == null) { + KeyManager[] kmMediator; + try { + kmMediator = getKeyManagers(); + } catch (Exception e) { + reservedException = e; + kmMediator = new KeyManager[0]; + } + keyManagers = kmMediator; + } else { + keyManagers = new KeyManager[0]; } - return defaultImpl; } - private static synchronized TrustManager[] getDefaultTrustManager() - throws Exception { - if (defaultTrustManagers != null) { - return defaultTrustManagers; - } - + private static TrustManager[] getTrustManagers() throws Exception { KeyStore ks = TrustManagerFactoryImpl.getCacertsKeyStore("defaultctx"); TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); - defaultTrustManagers = tmf.getTrustManagers(); - return defaultTrustManagers; + return tmf.getTrustManagers(); } - private static synchronized KeyManager[] getDefaultKeyManager() - throws Exception { - if (defaultKeyManagers != null) { - return defaultKeyManagers; - } + private static KeyManager[] getKeyManagers() throws Exception { final Map props = new HashMap<>(); AccessController.doPrivileged( @@ -874,11 +872,75 @@ public abstract class SSLContextImpl extends SSLContextSpi { kmf.init(ks, passwd); } - defaultKeyManagers = kmf.getKeyManagers(); - return defaultKeyManagers; + 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; + } + } + + }