This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new cd9ec89d5c Review "Fix TLS 1.3 cipher suite configuration"
cd9ec89d5c is described below

commit cd9ec89d5c3c211c1f453cbf5fa28dda0ed36297
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Nov 3 21:11:46 2026 +0000

    Review "Fix TLS 1.3 cipher suite configuration"
    
    Make null reset the ciphers to the defaults.
---
 java/org/apache/tomcat/util/net/SSLHostConfig.java | 43 +++++++++---------
 .../apache/tomcat/util/net/TestSSLHostConfig.java  | 51 +++++++++++++++++++++-
 2 files changed, 70 insertions(+), 24 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java 
b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index a8c1371258..2b16861c07 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -177,11 +177,11 @@ public class SSLHostConfig implements Serializable {
     /**
      * The cipher configuration for TLS 1.2 and below (OpenSSL format).
      */
-    private String ciphers = DEFAULT_TLS_CIPHERS_12;
+    private String ciphers = null;
     /**
      * The cipher suite configuration for TLS 1.3.
      */
-    private String cipherSuites = DEFAULT_TLS_CIPHERS_13;
+    private String cipherSuites = null;
     private String cipherSuitesFromCiphers = null;
     /**
      * The parsed cipher list for TLS 1.2 and below.
@@ -785,16 +785,17 @@ public class SSLHostConfig implements Serializable {
      * Set the new cipher (TLSv1.2 and below) configuration. Note: Regardless 
of the format used to set the
      * configuration, it is always stored in OpenSSL format.
      *
-     * @param ciphersList The new cipher configuration in OpenSSL or JSSE 
format
+     * @param ciphersList The new cipher configuration in OpenSSL or JSSE 
format. A value of {@code null} restores the
+     *                        default, {@link #DEFAULT_TLS_CIPHERS_12}.
      */
     public void setCiphers(String ciphersList) {
         // Ciphers is stored in OpenSSL format. Convert the provided value if
         // necessary.
         if (ciphersList != null) {
+            StringBuilder sbCiphers = new StringBuilder();
+            StringBuilder sbCipherSuitesFromCiphers = new StringBuilder();
             if (ciphersList.contains(":")) {
                 // OpenSSL format
-                StringBuilder sbCiphers = new StringBuilder();
-                StringBuilder sbCipherSuitesFromCiphers = new StringBuilder();
                 String[] components = ciphersList.split(":");
                 // Remove any TLS 1.3 cipher suites
                 for (String component : components) {
@@ -812,13 +813,9 @@ public class SSLHostConfig implements Serializable {
                         sbCiphers.append(trimmed);
                     }
                 }
-                this.ciphers = sbCiphers.toString();
-                this.cipherSuitesFromCiphers = 
sbCipherSuitesFromCiphers.toString();
             } else {
                 // Not obviously in OpenSSL format. Might be a single OpenSSL 
or JSSE
                 // cipher name. Might be a comma separated list of cipher names
-                StringBuilder sbCiphers = new StringBuilder();
-                StringBuilder sbCipherSuitesFromCiphers = new StringBuilder();
                 String[] ciphers = ciphersList.split(",");
                 for (String cipher : ciphers) {
                     String trimmed = JreCompat.getInstance().strip(cipher);
@@ -842,9 +839,9 @@ public class SSLHostConfig implements Serializable {
                         }
                     }
                 }
-                this.ciphers = sbCiphers.toString();
-                this.cipherSuitesFromCiphers = 
sbCipherSuitesFromCiphers.toString();
             }
+            this.ciphers = sbCiphers.toString();
+            this.cipherSuitesFromCiphers = 
sbCipherSuitesFromCiphers.toString();
         } else {
             this.ciphers = null;
             this.cipherSuitesFromCiphers = null;
@@ -858,9 +855,13 @@ public class SSLHostConfig implements Serializable {
     /**
      * Returns the cipher (TLSv1.2 and below) configuration.
      *
-     * @return An OpenSSL cipher string for the current configuration.
+     * @return An OpenSSL cipher string for the current configuration. If no 
configuration has been set, or it was set
+     *             to {@code null}, this is {@link #DEFAULT_TLS_CIPHERS_12}.
      */
     public String getCiphers() {
+        if (ciphers == null) {
+            return DEFAULT_TLS_CIPHERS_12;
+        }
         return ciphers;
     }
 
@@ -899,11 +900,13 @@ public class SSLHostConfig implements Serializable {
      * Set the cipher suite (TLSv1.3) configuration. Note: Regardless of the 
format used to set the configuration, it is
      * always stored in OpenSSL format.
      *
-     * @param cipherSuites The cipher suites to use in a colon-separated, 
preference order list
+     * @param cipherSuites The cipher suites to use in a preference order 
list. The list may be colon-separated
+     *                         (OpenSSL format) or comma-separated (JSSE 
format). A value of {@code null} restores the
+     *                         default, {@link #DEFAULT_TLS_CIPHERS_13}.
      */
     public void setCipherSuites(String cipherSuites) {
         if (cipherSuites == null) {
-            // As for setCiphers(), a null value is stored as provided.
+            // As for setCiphers(), a null value restores the default.
             this.cipherSuites = null;
             this.cipherSuiteList = null;
             this.jsseCipherNames = null;
@@ -952,17 +955,13 @@ public class SSLHostConfig implements Serializable {
     /**
      * Obtain the current cipher suite (TLSv1.3) configuration.
      *
-     * @return An OpenSSL cipher suite string for the current configuration
-     *         or {@code null} if no cipher suites have been configured.
+     * @return An OpenSSL cipher suite string for the current configuration. 
If no configuration has been set, or it
+     *             was set to {@code null}, this is {@link 
#DEFAULT_TLS_CIPHERS_13}.
      */
     public String getCipherSuites() {
-        if (cipherSuites == null && cipherSuitesFromCiphers == null) {
-            return null;
-        }
         StringBuilder sb = new StringBuilder();
-        if (cipherSuites != null) {
-            sb.append(cipherSuites);
-        }
+        // The default applies unless cipher suites have been configured 
explicitly
+        sb.append(cipherSuites != null ? cipherSuites : 
DEFAULT_TLS_CIPHERS_13);
         if (cipherSuitesFromCiphers != null && 
!cipherSuitesFromCiphers.isEmpty()) {
             if (sb.length() > 0) {
                 sb.append(':');
diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfig.java 
b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
index 9a9941d9b7..f492a42e28 100644
--- a/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
+++ b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
@@ -186,9 +186,56 @@ public class TestSSLHostConfig {
     public void testCiphersuite05() {
         SSLHostConfig hc = new SSLHostConfig();
 
-        // Null should be handled without an error, as it is for setCiphers()
+        // Null restores the default, as it does for setCiphers()
+        hc.setCipherSuites(Cipher.TLS_AES_128_CCM_SHA256.getOpenSSLAlias());
         hc.setCipherSuites(null);
-        Assert.assertNull(hc.getCipherSuites());
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13, 
hc.getCipherSuites());
+        // and the derived values must be usable
+        Assert.assertFalse(hc.getJsseCipherNames().isEmpty());
+    }
+
+
+    @Test
+    public void testCiphersWithoutTls13KeepDefaultCipherSuites() {
+        // Setting ciphers that do not include any TLS 1.3 cipher suites must 
not remove the default TLS 1.3 cipher
+        // suites, whatever the format of the ciphers list
+        String[] ciphersLists = { 
Cipher.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.getOpenSSLAlias(),
+                Cipher.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.getOpenSSLAlias() 
+ ":" +
+                        
Cipher.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.getOpenSSLAlias(),
+                Cipher.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.getOpenSSLAlias() 
+ "," +
+                        
Cipher.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.getOpenSSLAlias(),
+                "" };
+
+        for (String ciphersList : ciphersLists) {
+            SSLHostConfig hc = new SSLHostConfig();
+            hc.setCiphers(ciphersList);
+            Assert.assertEquals(ciphersList, 
SSLHostConfig.DEFAULT_TLS_CIPHERS_13, hc.getCipherSuites());
+        }
+    }
+
+
+    @Test
+    public void testCiphersWithoutTls13KeepJsseCipherSuites() {
+        SSLHostConfig hc = new SSLHostConfig();
+
+        // A single TLS 1.2 cipher, which is not in OpenSSL (colon separated) 
format
+        
hc.setCiphers(Cipher.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.getOpenSSLAlias());
+        String tls13Name = 
Cipher.TLS_AES_256_GCM_SHA384.getJsseNames().iterator().next();
+        Assert.assertTrue(hc.getJsseCipherNames().contains(tls13Name));
+    }
+
+
+    @Test
+    public void testCiphersNull() {
+        SSLHostConfig hc = new SSLHostConfig();
+
+        // Null restores the default
+        
hc.setCiphers(Cipher.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.getOpenSSLAlias());
+        hc.setCiphers(null);
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_12, 
hc.getCiphers());
+        // and the derived values must be usable
+        Assert.assertFalse(hc.getCipherList().isEmpty());
+        Assert.assertFalse(hc.getJsseCipherNames().isEmpty());
     }
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to