ctubbsii commented on code in PR #1919:
URL: https://github.com/apache/zookeeper/pull/1919#discussion_r1336181672


##########
zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java:
##########
@@ -81,7 +87,32 @@ public abstract class X509Util implements Closeable, 
AutoCloseable {
         }
     }
 
-    public static final String DEFAULT_PROTOCOL = "TLSv1.2";
+    public static final String DEFAULT_PROTOCOL = defaultTlsProtocol();
+
+    /**
+     * Return TLSv1.3 or TLSv1.2 depending on Java runtime version being used.
+     * TLSv1.3 was first introduced in JDK11 and back-ported to OpenJDK 8u272.
+     */
+    private static String defaultTlsProtocol() {
+        String defaultProtocol = "TLSv1.2";
+        List<String> supported = new ArrayList<>();
+        try {
+            supported = 
Arrays.asList(SSLContext.getDefault().getSupportedSSLParameters().getProtocols());
+            if (supported.contains("TLSv1.3")) {
+                defaultProtocol = "TLSv1.3";
+            }
+        } catch (NoSuchAlgorithmException e) {
+            // Ignore.
+        }
+        LOG.info("Default TLS protocol is {}, supported TLS protocols are {}", 
defaultProtocol, supported);
+        return defaultProtocol;
+    }
+
+    // ChaCha20 was introduced in OpenJDK 11.0.15 and it is not supported by 
JDK8.
+    private static String[] getTLSv13Ciphers() {
+        return new String[]{"TLS_AES_256_GCM_SHA384", 
"TLS_AES_128_GCM_SHA256", "TLS_CHACHA20_POLY1305_SHA256"};
+    }

Review Comment:
   It kind of feels wrong that ZK is hard-coding lists of ciphers to use. It 
seems that users should be able to control their preferred ciphers in their 
normal Java security configuration settings. One reason a user might want to do 
that is to enforce a system-wide security policy.
   
   Filtering out these lists by what is supported by the JDK also limits the 
user to what is in these lists, rather than being able to use something newer 
that might be available in their JDK.



