8162362: Introduce system property to control enabled ciphersuites
Reviewed-by: coffeys, mullan
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Fri Aug 12 20:14:25 2016 +0300
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Sat Aug 13 02:21:30 2016 +0000
@@ -59,6 +59,11 @@
"jdk.tls.client.enableStatusRequestExtension", true);
private final boolean serverEnableStapling = Debug.getBooleanProperty(
"jdk.tls.server.enableStatusRequestExtension", false);
+ private final static Collection<CipherSuite> clientCustomizedCipherSuites =
+ getCustomizedCipherSuites("jdk.tls.client.cipherSuites");
+ private final static Collection<CipherSuite> serverCustomizedCipherSuites =
+ getCustomizedCipherSuites("jdk.tls.server.cipherSuites");
+
private volatile StatusResponseManager statusResponseManager;
SSLContextImpl() {
@@ -336,20 +341,52 @@
return isClient ? clientEnableStapling : serverEnableStapling;
}
+
/*
- * 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()) &&
@@ -386,6 +423,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 = GetPropertyAction.privilegedGetProperty(propertyName);
+ if (debug != null && Debug.isOn("sslctx")) {
+ System.out.println(
+ "System property " + propertyName + " is set to '" +
+ property + "'");
+ }
+ if (property != null && property.length() != 0) {
+ // remove double quote marks from beginning/end of the property
+ if (property.length() > 1 && property.charAt(0) == '"' &&
+ property.charAt(property.length() - 1) == '"') {
+ property = property.substring(1, property.length() - 1);
+ }
+ }
+
+ if (property != null && property.length() != 0) {
+ String[] cipherSuiteNames = property.split(",");
+ Collection<CipherSuite> cipherSuites =
+ new ArrayList<>(cipherSuiteNames.length);
+ for (int i = 0; i < cipherSuiteNames.length; i++) {
+ cipherSuiteNames[i] = cipherSuiteNames[i].trim();
+ if (cipherSuiteNames[i].isEmpty()) {
+ continue;
+ }
+
+ CipherSuite suite;
+ try {
+ suite = CipherSuite.valueOf(cipherSuiteNames[i]);
+ } catch (IllegalArgumentException iae) {
+ if (debug != null && Debug.isOn("sslctx")) {
+ System.out.println(
+ "Unknown or unsupported cipher suite name: " +
+ cipherSuiteNames[i]);
+ }
+
+ continue;
+ }
+
+ if (suite.isAvailable()) {
+ cipherSuites.add(suite);
+ } else {
+ if (debug != null && Debug.isOn("sslctx")) {
+ System.out.println(
+ "The current installed providers do not " +
+ "support cipher suite: " + cipherSuiteNames[i]);
+ }
+ }
+ }
+
+ return cipherSuites;
+ }
+
+ return Collections.emptyList();
+ }
+
+
private static String[] getAvailableProtocols(
ProtocolVersion[] protocolCandidates) {
@@ -481,10 +579,10 @@
}));
}
- supportedCipherSuiteList = getApplicableCipherSuiteList(
- supportedProtocolList, false); // all supported
- serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
- serverDefaultProtocolList, true); // enabled only
+ supportedCipherSuiteList = getApplicableSupportedCipherSuiteList(
+ supportedProtocolList);
+ serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+ serverDefaultProtocolList, false);
}
@Override
@@ -541,8 +639,8 @@
}));
}
- clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
- clientDefaultProtocolList, true); // enabled only
+ clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+ clientDefaultProtocolList, true);
}
@Override
@@ -581,8 +679,9 @@
}));
}
- clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
- clientDefaultProtocolList, true); // enabled only
+ clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+ clientDefaultProtocolList, true);
+
}
@Override
@@ -623,8 +722,8 @@
}));
}
- clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
- clientDefaultProtocolList, true); // enabled only
+ clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+ clientDefaultProtocolList, true);
}
@Override
@@ -757,8 +856,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
@@ -1032,10 +1132,10 @@
ProtocolVersion.DTLS12
}));
- supportedCipherSuiteList = getApplicableCipherSuiteList(
- supportedProtocolList, false); // all supported
- serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
- serverDefaultProtocolList, true); // enabled only
+ supportedCipherSuiteList = getApplicableSupportedCipherSuiteList(
+ supportedProtocolList);
+ serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+ serverDefaultProtocolList, false);
}
@Override
@@ -1090,8 +1190,8 @@
ProtocolVersion.DTLS10
}));
- clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
- clientDefaultProtocolList, true); // enabled only
+ clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+ clientDefaultProtocolList, true);
}
@Override
@@ -1122,8 +1222,8 @@
ProtocolVersion.DTLS12
}));
- clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
- clientDefaultProtocolList, true); // enabled only
+ clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
+ clientDefaultProtocolList, true);
}
@Override
@@ -1187,8 +1287,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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/ssl/SSLContextImpl/CustomizedCipherSuites.java Sat Aug 13 02:21:30 2016 +0000
@@ -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;
+ }
+}