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

markt 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 e6f0d10d04 Ease the migration from ciphers to ciphers plus cipherSuites
e6f0d10d04 is described below

commit e6f0d10d04df1c7b30ebf044799b5ec1f3fe0688
Author: Mark Thomas <[email protected]>
AuthorDate: Mon Mar 9 17:46:59 2026 +0000

    Ease the migration from ciphers to ciphers plus cipherSuites
---
 .../apache/tomcat/util/net/LocalStrings.properties |  2 +-
 java/org/apache/tomcat/util/net/SSLHostConfig.java | 62 +++++++++++++++-------
 .../apache/tomcat/util/net/TestSSLHostConfig.java  | 27 ++++++++--
 webapps/docs/changelog.xml                         |  9 ++++
 webapps/docs/config/http.xml                       | 12 +++--
 5 files changed, 84 insertions(+), 28 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties 
b/java/org/apache/tomcat/util/net/LocalStrings.properties
index 91e0908e48..132e07beaf 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -177,8 +177,8 @@ socketWrapper.writeTimeout=Write timeout
 sslHostConfig.certificate.notype=Multiple certificates were specified and at 
least one is missing the required attribute type
 sslHostConfig.certificateVerificationInvalid=The certificate verification 
value [{0}] is not recognised
 sslHostConfig.fileNotFound=Configured file [{0}] does not exist
+sslHostConfig.handleTls13CiphersuiteInCiphers=The TLS 1.3 cipher suite [{0}] 
included in the TLS 1.2 and below ciphers list will be removed from the TLS 1.2 
ciphers list and added to the end of the TLS 1.3 cipher suite list
 sslHostConfig.ignoreNonTls13Ciphersuite=The non-TLS 1.3 cipher suite [{0}] 
included in the TLS 1.3 cipher suite list will be ignored
-sslHostConfig.ignoreTls13Ciphersuite=The TLS 1.3 cipher suite [{0}] included 
in the TLS 1.2 and below ciphers list will be ignored
 sslHostConfig.invalid_truststore_password=The provided trust store password 
could not be used to unlock and/or validate the trust store. Retrying to access 
the trust store with a null password which will skip validation.
 sslHostConfig.mismatch=The property [{0}] was set on the SSLHostConfig named 
[{1}] and is for the [{2}] configuration syntax but the SSLHostConfig is being 
used with the [{3}] configuration syntax
 sslHostConfig.mismatch.trust=The trust configuration property [{0}] was set on 