##########
zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java:
##########
@@ -102,6 +106,19 @@ public void testCreateSSLContextWithoutCustomProtocol(
         init(caKeyType, certKeyType, keyPassword, paramIndex);
         SSLContext sslContext = x509Util.getDefaultSSLContext();
         assertEquals(X509Util.DEFAULT_PROTOCOL, sslContext.getProtocol());
+
+        // Check that TLSv1.3 is selected in JDKs that support it (OpenJDK 
8u272 and later).
+        List<String> supported = 
Arrays.asList(SSLContext.getDefault().getSupportedSSLParameters().getProtocols());
+        if (supported.contains("TLSv1.3")) {
+            // SSLContext protocol.
+            assertEquals("TLSv1.3", sslContext.getProtocol());
+            // Enabled protocols.
+            
assertThat(Arrays.asList(sslContext.getDefaultSSLParameters().getProtocols()),
+                    Matchers.containsInAnyOrder(new String[] {"TLSv1.2", 
"TLSv1.3"}));

Review Comment:
   You don't actually need to use the hamcrest matcher here. It'd be better to 
avoid it, to help phase out its use, which probably isn't needed at all with 
JUnit5.
   
   ```suggestion
               List<String> protos = 
Arrays.asList(sslContext.getDefaultSSLParameters().getProtocols());
               assertTrue(protos.contains("TLSv1.2"));
               assertTrue(protos.contains("TLSv1.3"));
   ```



##########
zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java:
##########
@@ -90,18 +121,30 @@ private static String[] getCBCCiphers() {
         return new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"};
     }
 
-    private static String[] concatArrays(String[] left, String[] right) {
-        String[] result = new String[left.length + right.length];
-        System.arraycopy(left, 0, result, 0, left.length);
-        System.arraycopy(right, 0, result, left.length, right.length);
-        return result;
+    /**
+     * Returns a filtered set of ciphers, where ciphers not supported by the 
JDK are removed.
+     */
+    private static String[] getSupportedCiphers(String[]... cipherLists) {

Review Comment:
   This method returns an array. It'd be a bigger change to return an 
unmodifiable list, but it might be worth it in a future change, because this 
gets converted back to a list later, and it's probably better to keep it as a 
list here, then only convert it to an array when needed, rather than convert it 
to an array now and then convert it back to a list later. That could probably 
be done in a subsequent change, though, rather than making that change in this 
PR.



##########
zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java:
##########
@@ -81,7 +87,32 @@ public abstract class X509Util implements Closeable, 
AutoCloseable {
         }
     }
 
-    public static final String DEFAULT_PROTOCOL = "TLSv1.2";
+    public static final String DEFAULT_PROTOCOL = defaultTlsProtocol();
+
+    /**
+     * Return TLSv1.3 or TLSv1.2 depending on Java runtime version being used.
+     * TLSv1.3 was first introduced in JDK11 and back-ported to OpenJDK 8u272.
+     */
+    private static String defaultTlsProtocol() {
+        String defaultProtocol = "TLSv1.2";

Review Comment:
   There are a lot of places where "TLSv1.2" and "TLSv1.3" are used as String 
literals. It'd be good to make those constants somewhere, to avoid typos.



##########
zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java:
##########
@@ -90,18 +121,30 @@ private static String[] getCBCCiphers() {
         return new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"};
     }
 
-    private static String[] concatArrays(String[] left, String[] right) {
-        String[] result = new String[left.length + right.length];
-        System.arraycopy(left, 0, result, 0, left.length);
-        System.arraycopy(right, 0, result, left.length, right.length);
-        return result;
+    /**
+     * Returns a filtered set of ciphers, where ciphers not supported by the 
JDK are removed.
+     */
+    private static String[] getSupportedCiphers(String[]... cipherLists) {
+        Set<String> supported = new HashSet<>(Arrays.asList(
+            ((SSLServerSocketFactory) 
SSLServerSocketFactory.getDefault()).getSupportedCipherSuites()));

Review Comment:
   This list doesn't need to be placed inside a set. The `.contains` check will 
work on the list.
   
   ```suggestion
           List<String> supported = Arrays.asList(
               ((SSLServerSocketFactory) 
SSLServerSocketFactory.getDefault()).getSupportedCipherSuites());
   ```



##########
zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java:
##########
@@ -90,18 +121,30 @@ private static String[] getCBCCiphers() {
         return new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"};
     }
 
-    private static String[] concatArrays(String[] left, String[] right) {
-        String[] result = new String[left.length + right.length];
-        System.arraycopy(left, 0, result, 0, left.length);
-        System.arraycopy(right, 0, result, left.length, right.length);
-        return result;
+    /**
+     * Returns a filtered set of ciphers, where ciphers not supported by the 
JDK are removed.
+     */
+    private static String[] getSupportedCiphers(String[]... cipherLists) {
+        Set<String> supported = new HashSet<>(Arrays.asList(
+            ((SSLServerSocketFactory) 
SSLServerSocketFactory.getDefault()).getSupportedCipherSuites()));
+
+        List<String> chosen = new ArrayList<>();
+        for (String[] ciphers : cipherLists) {
+            for (String cipher : ciphers) {
+                if (supported.contains(cipher)) {
+                    chosen.add(cipher);
+                }
+            }
+        }
+        return chosen.toArray(new String[0]);

Review Comment:
   This is a pretty simple Stream operation that concatenates the lists, 
filters on whether the elements exist in the supported list, and then collects 
them into the list, all in one line (2 when wrapped):
   
   ```suggestion
           return 
Arrays.stream(cipherLists).flatMap(Arrays::stream).filter(supported::contains)
               .collect(Collectors.toList()).toArray(new String[0]);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscr...@zookeeper.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to