You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2026/01/12 17:17:57 UTC
(tomcat) branch 10.1.x updated (bd9c0a4f9b -> 4ee45c5571)
This is an automated email from the ASF dual-hosted git repository.
markt pushed a change to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
from bd9c0a4f9b Update to Tomcat Native 2.0.12
new 4410284785 Update minimum Tomcat Native version to 1.3.4/2.0.12
new 4ee45c5571 Add support for setting TLS 1.3 cipher suites
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../apache/catalina/core/AprLifecycleListener.java | 54 ++++++--
.../apache/catalina/core/LocalStrings.properties | 4 +-
.../apache/tomcat/util/net/LocalStrings.properties | 2 +
java/org/apache/tomcat/util/net/SSLHostConfig.java | 131 ++++++++++++++++---
.../tomcat/util/net/openssl/OpenSSLContext.java | 1 +
.../util/net/openssl/panama/OpenSSLContext.java | 6 +-
.../apache/tomcat/util/net/TestSSLHostConfig.java | 70 ++++++++++
.../tomcat/util/net/TestSSLHostConfigCipher.java | 144 +++++++++++++++++++++
test/org/apache/tomcat/util/net/TesterSupport.java | 11 ++
webapps/docs/changelog.xml | 19 +++
webapps/docs/config/http.xml | 44 +++++--
11 files changed, 440 insertions(+), 46 deletions(-)
create mode 100644 test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
(tomcat) 01/02: Update minimum Tomcat Native version to 1.3.4/2.0.12
Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 44102847855f1f4a579a0dcfa1325fd9e58f4404
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Jan 12 15:55:30 2026 +0000
Update minimum Tomcat Native version to 1.3.4/2.0.12
This is in preparation for adding TLS 1.3 config support and expanding
the OCSP config support
---
.../apache/catalina/core/AprLifecycleListener.java | 54 +++++++++++++++++-----
.../apache/catalina/core/LocalStrings.properties | 4 +-
webapps/docs/changelog.xml | 6 +++
3 files changed, 52 insertions(+), 12 deletions(-)
diff --git a/java/org/apache/catalina/core/AprLifecycleListener.java b/java/org/apache/catalina/core/AprLifecycleListener.java
index bf047c362d..d10d9591fe 100644
--- a/java/org/apache/catalina/core/AprLifecycleListener.java
+++ b/java/org/apache/catalina/core/AprLifecycleListener.java
@@ -64,12 +64,17 @@ public class AprLifecycleListener implements LifecycleListener {
// ---------------------------------------------- Constants
- protected static final int TCN_REQUIRED_MAJOR = 1;
- protected static final int TCN_REQUIRED_MINOR = 2;
- protected static final int TCN_REQUIRED_PATCH = 34;
+ private static final int TCN_1_REQUIRED_MINOR = 3;
+ private static final int TCN_1_REQUIRED_PATCH = 4;
+ private static final int TCN_1_RECOMMENDED_MINOR = 3;
+ private static final int TCN_1_RECOMMENDED_PATCH = 4;
+
+ protected static final int TCN_REQUIRED_MAJOR = 2;
+ protected static final int TCN_REQUIRED_MINOR = 0;
+ protected static final int TCN_REQUIRED_PATCH = 12;
protected static final int TCN_RECOMMENDED_MAJOR = 2;
protected static final int TCN_RECOMMENDED_MINOR = 0;
- protected static final int TCN_RECOMMENDED_PV = 5;
+ protected static final int TCN_RECOMMENDED_PV = 12;
// ---------------------------------------------- Properties
@@ -209,9 +214,6 @@ public class AprLifecycleListener implements LifecycleListener {
}
private static void init() {
- int rqver = TCN_REQUIRED_MAJOR * 1000 + TCN_REQUIRED_MINOR * 100 + TCN_REQUIRED_PATCH;
- int rcver = TCN_RECOMMENDED_MAJOR * 1000 + TCN_RECOMMENDED_MINOR * 100 + TCN_RECOMMENDED_PV;
-
if (org.apache.tomcat.jni.AprStatus.isAprInitialized()) {
return;
}
@@ -249,9 +251,34 @@ public class AprLifecycleListener implements LifecycleListener {
}
return;
}
+
+ /*
+ * With parallel development of 1.x and 2.x there are now minimum and recommended versions for both branches.
+ *
+ * The minimum required version is increased when the Tomcat Native API is changed (typically extended) to
+ * include functionality that Tomcat expects to always be present.
+ *
+ * The minimum recommended version is increased when there is a change in Tomcat Native that while not required
+ * is recommended (such as bug fixes).
+ */
+ int rqver;
+ int rcver;
+ if (tcnMajor == 1) {
+ rqver = 1000 + TCN_1_REQUIRED_MINOR * 100 + TCN_1_REQUIRED_PATCH;
+ rcver = 1000 + TCN_1_RECOMMENDED_MINOR * 100 + TCN_1_RECOMMENDED_PATCH;
+ } else {
+ rqver = TCN_REQUIRED_MAJOR * 1000 + TCN_REQUIRED_MINOR * 100 + TCN_REQUIRED_PATCH;
+ rcver = TCN_RECOMMENDED_MAJOR * 1000 + TCN_RECOMMENDED_MINOR * 100 + TCN_RECOMMENDED_PV;
+ }
+
if (tcnVersion < rqver) {
- log.error(sm.getString("aprListener.tcnInvalid", Library.versionString(),
- TCN_REQUIRED_MAJOR + "." + TCN_REQUIRED_MINOR + "." + TCN_REQUIRED_PATCH));
+ if (tcnMajor == 1) {
+ log.error(sm.getString("aprListener.tcnInvalid.1", Library.versionString(),
+ "1." + TCN_1_REQUIRED_MINOR + "." + TCN_1_REQUIRED_PATCH));
+ } else {
+ log.error(sm.getString("aprListener.tcnInvalid", Library.versionString(),
+ TCN_REQUIRED_MAJOR + "." + TCN_REQUIRED_MINOR + "." + TCN_REQUIRED_PATCH));
+ }
try {
// Terminate the APR in case the version
// is below required.
@@ -263,8 +290,13 @@ public class AprLifecycleListener implements LifecycleListener {
return;
}
if (tcnVersion < rcver) {
- initInfoLogMessages.add(sm.getString("aprListener.tcnVersion", Library.versionString(),
- TCN_RECOMMENDED_MAJOR + "." + TCN_RECOMMENDED_MINOR + "." + TCN_RECOMMENDED_PV));
+ if (tcnMajor == 1) {
+ initInfoLogMessages.add(sm.getString("aprListener.tcnVersion.1", Library.versionString(),
+ "1." + TCN_1_RECOMMENDED_MINOR + "." + TCN_1_RECOMMENDED_PATCH));
+ } else {
+ initInfoLogMessages.add(sm.getString("aprListener.tcnVersion", Library.versionString(),
+ TCN_RECOMMENDED_MAJOR + "." + TCN_RECOMMENDED_MINOR + "." + TCN_RECOMMENDED_PV));
+ }
}
initInfoLogMessages
diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index 7258f44a23..df0e718344 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -90,9 +90,11 @@ aprListener.requireNotInFIPSMode=AprLifecycleListener is configured to require t
aprListener.skipFIPSInitialization=Already in FIPS mode; skipping FIPS initialization.
aprListener.sslInit=Failed to initialize the SSLEngine.
aprListener.sslRequired=[{0}] is not a valid value for SSLEngine when using version [{1}] of the Tomcat Native library since SSL is required for version 2.x onwards.
-aprListener.tcnInvalid=An incompatible version [{0}] of the Apache Tomcat Native library is installed, while Tomcat requires version [{1}]
+aprListener.tcnInvalid=An incompatible version [{0}] of the Apache Tomcat Native library is installed, while Tomcat requires at least version [{1}]
+aprListener.tcnInvalid.1=An incompatible version [{0}] of the Apache Tomcat Native library is installed, while Tomcat requires at least version [{1}] of Tomcat Native 1.x
aprListener.tcnValid=Loaded Apache Tomcat Native library [{0}] using APR version [{1}].
aprListener.tcnVersion=An older version [{0}] of the Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of [{1}]
+aprListener.tcnVersion.1=An older version [{0}] of the Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of [{1}] of Tomcat Native 1.x
aprListener.tooLateForFIPSMode=Cannot setFIPSMode: SSL has already been initialized
aprListener.tooLateForSSLEngine=Cannot setSSLEngine: SSL has already been initialized
aprListener.tooLateForSSLRandomSeed=Cannot setSSLRandomSeed: SSL has already been initialized
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ba7919093c..ad25e4f943 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -121,6 +121,12 @@
<bug>69918</bug>: Ensure request parameters are correctly parsed for HTTP/2 requests
when the content-length header is not set. (dsoumis)
</fix>
+ <update>
+ Enable minimum and recommended Tomcat Native versions to be set
+ separately for Tomcat Native 1.x and 2.x. Update the minimum and
+ recommended versions for Tomcat Native 1.x to 1.3.4. Update the minimum
+ and recommended versions for Tomcat Native 2.x to 2.0.12. (markt)
+ </update>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
(tomcat) 02/02: Add support for setting TLS 1.3 cipher suites
Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 4ee45c557168cb3051c09ad8e37f6d8ea475a44a
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Jan 6 08:12:52 2026 +0000
Add support for setting TLS 1.3 cipher suites
For consistency between OpenSSL and JSSE TLS implementations, TLSv.13
cipher
suites included in the ciphers attribute of an SSLHostConfig are now
always
ignored (previously they would be ignored with OpenSSL implementations
and
used with JSSE implementations) and a warning is logged that the cipher
suite
has been ignored.
---
.../apache/tomcat/util/net/LocalStrings.properties | 2 +
java/org/apache/tomcat/util/net/SSLHostConfig.java | 131 ++++++++++++++++---
.../tomcat/util/net/openssl/OpenSSLContext.java | 1 +
.../util/net/openssl/panama/OpenSSLContext.java | 6 +-
.../apache/tomcat/util/net/TestSSLHostConfig.java | 70 ++++++++++
.../tomcat/util/net/TestSSLHostConfigCipher.java | 144 +++++++++++++++++++++
test/org/apache/tomcat/util/net/TesterSupport.java | 11 ++
webapps/docs/changelog.xml | 13 ++
webapps/docs/config/http.xml | 44 +++++--
9 files changed, 388 insertions(+), 34 deletions(-)
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties
index f9eb84189e..ad39a0a83e 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -154,6 +154,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.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 a65a4e6e60..61c0efcb04 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -109,8 +109,10 @@ public class SSLHostConfig implements Serializable {
private int certificateVerificationDepth = 10;
// Used to track if certificateVerificationDepth has been explicitly set
private boolean certificateVerificationDepthConfigured = false;
- private String ciphers = DEFAULT_TLS_CIPHERS;
+ private String ciphers = DEFAULT_TLS_CIPHERS_12;
+ private String cipherSuites = DEFAULT_TLS_CIPHERS_13;
private LinkedHashSet<Cipher> cipherList = null;
+ private LinkedHashSet<Cipher> cipherSuiteList = null;
private List<String> jsseCipherNames = null;
private boolean honorCipherOrder = false;
private final Set<String> protocols = new HashSet<>();
@@ -381,36 +383,59 @@ public class SSLHostConfig implements Serializable {
/**
- * Set the new cipher configuration. Note: Regardless of the format used to set the configuration, it is always
- * stored in OpenSSL format.
+ * 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
*/
public void setCiphers(String ciphersList) {
// Ciphers is stored in OpenSSL format. Convert the provided value if
// necessary.
- if (ciphersList != null && !ciphersList.contains(":")) {
- StringBuilder sb = new StringBuilder();
- // Not obviously in OpenSSL format. Might be a single OpenSSL or JSSE
- // cipher name. Might be a comma separated list of cipher names
- String[] ciphers = ciphersList.split(",");
- for (String cipher : ciphers) {
- String trimmed = cipher.trim();
- if (!trimmed.isEmpty()) {
- String openSSLName = OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed);
- if (openSSLName == null) {
- // Not a JSSE name. Maybe an OpenSSL name or alias
- openSSLName = trimmed;
+ if (ciphersList != null) {
+ if (ciphersList.contains(":")) {
+ // OpenSSL format
+ StringBuilder sb = 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));
+ } else {
+ if (sb.length() > 0) {
+ sb.append(':');
+ }
+ sb.append(trimmed);
}
- if (sb.length() > 0) {
- sb.append(':');
+ }
+ this.ciphers = sb.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();
+ 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(':');
+ }
+ sb.append(openSSLName);
}
- sb.append(openSSLName);
}
+ this.ciphers = sb.toString();
}
- this.ciphers = sb.toString();
} else {
- this.ciphers = ciphersList;
+ this.ciphers = null;
}
this.cipherList = null;
this.jsseCipherNames = null;
@@ -441,12 +466,76 @@ public class SSLHostConfig implements Serializable {
*/
public List<String> getJsseCipherNames() {
if (jsseCipherNames == null) {
- jsseCipherNames = OpenSSLCipherConfigurationParser.convertForJSSE(getCipherList());
+ Set<Cipher> jsseCiphers = new HashSet<>();
+ jsseCiphers.addAll(getCipherSuiteList());
+ jsseCiphers.addAll(getCipherList());
+ jsseCipherNames = OpenSSLCipherConfigurationParser.convertForJSSE(jsseCiphers);
}
return jsseCipherNames;
}
+ /**
+ * Set the cipher suite (TLSv1.3) configuration.
+ *
+ * @param cipherSuites The cipher suites to use in a colon-separated, preference order list
+ */
+ public void setCipherSuites(String cipherSuites) {
+ StringBuilder sb = new StringBuilder();
+ String[] values;
+ if (cipherSuites.contains(":")) {
+ // OpenSSL format
+ values = cipherSuites.split(":");
+ } else {
+ // JSSE format or possible a single cipher suite name
+ values = cipherSuites.split(",");
+ }
+ for (String value : values) {
+ String trimmed = value.trim();
+ if (!trimmed.isEmpty()) {
+ if (!OpenSSLCipherConfigurationParser.isTls13Cipher(trimmed)) {
+ log.warn(sm.getString("sslHostConfig.ignoreNonTls13Ciphersuite", trimmed));
+ continue;
+ }
+ /*
+ * OpenSSL and JSSE names for TLSv1.3 cipher suites are currently (January 2026) the same but handle the
+ * possible future case where they are not.
+ */
+ 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(':');
+ }
+ sb.append(trimmed);
+ }
+ }
+ this.cipherSuites = sb.toString();
+ this.cipherSuiteList = null;
+ this.jsseCipherNames = null;
+ }
+
+
+ /**
+ * Obtain the current cipher suite (TLSv1.3) configuration.
+ *
+ * @return An OpenSSL cipher suite string for the current configuration.
+ */
+ public String getCipherSuites() {
+ return cipherSuites;
+ }
+
+
+ private LinkedHashSet<Cipher> getCipherSuiteList() {
+ if (cipherSuiteList == null) {
+ cipherSuiteList = OpenSSLCipherConfigurationParser.parse(getCipherSuites());
+ }
+ return cipherSuiteList;
+ }
+
+
public void setHonorCipherOrder(boolean honorCipherOrder) {
this.honorCipherOrder = honorCipherOrder;
}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
index e67d449900..a4ac3f7f4e 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java
@@ -318,6 +318,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext {
// Configure the ciphers that the client is permitted to negotiate
SSLContext.setCipherSuite(state.ctx, sslHostConfig.getCiphers());
+ SSLContext.setCipherSuitesEx(state.ctx, sslHostConfig.getCipherSuites());
// If there is no certificate file must be using a KeyStore so a KeyManager is required.
// If there is a certificate file a KeyManager is helpful but not strictly necessary.
diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
index 25e2d5acc7..77e4de2c87 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
@@ -515,14 +515,14 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext {
}
if (maxTlsVersion >= TLS1_3_VERSION()) {
try {
- if (SSL_CTX_set_ciphersuites(state.sslCtx, localArena.allocateFrom(sslHostConfig.getCiphers())) <= 0) {
- tls13Warning = sm.getString("engine.failedCipherSuite", sslHostConfig.getCiphers());
+ if (SSL_CTX_set_ciphersuites(state.sslCtx, localArena.allocateFrom(sslHostConfig.getCipherSuites())) <= 0) {
+ tls13Warning = sm.getString("engine.failedCipherSuite", sslHostConfig.getCipherSuites());
} else {
ciphersSet = true;
}
} catch (NoClassDefFoundError | UnsatisfiedLinkError e) {
// Ignore unavailable TLS 1.3 call, which might be compiled out sometimes on LibreSSL
- tls13Warning = sm.getString("engine.failedCipherSuite", sslHostConfig.getCiphers());
+ tls13Warning = sm.getString("engine.failedCipherSuite", sslHostConfig.getCipherSuites());
}
}
if (!ciphersSet) {
diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfig.java b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
index 1ee4f80518..9bfdbfef94 100644
--- a/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
+++ b/test/org/apache/tomcat/util/net/TestSSLHostConfig.java
@@ -76,6 +76,76 @@ public class TestSSLHostConfig {
}
+ @Test
+ public void testCipher05() {
+ SSLHostConfig hc = new SSLHostConfig();
+ Cipher c = Cipher.TLS_AES_128_CCM_SHA256;
+
+ // Single TLSv1.3 name - should be filtered out
+ hc.setCiphers(c.getOpenSSLAlias());
+ Assert.assertEquals("", hc.getCiphers());
+ }
+
+
+ @Test
+ public void testCipher06() {
+ SSLHostConfig hc = new SSLHostConfig();
+ 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
+ hc.setCiphers(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias());
+ Assert.assertEquals(c2.getOpenSSLAlias(), hc.getCiphers());
+ }
+
+
+ @Test
+ public void testCipher07() {
+ SSLHostConfig hc = new SSLHostConfig();
+ 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
+ hc.setCiphers(c2.getOpenSSLAlias() + ":" + c1.getOpenSSLAlias());
+ Assert.assertEquals(c2.getOpenSSLAlias(), hc.getCiphers());
+ }
+
+
+ @Test
+ public void testCiphersuite01() {
+ SSLHostConfig hc = new SSLHostConfig();
+ Cipher c = Cipher.TLS_AES_128_CCM_SHA256;
+
+ // Single TLSv1.3 cipher suite name
+ hc.setCipherSuites(c.getOpenSSLAlias());
+ Assert.assertEquals(c.getOpenSSLAlias(), hc.getCipherSuites());
+ }
+
+
+ @Test
+ public void testCiphersuite02() {
+ SSLHostConfig hc = new SSLHostConfig();
+ Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256;
+ Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5;
+
+ // TLSv1.3 then TLSv1.2 - TLSv1.2 name should be filtered out
+ hc.setCipherSuites(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias());
+ Assert.assertEquals(c1.getOpenSSLAlias(), hc.getCipherSuites());
+ }
+
+
+ @Test
+ public void testCiphersuite03() {
+ SSLHostConfig hc = new SSLHostConfig();
+ Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256;
+ Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5;
+
+ // TLSv1.2 then TLSv1.3 - TLSv1.2 name should be filtered out
+ hc.setCipherSuites(c2.getOpenSSLAlias() + ":" + c1.getOpenSSLAlias());
+ Assert.assertEquals(c1.getOpenSSLAlias(), hc.getCipherSuites());
+ }
+
+
@Test
public void testSerialization() throws IOException, ClassNotFoundException {
// Dummy OpenSSL command name/value pair
diff --git a/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java b/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java
new file mode 100644
index 0000000000..19e5fb0970
--- /dev/null
+++ b/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.TesterServlet;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+@RunWith(Parameterized.class)
+public class TestSSLHostConfigCipher extends TomcatBaseTest {
+
+ private static final String CIPHER_12_AVAILABLE = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256";
+ private static final String CIPHER_12_NOT_AVAILABLE = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384";
+ private static final String CIPHER_13_AVAILABLE = "TLS_AES_128_GCM_SHA256";
+ private static final String CIPHER_13_NOT_AVAILABLE = "TLS_AES_256_GCM_SHA384";
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> parameters() {
+ List<Object[]> parameterSets = new ArrayList<>();
+ parameterSets.add(new Object[] {
+ "JSSE", Boolean.FALSE, "org.apache.tomcat.util.net.jsse.JSSEImplementation"});
+ parameterSets.add(new Object[] {
+ "OpenSSL", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.OpenSSLImplementation"});
+ parameterSets.add(new Object[] {
+ "OpenSSL-FFM", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"});
+
+ return parameterSets;
+ }
+
+ @Parameter(0)
+ public String connectorName;
+
+ @Parameter(1)
+ public boolean useOpenSSL;
+
+ @Parameter(2)
+ public String sslImplementationName;
+
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ Tomcat tomcat = getTomcatInstance();
+
+ // Server-side TLS configuration
+ TesterSupport.initSsl(tomcat);
+ TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL);
+
+ // Test specific, server-side cipher & protocol configuration
+ SSLHostConfig sslHostConfig = getSSLHostConfig();
+ sslHostConfig.setProtocols("+TLSv1.2+TLSv1.3");
+ sslHostConfig.setCiphers(CIPHER_12_AVAILABLE);
+ sslHostConfig.setCipherSuites(CIPHER_13_AVAILABLE);
+
+ // Simple webapp
+ Context ctxt = getProgrammaticRootContext();
+ Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet());
+ ctxt.addServletMappingDecoded("/*", "TesterServlet");
+ }
+
+
+ @Test
+ public void testTls12CipherAvailable() throws Exception {
+ // Client-side TLS configuration
+ TesterSupport.configureClientSsl(true, new String[] { CIPHER_12_AVAILABLE } );
+
+ doTest();
+ }
+
+
+ @Test(expected=SSLHandshakeException.class)
+ public void testTls12CipherNotAvailable() throws Exception {
+ // Client-side TLS configuration
+ TesterSupport.configureClientSsl(true, new String[] { CIPHER_12_NOT_AVAILABLE } );
+
+ doTest();
+ }
+
+
+ @Test
+ public void testTls13CipherAvailable() throws Exception {
+ // Client-side TLS configuration
+ TesterSupport.configureClientSsl(new String[] { CIPHER_13_AVAILABLE } );
+
+ doTest();
+ }
+
+
+ @Test(expected=SSLHandshakeException.class)
+ public void testTls13CipherNotAvailable() throws Exception {
+ // Client-side TLS configuration
+ TesterSupport.configureClientSsl(new String[] { CIPHER_13_NOT_AVAILABLE } );
+
+ doTest();
+ }
+
+
+ private void doTest() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+
+ tomcat.start();
+
+ // Check a request can be made
+ ByteChunk res = getUrl("https://localhost:" + getPort() + "/");
+ Assert.assertEquals("OK", res.toString());
+ }
+
+
+ private SSLHostConfig getSSLHostConfig() {
+ Tomcat tomcat = getTomcatInstance();
+ Connector connector = tomcat.getConnector();
+ return connector.findSslHostConfigs()[0];
+ }
+}
diff --git a/test/org/apache/tomcat/util/net/TesterSupport.java b/test/org/apache/tomcat/util/net/TesterSupport.java
index f67b2dc5d5..2aa332fe6b 100644
--- a/test/org/apache/tomcat/util/net/TesterSupport.java
+++ b/test/org/apache/tomcat/util/net/TesterSupport.java
@@ -181,7 +181,15 @@ public final class TesterSupport {
return configureClientSsl(false);
}
+ public static ClientSSLSocketFactory configureClientSsl(String[] ciphers) {
+ return configureClientSsl(false, ciphers);
+ }
+
public static ClientSSLSocketFactory configureClientSsl(boolean forceTls12) {
+ return configureClientSsl(forceTls12, null);
+ }
+
+ public static ClientSSLSocketFactory configureClientSsl(boolean forceTls12, String[] ciphers) {
ClientSSLSocketFactory clientSSLSocketFactory = null;
try {
SSLContext sc;
@@ -192,6 +200,9 @@ public final class TesterSupport {
}
sc.init(getUser1KeyManagers(), getTrustManagers(), null);
clientSSLSocketFactory = new ClientSSLSocketFactory(sc.getSocketFactory());
+ if (ciphers != null) {
+ clientSSLSocketFactory.setCipher(ciphers);
+ }
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(clientSSLSocketFactory);
} catch (Exception e) {
e.printStackTrace();
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ad25e4f943..a04740a3e7 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -157,6 +157,19 @@
certificates unless CA certificates were configured and the
configuration failed. (markt)
</fix>
+ <add>
+ For configuration consistency between OpenSSL and JSSE TLS
+ implementations, TLSv1.3 cipher suites included in the
+ <code>ciphers</code> attribute of an <code>SSLHostConfig</code> are now
+ always ignored (previously they would be ignored with OpenSSL
+ implementations and used with JSSE implementations) and a warning is
+ logged that the cipher suite has been ignored. (markt)
+ </add>
+ <add>
+ Add the <code>ciphersuite</code> attribute to
+ <code>SSLHostConfig</code> to configure the TLSv1.3 cipher suites.
+ (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Cluster">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 4028bc4aba..4a12e601c8 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1361,10 +1361,11 @@
</attribute>
<attribute name="ciphers" required="false">
- <p>The ciphers to enable using the OpenSSL syntax. (See the OpenSSL
- documentation for the list of ciphers supported and the syntax).
- Alternatively, a comma separated list of ciphers using the standard
- OpenSSL cipher names or the standard JSSE cipher names may be used.</p>
+ <p>The TLSv1.2 and below ciphers to enable using the OpenSSL syntax. (See
+ the OpenSSL documentation for the list of ciphers supported and the
+ syntax). Alternatively, a comma separated list of TLS v1.2 and below
+ ciphers using the standard OpenSSL cipher names or the standard JSSE
+ cipher names may be used.</p>
<p>Different versions of OpenSSL may interpret the same cipher string
differently. For example, the <code>CCM8</code> ciphers were moved from
<code>HIGH</code> to <code>MEDIUM</code> in OpenSSL 3.2. Regardless of
@@ -1372,17 +1373,40 @@
value to a list of ciphers in a manner consistent with the latest OpenSSL
development branch. This list of ciphers is then passed to the SSL
implementation.</p>
- <p>Only the 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 will be logged in a
+ <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 ciphers that are supported by the
- configured SSL implementation.</p>
+ 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>
<p>Note that, by default, the order in which ciphers are defined is
- treated as an order of preference. See <code>honorCipherOrder</code>.</p>
+ not treated as an order of preference. See
+ <code>honorCipherOrder</code>.</p>
+ <p>For JSSE (which - unlike OpenSSL - uses a single configuration setting
+ for both ciphers and cipher suites) the cipher suites attribute will be
+ prepended to the ciphers attribute before the combined value is used to
+ configure the JSSE implementation.</p>
+ </attribute>
+
+ <attribute name="cipherSuites" required="false">
+ <p>The TLSv1.3 cipher suites to enable using either the OpenSSL syntax
+ (colon separated list) or the JSSE syntax (comma separated list) using the
+ standard TLSv1.3 cipher suite names.</p>
+ <p>Only TLSv1.3 cipher suites will be used. Any other entries in the list
+ 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.3 cipher suites.</p>
+ <p>If not specified, a default (using the OpenSSL notation) of
+ <code>TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256</code>
+ will be used.</p>
+ <p>For JSSE (which - unlike OpenSSL - uses a single configuration setting
+ for both ciphers and cipher suites) the cipher suites attribute will be
+ prepended to the ciphers attribute before the combined value is used to
+ configure the JSSE implementation.</p>
</attribute>
<attribute name="disableCompression" required="false">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org