Mercurial > hg > release > icedtea7-forest-2.6 > jdk
changeset 9711:bbb49ffe60a3
8133070: Hot lock on BulkCipher.isAvailable
Reviewed-by: mullan
Contributed-by: xuelei.fan@oracle.com, kungu.mjh@alibaba-inc.com
author | igerasim |
---|---|
date | Wed, 13 Feb 2019 06:50:44 +0000 |
parents | 83079b594f5e |
children | 1f10820808c5 |
files | src/share/classes/sun/security/ssl/CipherSuite.java src/share/classes/sun/security/ssl/CipherSuiteList.java src/share/classes/sun/security/ssl/JsseJce.java src/share/classes/sun/security/ssl/SSLContextImpl.java |
diffstat | 4 files changed, 411 insertions(+), 377 deletions(-) [+] |
line wrap: on
line diff
--- a/src/share/classes/sun/security/ssl/CipherSuite.java Fri Oct 05 14:11:39 2018 -0700 +++ b/src/share/classes/sun/security/ssl/CipherSuite.java Wed Feb 13 06:50:44 2019 +0000 @@ -32,6 +32,7 @@ import java.security.InvalidKeyException; import java.security.SecureRandom; +import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; @@ -73,12 +74,6 @@ // 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 +181,6 @@ * 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(); @@ -397,10 +389,6 @@ */ final static class BulkCipher { - // Map BulkCipher -> Boolean(available) - private final static Map<BulkCipher,Boolean> availableCache = - new HashMap<>(8); - // descriptive name including key size, e.g. AES/128 final String description; @@ -429,6 +417,9 @@ // Is the cipher algorithm of Cipher Block Chaining (CBC) mode? final boolean isCBCMode; + // runtime availability + private final boolean isAvailable; + BulkCipher(String transformation, int keySize, int expandedKeySize, int ivSize, boolean allowed) { this.transformation = transformation; @@ -443,6 +434,17 @@ 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, int keySize, @@ -459,6 +461,17 @@ 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; } /** @@ -476,49 +489,27 @@ /** * 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 - * 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. */ boolean isAvailable() { - if (allowed == false) { - return false; - } - if (this == B_AES_256) { - 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 synchronized boolean isAvailable(BulkCipher cipher) { - Boolean b = availableCache.get(cipher); - if (b == null) { + private static boolean isUnlimited(int keySize, String transformation) { + int keySizeInBits = keySize * 8; + if (keySizeInBits > 128) { // need the JCE unlimited + // strength jurisdiction policy try { - SecretKey key = new SecretKeySpec - (new byte[cipher.expandedKeySize], cipher.algorithm); - IvParameterSpec iv = - new IvParameterSpec(new byte[cipher.ivSize]); - cipher.newCipher(ProtocolVersion.DEFAULT, - key, iv, null, true); - b = Boolean.TRUE; - } catch (NoSuchAlgorithmException e) { - b = Boolean.FALSE; + if (Cipher.getMaxAllowedKeyLength( + transformation) < keySizeInBits) { + + return false; + } + } catch (Exception e) { + return false; } - availableCache.put(cipher, b); } - return b.booleanValue(); + + return true; } public String toString() {
--- a/src/share/classes/sun/security/ssl/CipherSuiteList.java Fri Oct 05 14:11:39 2018 -0700 +++ b/src/share/classes/sun/security/ssl/CipherSuiteList.java Wed Feb 13 06:50:44 2019 +0000 @@ -74,24 +74,12 @@ throw new IllegalArgumentException("CipherSuites may not be null"); } cipherSuites = new ArrayList<CipherSuite>(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); } @@ -189,16 +177,4 @@ } 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(); - } - } }
--- a/src/share/classes/sun/security/ssl/JsseJce.java Fri Oct 05 14:11:39 2018 -0700 +++ b/src/share/classes/sun/security/ssl/JsseJce.java Wed Feb 13 06:50:44 2019 +0000 @@ -58,11 +58,6 @@ 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; @@ -191,25 +186,8 @@ // 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"); - JsseJce.getAlgorithmParameters("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() { @@ -420,4 +398,28 @@ } } + + // 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"); + JsseJce.getAlgorithmParameters("EC"); + } catch (Exception e) { + mediator = false; + } + + isAvailable = mediator; + } + } }
--- a/src/share/classes/sun/security/ssl/SSLContextImpl.java Fri Oct 05 14:11:39 2018 -0700 +++ b/src/share/classes/sun/security/ssl/SSLContextImpl.java Wed Feb 13 06:50:44 2019 +0000 @@ -53,16 +53,6 @@ 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(); @@ -189,13 +179,15 @@ } return new SSLServerSocketFactoryImpl(this); } + abstract SSLEngine createSSLEngineImpl(); + abstract SSLEngine createSSLEngineImpl(String host, int port); protected SSLEngine engineCreateSSLEngine() { if (!isInitialized) { throw new IllegalStateException( "SSLContextImpl is not initialized"); } - return new SSLEngineImpl(this); + return createSSLEngineImpl(); } protected SSLEngine engineCreateSSLEngine(String host, int port) { @@ -203,7 +195,7 @@ throw new IllegalStateException( "SSLContextImpl is not initialized"); } - return new SSLEngineImpl(this, host, port); + return createSSLEngineImpl(host, port); } protected SSLSessionContext engineGetClientSessionContext() { @@ -230,78 +222,35 @@ 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(); + + // Get default ProtocolList for server mode. + abstract ProtocolList getServerDefaultProtocolList(); + + // Get default ProtocolList for client mode. + abstract ProtocolList getClientDefaultProtocolList(); - return supportedProtocolList; - } + // 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) { - if (roleIsServer) { - if (defaultServerProtocolList == null) { - defaultServerProtocolList = new ProtocolList( - getDefaultServerSSLParams().getProtocols()); - } - - return defaultServerProtocolList; - } else { - if (defaultClientProtocolList == null) { - defaultClientProtocolList = new ProtocolList( - getDefaultClientSSLParams().getProtocols()); - } - - return defaultClientProtocolList; - } - } - - // 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); - } - - return supportedCipherSuiteList; - } + 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(); } /** @@ -309,16 +258,24 @@ * 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; @@ -371,29 +328,11 @@ 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(); - } - } - - static String[] getAvailableProtocols( + private static String[] getAvailableProtocols( ProtocolVersion[] protocolCandidates) { List<String> availableProtocols = Collections.<String>emptyList(); - if (protocolCandidates != null && protocolCandidates.length != 0) { + if (protocolCandidates != null && protocolCandidates.length != 0) { availableProtocols = new ArrayList<>(protocolCandidates.length); for (ProtocolVersion p : protocolCandidates) { if (ProtocolVersion.availableProtocols.contains(p)) { @@ -435,39 +374,37 @@ */ /* - * 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; + 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 { - // supported SSL parameters - supportedSSLParams = new SSLParameters(); - - // candidates for available protocols - ProtocolVersion[] candidates; - 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, @@ -475,26 +412,50 @@ 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)); + supportedCipherSuiteList = getApplicableCipherSuiteList( + supportedProtocolList, false); // all supported + serverDefaultCipherSuiteList = getApplicableCipherSuiteList( + serverDefaultProtocolList, true); // enabled only + } + + @Override + ProtocolList getSuportedProtocolList() { + return supportedProtocolList; + } + + @Override + CipherSuiteList getSupportedCipherSuiteList() { + return supportedCipherSuiteList; } - SSLParameters getDefaultServerSSLParams() { - return defaultServerSSLParams; + @Override + ProtocolList getServerDefaultProtocolList() { + return serverDefaultProtocolList; } - SSLParameters getSupportedSSLParams() { - return supportedSSLParams; + @Override + CipherSuiteList getServerDefaultCipherSuiteList() { + return serverDefaultCipherSuiteList; + } + + @Override + SSLEngine createSSLEngineImpl() { + return new SSLEngineImpl(this); + } + + @Override + SSLEngine createSSLEngineImpl(String host, int port) { + return new SSLEngineImpl(this, host, port); } } @@ -503,31 +464,36 @@ * * @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)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -536,33 +502,38 @@ * * @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)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -571,35 +542,101 @@ * * @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)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only + } + + @Override + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + 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 = AccessController.doPrivileged( + new GetPropertyAction(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); + } + } + } } } @@ -608,9 +645,11 @@ * * @see SSLContext */ - private static class CustomizedSSLContext extends AbstractSSLContext { - private static final String PROPERTY_NAME = "jdk.tls.client.protocols"; - private static final SSLParameters defaultClientSSLParams; + 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. @@ -620,86 +659,65 @@ // the provider service. Instead, let's handle the initialization // exception in 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 { - // 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); + reservedException = CustomizedSSLProtocols.reservedException; + if (reservedException == null) { + ArrayList<ProtocolVersion> + customizedTLSProtocols = new ArrayList<>(); + for (ProtocolVersion protocol : + CustomizedSSLProtocols.customizedProtocols) { + customizedTLSProtocols.add(protocol); } - String[] protocols = null; - if (property != null && property.length() != 0) { - protocols = property.split(","); + // 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 { - reservedException = new IllegalArgumentException( - "No protocol specified in " + - PROPERTY_NAME + " system property"); - protocols = new String[0]; + // Use the customized TLS protocols. + candidates = + new ProtocolVersion[customizedTLSProtocols.size()]; + candidates = customizedTLSProtocols.toArray(candidates); } - candidates = new ProtocolVersion[protocols.length]; - 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; - } - } - - if ((reservedException == null) && SunJSSE.isFIPS()) { - for (ProtocolVersion protocolVersion : candidates) { - if (ProtocolVersion.SSL20Hello.v == protocolVersion.v || - ProtocolVersion.SSL30.v == protocolVersion.v) { - reservedException = new IllegalArgumentException( - PROPERTY_NAME + ": " + protocolVersion + - " is not FIPS compliant"); - } - } - } - } - - defaultClientSSLParams = new SSLParameters(); - if (reservedException == null) { - defaultClientSSLParams.setProtocols( - getAvailableProtocols(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; } } @@ -708,74 +726,57 @@ * * @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); + 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]; } - throw e; - } - - if (defaultImpl == null) { - defaultImpl = this; + keyManagers = kmMediator; + } else { + keyManagers = new KeyManager[0]; } } - 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(); - } - 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<String,String> props = new HashMap<>(); AccessController.doPrivileged( @@ -866,11 +867,75 @@ 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; + } + } + + }