changeset 9712:1f10820808c5

8162362: Introduce system property to control enabled ciphersuites Reviewed-by: xuelei
author coffeys
date Tue, 27 Mar 2018 16:54:03 +0100
parents bbb49ffe60a3
children ba99d2bbbda5
files src/share/classes/sun/security/ssl/SSLContextImpl.java src/share/classes/sun/security/ssl/SSLEngineImpl.java src/share/classes/sun/security/ssl/SSLSocketImpl.java test/sun/security/ssl/SSLContextImpl/CustomizedCipherSuites.java
diffstat 4 files changed, 420 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/ssl/SSLContextImpl.java	Wed Feb 13 06:50:44 2019 +0000
+++ b/src/share/classes/sun/security/ssl/SSLContextImpl.java	Tue Mar 27 16:54:03 2018 +0100
@@ -53,6 +53,11 @@
     private X509TrustManager trustManager;
     private SecureRandom secureRandom;
 
+    private final static Collection<CipherSuite> clientCustomizedCipherSuites =
+            getCustomizedCipherSuites("jdk.tls.client.cipherSuites");
+    private final static Collection<CipherSuite> serverCustomizedCipherSuites =
+            getCustomizedCipherSuites("jdk.tls.server.cipherSuites");
+
     SSLContextImpl() {
         ephemeralKeyManager = new EphemeralKeyManager();
         clientCache = new SSLSessionContextImpl();
@@ -272,19 +277,50 @@
     }
 
     /*
-     * Return the list of all available CipherSuites with a priority of
-     * minPriority or above.
+     * Return the list of all available CipherSuites that are supported
+     * using currently installed providers.
+     */
+    private static CipherSuiteList getApplicableSupportedCipherSuiteList(
+            ProtocolList protocols) {
+
+        return getApplicableCipherSuiteList(
+                CipherSuite.allowedCipherSuites(),
+                protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY);
+    }
+
+    /*
+     * Return the list of all available CipherSuites that are default enabled
+     * in client or server side.
+     */
+    private static CipherSuiteList getApplicableEnabledCipherSuiteList(
+            ProtocolList protocols, boolean isClient) {
+
+        if (isClient) {
+            if (!clientCustomizedCipherSuites.isEmpty()) {
+                return getApplicableCipherSuiteList(
+                        clientCustomizedCipherSuites,
+                        protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY);
+            }
+        } else {
+            if (!serverCustomizedCipherSuites.isEmpty()) {
+                return getApplicableCipherSuiteList(
+                        serverCustomizedCipherSuites,
+                        protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY);
+            }
+        }
+
+        return getApplicableCipherSuiteList(
+                CipherSuite.allowedCipherSuites(),
+                protocols, CipherSuite.DEFAULT_SUITES_PRIORITY);
+    }
+
+    /*
+     * Return the list of available CipherSuites which are applicable to
+     * the specified protocols.
      */
     private static CipherSuiteList getApplicableCipherSuiteList(
-            ProtocolList protocols, boolean onlyEnabled) {
-
-        int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY;
-        if (onlyEnabled) {
-            minPriority = CipherSuite.DEFAULT_SUITES_PRIORITY;
-        }
-
-        Collection<CipherSuite> allowedCipherSuites =
-                                    CipherSuite.allowedCipherSuites();
+            Collection<CipherSuite> allowedCipherSuites,
+            ProtocolList protocols, int minPriority) {
 
         TreeSet<CipherSuite> suites = new TreeSet<>();
         if (!(protocols.collection().isEmpty()) &&
@@ -328,6 +364,67 @@
         return new CipherSuiteList(suites);
     }
 
+    /*
+     * Get the customized cipher suites specified by the given system property.
+     */
+    private static Collection<CipherSuite> getCustomizedCipherSuites(
+            String propertyName) {
+
+        String property = AccessController.doPrivileged(
+                    new GetPropertyAction(propertyName));
+        if (debug != null && Debug.isOn("sslctx")) {
+            System.out.println(
+                    "System property " + propertyName + " is set to '" +
+                    property + "'");
+        }
+        if (property != null && property.length() != 0) {
+            // remove double quote marks from beginning/end of the property
+            if (property.length() > 1 && property.charAt(0) == '"' &&
+                    property.charAt(property.length() - 1) == '"') {
+                property = property.substring(1, property.length() - 1);
+            }
+        }
+
+        if (property != null && property.length() != 0) {
+            String[] cipherSuiteNames = property.split(",");
+            Collection<CipherSuite> cipherSuites =
+                        new ArrayList<>(cipherSuiteNames.length);
+            for (int i = 0; i < cipherSuiteNames.length; i++) {
+                cipherSuiteNames[i] = cipherSuiteNames[i].trim();
+                if (cipherSuiteNames[i].isEmpty()) {
+                    continue;
+                }
+
+                CipherSuite suite;
+                try {
+                    suite = CipherSuite.valueOf(cipherSuiteNames[i]);
+                } catch (IllegalArgumentException iae) {
+                    if (debug != null && Debug.isOn("sslctx")) {
+                        System.out.println(
+                                "Unknown or unsupported cipher suite name: " +
+                                cipherSuiteNames[i]);
+                    }
+
+                    continue;
+                }
+
+                if (suite.isAvailable()) {
+                    cipherSuites.add(suite);
+                } else {
+                    if (debug != null && Debug.isOn("sslctx")) {
+                        System.out.println(
+                                "The current installed providers do not " +
+                                "support cipher suite: " + cipherSuiteNames[i]);
+                    }
+                }
+            }
+
+            return cipherSuites;
+        }
+
+        return Collections.emptyList();
+    }
+
     private static String[] getAvailableProtocols(
             ProtocolVersion[] protocolCandidates) {
 
@@ -422,10 +519,10 @@
                 }));
             }
 
