8133070: Hot lock on BulkCipher.isAvailable
authorxuelei
Wed, 23 Dec 2015 03:51:35 +0000
changeset 34826 4bbdce2630f8
parent 34825 d315dac0f1a8
child 34827 de746e7eb1e3
8133070: Hot lock on BulkCipher.isAvailable Reviewed-by: mullan
jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java
jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java
jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java
jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java
jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java	Tue Dec 22 21:32:29 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java	Wed Dec 23 03:51:35 2015 +0000
@@ -1105,41 +1105,6 @@
         return fragLen;
     }
 
-
-    /*
-     * 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.
      *
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Tue Dec 22 21:32:29 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Wed Dec 23 03:51:35 2015 +0000
@@ -77,12 +77,6 @@
     // minimum priority for default enabled CipherSuites
     static final 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.
-    static final boolean DYNAMIC_AVAILABILITY = true;
-
     private static final boolean ALLOW_ECC = Debug.getBooleanProperty
         ("com.sun.net.ssl.enableECC", true);
 
@@ -176,9 +170,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();
@@ -471,10 +462,6 @@
         B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 12, 4, true),
         B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 12, 4, true);
 
-        // Map BulkCipher -> Boolean(available)
-        private static final Map<BulkCipher,Boolean> availableCache =
-                                            new HashMap<>(8);
-
         // descriptive name including key size, e.g. AES/128
         final String description;
 
@@ -518,6 +505,9 @@
         // The secure random used to detect the cipher availability.
         private static final SecureRandom secureRandom;
 
+        // runtime availability
+        private final boolean isAvailable;
+
         static {
             try {
                 secureRandom = JsseJce.getSecureRandom();
@@ -542,6 +532,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, CipherType cipherType, int keySize,
@@ -558,6 +559,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;
         }
 
         /**
@@ -575,84 +587,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 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;
-        }
-
-        // for use by CipherSuiteList.clearAvailableCache();
-        static synchronized void clearAvailableCache() {
-            if (DYNAMIC_AVAILABILITY) {
-                availableCache.clear();
-            }
+            return this.isAvailable;
         }
 
-        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;
-                    }
-                }
+        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) {
 
-                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_TLS,
-                                            key, iv, secureRandom, true);
-                        b = temporary.isAvailable();
-                    } catch (NoSuchAlgorithmException e) {
-                        // not available
-                    } finally {
-                        if (temporary != null) {
-                            temporary.dispose();
-                        }
+                        return false;
                     }
+                } catch (Exception e) {
+                    return false;
                 }
-
-                availableCache.put(cipher, b);
             }
 
-            return b.booleanValue();
+            return true;
         }
 
         @Override
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java	Tue Dec 22 21:32:29 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java	Wed Dec 23 03:51:35 2015 +0000
@@ -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 @@
             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);
         }
@@ -195,16 +183,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/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Tue Dec 22 21:32:29 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Wed Dec 23 03:51:35 2015 +0000
@@ -55,11 +55,6 @@
 
     private static final 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 static final boolean kerberosAvailable;
@@ -180,24 +175,8 @@
         // no instantiation of this class
     }
 
-    static synchronized 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;
-    }
-
-    static synchronized void clearEcAvailable() {
-        ecAvailable = null;
+    static boolean isEcAvailable() {
+        return EcAvailability.isAvailable;
     }
 
     static boolean isKerberosAvailable() {
@@ -399,4 +378,27 @@
         }
     }
 
+
+    // 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;
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Tue Dec 22 21:32:29 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Wed Dec 23 03:51:35 2015 +0000
@@ -52,18 +52,8 @@
     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;
-
     // DTLS cookie exchange manager
-    private HelloCookieManager helloCookieManager;
+    private volatile HelloCookieManager helloCookieManager;
 
     private StatusResponseManager statusResponseManager;
 
@@ -117,6 +107,7 @@
         if (debug != null && Debug.isOn("sslctx")) {
             System.out.println("done seeding SecureRandom");
         }
+
         isInitialized = true;
     }
 
@@ -242,13 +233,20 @@
         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) {
-            helloCookieManager = getHelloCookieManager(secureRandom);
+        if (helloCookieManager != null) {
+            return helloCookieManager;
+        }
+
+        synchronized (this) {
+            if (helloCookieManager == null) {
+                helloCookieManager = getHelloCookieManager(secureRandom);
+            }
         }
 
         return helloCookieManager;
@@ -263,78 +261,34 @@
         return statusResponseManager;
     }
 
-    abstract SSLParameters getDefaultServerSSLParams();
-    abstract SSLParameters getDefaultClientSSLParams();
-    abstract SSLParameters getSupportedSSLParams();
+    // 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 ProtocolList.
-    ProtocolList getSuportedProtocolList() {
-        if (supportedProtocolList == null) {
-            supportedProtocolList =
-                new ProtocolList(getSupportedSSLParams().getProtocols());
-        }
+    // Get supported CipherSuiteList.
+    abstract CipherSuiteList getSupportedCipherSuiteList();
 
-        return supportedProtocolList;
-    }
+    // 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();
     }
 
     /**
@@ -342,8 +296,8 @@
      * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
      */
     boolean isDefaultProtocolList(ProtocolList protocols) {
-        return (protocols == defaultServerProtocolList) ||
-               (protocols == defaultClientProtocolList);
+        return (protocols == getServerDefaultProtocolList()) ||
+               (protocols == getClientDefaultProtocolList());
     }
 
     /**
@@ -351,8 +305,8 @@
      * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
      */
     boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