the SSLHostConfig named [{1}] and is for the [{2}] configuration syntax but the 
SSLHostConfig is being used with the [{3}] trust configuration syntax
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java 
b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index 6ccf4f6ff1..4d441cf278 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -111,6 +111,7 @@ public class SSLHostConfig implements Serializable {
     private boolean certificateVerificationDepthConfigured = false;
     private String ciphers = DEFAULT_TLS_CIPHERS_12;
     private String cipherSuites = DEFAULT_TLS_CIPHERS_13;
+    private String cipherSuitesFromCiphers = null;
     private LinkedHashSet<Cipher> cipherList = null;
     private LinkedHashSet<Cipher> cipherSuiteList = null;
     private List<String> jsseCipherNames = null;
@@ -455,51 +456,65 @@ public class SSLHostConfig implements Serializable {
         if (ciphersList != null) {
             if (ciphersList.contains(":")) {
                 // OpenSSL format
-                StringBuilder sb = new StringBuilder();
+                StringBuilder sbCiphers = new StringBuilder();
+                StringBuilder sbCipherSuitesFromCiphers = new StringBuilder();
                 String[] components = ciphersList.split(":");
                 // Remove any TLS 1.3 cipher suites
                 for (String component : components) {
                     String trimmed = component.trim();
                     if 
(OpenSSLCipherConfigurationParser.isTls13Cipher(trimmed)) {
-                        
log.warn(sm.getString("sslHostConfig.ignoreTls13Ciphersuite", trimmed));
+                        
log.warn(sm.getString("sslHostConfig.handleTls13CiphersuiteInCiphers", 
trimmed));
+                        if (sbCipherSuitesFromCiphers.length() > 0) {
+                            sbCipherSuitesFromCiphers.append(':');
+                        }
+                        sbCipherSuitesFromCiphers.append(trimmed);
                     } else {
-                        if (sb.length() > 0) {
-                            sb.append(':');
+                        if (sbCiphers.length() > 0) {
+                            sbCiphers.append(':');
                         }
-                        sb.append(trimmed);
+                        sbCiphers.append(trimmed);
                     }
                 }
-                this.ciphers = sb.toString();
+                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 sb = new StringBuilder();
+                StringBuilder sbCiphers = new StringBuilder();
+                StringBuilder sbCipherSuitesFromCiphers = new StringBuilder();
                 String[] ciphers = ciphersList.split(",");
                 for (String cipher : ciphers) {
                     String trimmed = cipher.trim();
                     if (!trimmed.isEmpty()) {
                         if 
(OpenSSLCipherConfigurationParser.isTls13Cipher(trimmed)) {
-                            
log.warn(sm.getString("sslHostConfig.ignoreTls13Ciphersuite", trimmed));
-                            continue;
-                        }
-                        String openSSLName = 
OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed);
-                        if (openSSLName == null) {
-                            // Not a JSSE name. Maybe an OpenSSL name or alias
-                            openSSLName = trimmed;
-                        }
-                        if (sb.length() > 0) {
-                            sb.append(':');
+                            
log.warn(sm.getString("sslHostConfig.handleTls13CiphersuiteInCiphers", 
trimmed));
+                            if (sbCipherSuitesFromCiphers.length() > 0) {
+                                sbCipherSuitesFromCiphers.append(':');
+                            }
+                            sbCipherSuitesFromCiphers.append(trimmed);
+                        } else {
+                            String openSSLName = 
OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed);
+                            if (openSSLName == null) {
+                                // Not a JSSE name. Maybe an OpenSSL name or 
alias
+                                openSSLName = trimmed;
+                            }
+                            if (sbCiphers.length() > 0) {
+                                sbCiphers.append(':');
+                            }
+                            sbCiphers.append(openSSLName);
                         }
-                        sb.append(openSSLName);
                     }
                 }
-                this.ciphers = sb.toString();
+                this.ciphers = sbCiphers.toString();
+                this.cipherSuitesFromCiphers = 
sbCipherSuitesFromCiphers.toString();
             }
         } else {
             this.ciphers = null;
+            this.cipherSuitesFromCiphers = null;
         }
         this.cipherList = null;
         this.jsseCipherNames = null;
+        this.cipherSuiteList = null;
     }
 
 
@@ -585,7 +600,14 @@ public class SSLHostConfig implements Serializable {
      * @return An OpenSSL cipher suite string for the current configuration.
      */
     public String getCipherSuites() {
-        return cipherSuites;
+        StringBuilder sb = new StringBuilder(cipherSuites);
+        if (cipherSuitesFromCiphers != null && 
!cipherSuitesFromCiphers.isEmpty()) {
+            if (sb.length() > 0) {
+                sb.append(':');
+            }
+            sb.append(cipherSuitesFromCiphers);
+        }
+        return sb.toString();
     }
 
 
diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfig.java 
b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
index 9bfdbfef94..82d4d38012 100644
--- a/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
+++ b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
@@ -40,6 +40,9 @@ public class TestSSLHostConfig {
         // Single JSSE name
         hc.setCiphers(c.getJsseNames().iterator().next());
         Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
+
+        // TLS 1.3 should be using defaults
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13, 
hc.getCipherSuites());
     }
 
 
@@ -53,6 +56,9 @@ public class TestSSLHostConfig {
         hc.setCiphers(c1.getJsseNames().iterator().next() + "," +
                 c2.getJsseNames().iterator().next());
         Assert.assertEquals(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias(), 
hc.getCiphers());
+
+        // TLS 1.3 should be using defaults
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13, 
hc.getCipherSuites());
     }
 
 
@@ -62,6 +68,9 @@ public class TestSSLHostConfig {
         // Single OpenSSL alias
         hc.setCiphers("ALL");
         Assert.assertEquals("ALL", hc.getCiphers());
+
+        // TLS 1.3 should be using defaults
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13, 
hc.getCipherSuites());
     }
 
 