-            supportedCipherSuiteList = getApplicableCipherSuiteList(
-                    supportedProtocolList, false);          // all supported
-            serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
-                    serverDefaultProtocolList, true);       // enabled only
+            supportedCipherSuiteList = getApplicableSupportedCipherSuiteList(
+                    supportedProtocolList);
+            serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+                    serverDefaultProtocolList, false);
         }
 
         @Override
@@ -482,8 +579,8 @@
                 }));
             }
 
-            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
-                    clientDefaultProtocolList, true);       // enabled only
+            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+                    clientDefaultProtocolList, true);
         }
 
         @Override
@@ -522,8 +619,9 @@
                 }));
             }
 
-            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
-                    clientDefaultProtocolList, true);       // enabled only
+            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+                    clientDefaultProtocolList, true);
+
         }
 
         @Override
@@ -564,8 +662,8 @@
                 }));
             }
 
-            clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
-                    clientDefaultProtocolList, true);       // enabled only
+            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+                    clientDefaultProtocolList, true);
         }
 
         @Override
@@ -696,8 +794,9 @@
 
                 clientDefaultProtocolList = new ProtocolList(
                         getAvailableProtocols(candidates));
-                clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
-                        clientDefaultProtocolList, true);   // enabled only
+                clientDefaultCipherSuiteList =
+                        getApplicableEnabledCipherSuiteList(
+                                clientDefaultProtocolList, true);
             } else {
                 clientDefaultProtocolList = null;       // unlikely to be used
                 clientDefaultCipherSuiteList = null;    // unlikely to be used
--- a/src/share/classes/sun/security/ssl/SSLEngineImpl.java	Wed Feb 13 06:50:44 2019 +0000
+++ b/src/share/classes/sun/security/ssl/SSLEngineImpl.java	Tue Mar 27 16:54:03 2018 +0100
@@ -1884,13 +1884,21 @@
 
         case cs_START:
             /*
-             * If we need to change the engine mode and the enabled
-             * protocols haven't specifically been set by the user,
-             * change them to the corresponding default ones.
+             * If we need to change the socket mode and the enabled
+             * protocols and cipher suites haven't specifically been
+             * set by the user, change them to the corresponding
+             * default ones.
              */
-            if (roleIsServer != (!flag) &&
-                    sslContext.isDefaultProtocolList(enabledProtocols)) {
-                enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+            if (roleIsServer != (!flag)) {
+                if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+                    enabledProtocols =
+                            sslContext.getDefaultProtocolList(!flag);
+                }
+
+                if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
+                    enabledCipherSuites =
+                            sslContext.getDefaultCipherSuiteList(!flag);
+                }
             }
 
             roleIsServer = !flag;
--- a/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Wed Feb 13 06:50:44 2019 +0000
+++ b/src/share/classes/sun/security/ssl/SSLSocketImpl.java	Tue Mar 27 16:54:03 2018 +0100
@@ -2315,13 +2315,22 @@
         case cs_START:
             /*
              * If we need to change the socket mode and the enabled
-             * protocols haven't specifically been set by the user,
-             * change them to the corresponding default ones.
+             * protocols and cipher suites haven't specifically been
+             * set by the user, change them to the corresponding
+             * default ones.
              */