-        return (cipherSuites == defaultClientCipherSuiteList) ||
-               (cipherSuites == defaultServerCipherSuiteList);
+        return (cipherSuites == getServerDefaultCipherSuiteList()) ||
+               (cipherSuites == getClientDefaultCipherSuiteList());
     }
 
     /*
@@ -405,24 +359,6 @@
         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) {
 
@@ -479,31 +415,28 @@
      * @see SSLContext
      */
     private abstract static class AbstractTLSContext extends SSLContextImpl {
-        // parameters
-        private static final SSLParameters defaultServerSSLParams;
-        private static final SSLParameters supportedSSLParams;
+        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,
@@ -511,28 +444,40 @@
                     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
-        SSLParameters getDefaultServerSSLParams() {
-            return defaultServerSSLParams;
+        ProtocolList getSuportedProtocolList() {
+            return supportedProtocolList;
         }
 
         @Override
-        SSLParameters getSupportedSSLParams() {
-            return supportedSSLParams;
+        CipherSuiteList getSupportedCipherSuiteList() {
+            return supportedCipherSuiteList;
+        }
+
+        @Override
+        ProtocolList getServerDefaultProtocolList() {
+            return serverDefaultProtocolList;
+        }
+
+        @Override
+        CipherSuiteList getServerDefaultCipherSuiteList() {
+            return serverDefaultCipherSuiteList;
         }
 
         @Override
@@ -552,30 +497,35 @@
      * @see SSLContext
      */
     public static final class TLS10Context extends AbstractTLSContext {
-        private static final SSLParameters defaultClientSSLParams;
+        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;
         }
     }
 
@@ -585,32 +535,37 @@
      * @see SSLContext
      */
     public static final class TLS11Context extends AbstractTLSContext {
-        private static final SSLParameters defaultClientSSLParams;
+        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;
         }
     }
 
@@ -620,34 +575,39 @@
      * @see SSLContext
      */
     public static final class TLS12Context extends AbstractTLSContext {
-        private static final SSLParameters defaultClientSSLParams;
+        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
-        SSLParameters getDefaultClientSSLParams() {
-            return defaultClientSSLParams;
+        ProtocolList getClientDefaultProtocolList() {
+            return clientDefaultProtocolList;
+        }
+
+        @Override
+        CipherSuiteList getClientDefaultCipherSuiteList() {
+            return clientDefaultCipherSuiteList;
         }
     }
 
@@ -719,7 +679,9 @@
      */
     private static class CustomizedTLSContext extends AbstractTLSContext {
 
-        private static final SSLParameters defaultClientSSLParams;
+        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.
@@ -766,11 +728,13 @@
                     candidates = customizedTLSProtocols.toArray(candidates);
                 }
 
-                defaultClientSSLParams = new SSLParameters();
-                defaultClientSSLParams.setProtocols(
+                clientDefaultProtocolList = new ProtocolList(
                         getAvailableProtocols(candidates));
+                clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+                    clientDefaultProtocolList, true);   // enabled only
             } else {
-                defaultClientSSLParams = null;  // unlikely to be used
+                clientDefaultProtocolList = null;       // unlikely to be used
+                clientDefaultCipherSuiteList = null;    // unlikely to be used
             }
         }
 
@@ -781,8 +745,13 @@
         }
 
         @Override
-        SSLParameters getDefaultClientSSLParams() {
-            return defaultClientSSLParams;
+        ProtocolList getClientDefaultProtocolList() {
+            return clientDefaultProtocolList;
+        }
+
+        @Override
+        CipherSuiteList getClientDefaultCipherSuiteList() {
+            return clientDefaultCipherSuiteList;
         }
     }
 