@@ -73,6 +82,9 @@ public class TestSSLHostConfig {
         // Single OpenSSLName name
         hc.setCiphers(c.getOpenSSLAlias());
         Assert.assertEquals(c.getOpenSSLAlias(), hc.getCiphers());
+
+        // TLS 1.3 should be using defaults
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13, 
hc.getCipherSuites());
     }
 
 
@@ -81,9 +93,12 @@ public class TestSSLHostConfig {
         SSLHostConfig hc = new SSLHostConfig();
         Cipher c = Cipher.TLS_AES_128_CCM_SHA256;
 
-        // Single TLSv1.3 name - should be filtered out
+        // Single TLSv1.3 name - should be filtered out ...
         hc.setCiphers(c.getOpenSSLAlias());
         Assert.assertEquals("", hc.getCiphers());
+
+        // ... and added to cipher suite list
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13 + ":" + 
c.getOpenSSLAlias(), hc.getCipherSuites());
     }
 
 
@@ -93,9 +108,12 @@ public class TestSSLHostConfig {
         Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256;
         Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5;
 
-        // TLSv1.3 then TLSv1.2 - TLSv1.3 name should be filtered out
+        // TLSv1.3 then TLSv1.2 - TLSv1.3 name should be filtered out ...
         hc.setCiphers(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias());
         Assert.assertEquals(c2.getOpenSSLAlias(), hc.getCiphers());
+
+        // ... and added to cipher suite list
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13 + ":" + 
c1.getOpenSSLAlias(), hc.getCipherSuites());
     }
 
 
@@ -105,9 +123,12 @@ public class TestSSLHostConfig {
         Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256;
         Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5;
 
-        // TLSv1.2 then TLSv1.3 - TLSv1.3 name should be filtered out
+        // TLSv1.2 then TLSv1.3 - TLSv1.3 name should be filtered out ...
         hc.setCiphers(c2.getOpenSSLAlias() + ":" + c1.getOpenSSLAlias());
         Assert.assertEquals(c2.getOpenSSLAlias(), hc.getCiphers());
+
+        // ... and added to cipher suite list
+        Assert.assertEquals(SSLHostConfig.DEFAULT_TLS_CIPHERS_13 + ":" + 
c1.getOpenSSLAlias(), hc.getCipherSuites());
     }
 
 
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 9b9bd7a01e..5166cf70ca 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -181,6 +181,15 @@
         <code>Integer</code> rather than a <code>Long</code> to be consistent
         with how port is exposed in the Servlet API. (markt)
       </fix>
+      <add>
+        To aid the migration from the single <code>ciphers</code> configuration
+        attribute to the use of <code>ciphers</code> and
+        <code>cipherSuites</code>, TLS 1.3 cipher suites listed in the
+        <code>ciphers</code> attribute will be removed from the
+        <code>ciphers</code> attribute and added to the end of the
+        <code>cipherSuites</code> attribute. This behaviour will be removed in
+        Tomcat 12.0.x onwards. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 485999a4e0..714ad5e705 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1492,10 +1492,14 @@
       <p>Only the TLSv1.2 and below ciphers that are supported by the SSL
       implementation will be used. Any ciphers in the list derived from a
       non-default cipher string that are not supported by the SSL 
implementation
-      or are TLSv1.3 cipher suites will be ignored and logged in a
-      <code>WARNING</code> message when the Connector starts. The warning can 
be
-      avoided by providing an explicit list of TLSv1.2 and below ciphers that
-      are supported by the configured SSL implementation.</p>
+      will be ignored and logged in a <code>WARNING</code> message when the
+      Connector starts. Any entires in the list that are TLSv1.3 cipher suites
+      will be removed from the cipher list, added to the end of the cipher 
suite
+      list (they will not be added to the cipher suite list from Tomact 12.0.x
+      onwards) and logged in a <code>WARNING</code> message when the Connector
+      starts.  The warnings can be avoided by providing an explicit list of
+      TLSv1.2 and below ciphers that are supported by the configured SSL
+      implementation.</p>
       <p>If not specified, a default (using the OpenSSL notation) of
       
<code>HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256</code>
       will be used.</p>


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

Reply via email to