-            if (roleIsServer != (!flag) &&
-                    sslContext.isDefaultProtocolList(enabledProtocols)) {
-                enabledProtocols = sslContext.getDefaultProtocolList(!flag);
+            if (roleIsServer != (!flag)) {
+                if (sslContext.isDefaultProtocolList(enabledProtocols)) {
+                    enabledProtocols =
+                            sslContext.getDefaultProtocolList(!flag);
+                }
+
+                if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
+                    enabledCipherSuites =
+                            sslContext.getDefaultCipherSuiteList(!flag);
+                }
             }
+
             roleIsServer = !flag;
             break;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/SSLContextImpl/CustomizedCipherSuites.java	Tue Mar 27 16:54:03 2018 +0100
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8162362
+ * @summary Cannot enable previously default enabled cipher suites
+ * @run main/othervm
+ *      CustomizedCipherSuites Default true
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm
+ *      -Djdk.tls.client.cipherSuites="unknown"
+ *      CustomizedCipherSuites Default true
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm
+ *      -Djdk.tls.client.cipherSuites=""
+ *      CustomizedCipherSuites Default true
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm
+ *      -Djdk.tls.client.cipherSuites="SSL_RSA_WITH_DES_CBC_SHA"
+ *      CustomizedCipherSuites Default true
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm
+ *      -Djdk.tls.server.cipherSuites="SSL_RSA_WITH_DES_CBC_SHA"
+ *      CustomizedCipherSuites Default false
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ * @run main/othervm
+ *      -Djdk.tls.client.cipherSuites="TLS_RSA_WITH_AES_128_CBC_SHA,unknown,SSL_RSA_WITH_DES_CBC_SHA"
+ *      CustomizedCipherSuites Default true
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ *      ""
+ * @run main/othervm
+ *      -Djdk.tls.server.cipherSuites="TLS_RSA_WITH_AES_128_CBC_SHA,unknown,SSL_RSA_WITH_DES_CBC_SHA"
+ *      CustomizedCipherSuites Default false
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ *      ""
+ * @run main/othervm
+ *      -Djdk.tls.server.cipherSuites="SSL_RSA_WITH_DES_CBC_SHA"
+ *      CustomizedCipherSuites Default true
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ * @run main/othervm
+ *      -Djdk.tls.client.cipherSuites="SSL_RSA_WITH_DES_CBC_SHA"
+ *      CustomizedCipherSuites Default false
+ *      TLS_RSA_WITH_AES_128_CBC_SHA
+ *      SSL_RSA_WITH_DES_CBC_SHA
+ */
+
+import javax.net.ssl.*;
+
+/**
+ * Test the customized default cipher suites.
+ *
+ * This test is based on the behavior that SSL_RSA_WITH_DES_CBC_SHA is
+ * disabled by default, and TLS_RSA_WITH_AES_128_CBC_SHA is enabled by
+ * default in JDK.  If the behavior is changed in the future, please
+ * update the test cases above accordingly.
+ */
+public class CustomizedCipherSuites {
+
+    private static String contextProtocol;
+    private static boolean isClientMode;
+
+    private static String enabledCipherSuite;
+    private static String disabledCipherSuite;
+
+    public static void main(String[] args) throws Exception {
+
+        contextProtocol = trimQuotes(args[0]);
+        isClientMode = Boolean.parseBoolean(args[1]);
+        enabledCipherSuite = trimQuotes(args[2]);
+        disabledCipherSuite = trimQuotes(args[3]);
+
+        //
+        // Create instance of SSLContext with the specified protocol.
+        //
+        SSLContext context = SSLContext.getInstance(contextProtocol);
+
+        // Default SSLContext is initialized automatically.
+        if (!contextProtocol.equals("Default")) {
+            // Use default TK, KM and random.
+            context.init((KeyManager[])null, (TrustManager[])null, null);
+        }
+
+        // SSLContext default parameters is client mode in JDK.
+        if (isClientMode) {
+            //
+            // Check default parameters of the specified SSLContext protocol
+            //
+            SSLParameters parameters = context.getDefaultSSLParameters();
+            System.out.println("Checking SSLContext default parameters ...");
+            checkEnabledCiphers(parameters.getCipherSuites());
+        }
+
+        //
+        // Check supported parameters of the specified SSLContext protocol
+        //
+        SSLParameters parameters = context.getSupportedSSLParameters();
+        System.out.println("Checking SSLContext suppport parameters ...");
+        checkSupportedCiphers(parameters.getCipherSuites());
+
+
+        //
+        // Check the default cipher suites of SSLEngine.
+        //
+        SSLEngine engine = context.createSSLEngine();
+        engine.setUseClientMode(isClientMode);
+
+        System.out.println("Checking SSLEngine default cipher suites ...");
+        checkEnabledCiphers(engine.getEnabledCipherSuites());
+
+        //
+        // Check the supported cipher suites of SSLEngine.
+        //
+        System.out.println("Checking SSLEngine supported cipher suites ...");
+        checkSupportedCiphers(engine.getSupportedCipherSuites());
+
+        if (isClientMode) {
+            SSLSocketFactory factory = context.getSocketFactory();
+            // Use an unconnected socket.
+            try (SSLSocket socket = (SSLSocket)factory.createSocket()) {
+                //
+                // Check the default cipher suites of SSLSocket.
+                //
+                System.out.println(
+                        "Checking SSLSocket default cipher suites ...");
+                checkEnabledCiphers(socket.getEnabledCipherSuites());
+
+                //
+                // Check the supported cipher suites of SSLSocket.
+                //
+                System.out.println(
+                        "Checking SSLSocket supported cipher suites ...");
+                checkSupportedCiphers(socket.getSupportedCipherSuites());
+            }
+        } else {
+            SSLServerSocketFactory factory = context.getServerSocketFactory();
+            // Use an unbound server socket.
+            try (SSLServerSocket socket =
+                    (SSLServerSocket)factory.createServerSocket()) {
+                //
+                // Check the default cipher suites of SSLServerSocket.
+                //
+                System.out.println(
+                        "Checking SSLServerSocket default cipher suites ...");
+                checkEnabledCiphers(socket.getEnabledCipherSuites());
+
+                //
+                // Check the supported cipher suites of SSLServerSocket.
+                //
+                System.out.println(
+                        "Checking SSLServerSocket supported cipher suites ...");
+                checkSupportedCiphers(socket.getSupportedCipherSuites());
+            }
+        }
+
+        System.out.println("\t... Success");
+    }
+
+    private static void checkEnabledCiphers(
+            String[] ciphers) throws Exception {
+
+        if (ciphers.length == 0) {
+            throw new Exception("No default cipher suites");
+        }
+
+        boolean isMatch = false;
+        if (enabledCipherSuite.isEmpty()) {
+            // Don't check if not specify the expected cipher suite.
+            isMatch = true;
+        }
+
+        boolean isBroken = false;
+        for (String cipher : ciphers) {
+            System.out.println("\tdefault cipher suite " + cipher);
+            if (!enabledCipherSuite.isEmpty() &&
+                        cipher.equals(enabledCipherSuite)) {
+                isMatch = true;
+            }
+
+            if (!disabledCipherSuite.isEmpty() &&
+                        cipher.equals(disabledCipherSuite)) {
+                isBroken = true;
+            }
+        }
+
+        if (!isMatch) {
+            throw new Exception(
+                "Cipher suite " + enabledCipherSuite + " should be enabled");
+        }
+
+        if (isBroken) {
+            throw new Exception(
+                "Cipher suite " + disabledCipherSuite + " should be disabled");
+        }
+    }
+
+    private static void checkSupportedCiphers(
+            String[] ciphers) throws Exception {
+
+        if (ciphers.length == 0) {
+            throw new Exception("No supported cipher suites");
+        }
+
+        boolean hasEnabledCipherSuite = enabledCipherSuite.isEmpty();
+        boolean hasDisabledCipherSuite = disabledCipherSuite.isEmpty();
+        for (String cipher : ciphers) {
+            System.out.println("\tsupported cipher suite " + cipher);
+            if (!enabledCipherSuite.isEmpty() &&
+                        cipher.equals(enabledCipherSuite)) {
+                hasEnabledCipherSuite = true;
+            }
+
+            if (!disabledCipherSuite.isEmpty() &&
+                        cipher.equals(disabledCipherSuite)) {
+                hasDisabledCipherSuite = true;
+            }
+        }
+
+        if (!hasEnabledCipherSuite) {
+            throw new Exception(
+                "Cipher suite " + enabledCipherSuite + " should be supported");
+        }
+
+        if (!hasDisabledCipherSuite) {
+            throw new Exception(
+                "Cipher suite " + disabledCipherSuite + " should be supported");
+        }
+    }
+
+    private static String trimQuotes(String candidate) {
+        if (candidate != null && candidate.length() != 0) {
+            // Remove double quote marks from beginning/end of the string.
+            if (candidate.length() > 1 && candidate.charAt(0) == '"' &&
+                    candidate.charAt(candidate.length() - 1) == '"') {
+                return candidate.substring(1, candidate.length() - 1);
+            }
+        }
+
+        return candidate;
+    }
+}