@@ -795,71 +764,53 @@
         // use the default constructor and methods
     }
 
-    /*
-     * The SSLContext implementation for default "Default" algorithm
-     *
-     * @see SSLContext
-     */
-    public static final class DefaultSSLContext extends CustomizedTLSContext {
+    // 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];
             }
         }
 
-        @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();
-            }
-            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(
@@ -956,8 +907,71 @@
                 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;
         }
     }
 
@@ -971,39 +985,50 @@
      * @see SSLContext
      */
     private abstract static class AbstractDTLSContext extends SSLContextImpl {
-        // parameters
-        private static final SSLParameters defaultServerSSLParams;
-        private static final SSLParameters supportedSSLParams;
+        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();
-
             // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
-            supportedSSLParams.setProtocols(new String[] {
+            supportedProtocolList = new ProtocolList(new String[] {
                 ProtocolVersion.DTLS10.name,
                 ProtocolVersion.DTLS12.name
             });
 
-            // candidates for available protocols
-            ProtocolVersion[] candidates = new ProtocolVersion[] {
+            // available protocols for server mode
+            serverDefaultProtocolList = new ProtocolList(
+                    getAvailableProtocols(new ProtocolVersion[] {
                 ProtocolVersion.DTLS10,
                 ProtocolVersion.DTLS12
-            };
+            }));
 
-            defaultServerSSLParams = new SSLParameters();
-            defaultServerSSLParams.setProtocols(
-                    getAvailableProtocols(candidates));
+            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;
+        }
+
+        @Override
+        ProtocolList getServerDefaultProtocolList() {
+            return serverDefaultProtocolList;
+        }
+
+        @Override
+        CipherSuiteList getServerDefaultCipherSuiteList() {
+            return serverDefaultCipherSuiteList;
         }
 
         @Override
@@ -1028,22 +1053,28 @@
      * @see SSLContext
      */
     public static final class DTLS10Context extends AbstractDTLSContext {
-        private static final SSLParameters defaultClientSSLParams;
+        private static final ProtocolList clientDefaultProtocolList;
+        private static final CipherSuiteList clientDefaultCipherSuiteList;
 
         static {
-            // candidates for available protocols
-            ProtocolVersion[] candidates = new ProtocolVersion[] {
+            // available protocols for client mode
+            clientDefaultProtocolList = new ProtocolList(
+                    getAvailableProtocols(new ProtocolVersion[] {
                 ProtocolVersion.DTLS10
-            };
+            }));
 
-            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;
         }
     }
 
@@ -1053,23 +1084,29 @@
      * @see SSLContext
      */
     public static final class DTLS12Context extends AbstractDTLSContext {
-        private static final SSLParameters defaultClientSSLParams;
+        private static final ProtocolList clientDefaultProtocolList;
+        private static final CipherSuiteList clientDefaultCipherSuiteList;
 
         static {
-            // candidates for available protocols
-            ProtocolVersion[] candidates = new ProtocolVersion[] {
+            // available protocols for client mode
+            clientDefaultProtocolList = new ProtocolList(
+                    getAvailableProtocols(new ProtocolVersion[] {
                 ProtocolVersion.DTLS10,
                 ProtocolVersion.DTLS12
-            };
+            }));
 
-            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;
         }
     }
 
@@ -1079,7 +1116,9 @@
      * @see SSLContext
      */
     private static class CustomizedDTLSContext extends AbstractDTLSContext {
-        private static final SSLParameters defaultClientSSLParams;
+        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.
@@ -1119,11 +1158,13 @@
                     candidates = customizedDTLSProtocols.toArray(candidates);
                 }
 
-                defaultClientSSLParams = new SSLParameters();
-                defaultClientSSLParams.setProtocols(
+                clientDefaultProtocolList = new ProtocolList(
                         getAvailableProtocols(candidates));
+                clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+                    clientDefaultProtocolList, true);   // enabled only
             } else {
-                defaultClientSSLParams = null;   // unlikely to be used
+                clientDefaultProtocolList = null;       // unlikely to be used
+                clientDefaultCipherSuiteList = null;    // unlikely to be used
             }
         }
 
@@ -1134,8 +1175,13 @@
         }
 
         @Override
-        SSLParameters getDefaultClientSSLParams() {
-            return defaultClientSSLParams;
+        ProtocolList getClientDefaultProtocolList() {
+            return clientDefaultProtocolList;
+        }
+
+        @Override
+        CipherSuiteList getClientDefaultCipherSuiteList() {
+            return clientDefaultCipherSuiteList;
         }
     }