This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit e2681a7649bfc98993c2acc4f289eeacb7bf41a7 Author: opencode <[email protected]> AuthorDate: Fri Oct 16 08:54:06 2026 +0200 Exclude unimplemented SSLv2 ciphers when converting OpenSSL cipher names to JSSE names openSSLToJsse() resolved an OpenSSL cipher name only when its alias mapped to exactly one Cipher constant, and returned that constant's first JSSE name otherwise. Two consequences of including the unimplemented SSLv2 constants of the Cipher enumeration in that resolution: - Genuine OpenSSL names shared with an SSLv2 constant (RC4-MD5, EXP-RC4-MD5, EXP-RC2-CBC-MD5) were ambiguous and resolved to null. The JNI SSLEngine implementation then added that null to the set returned by getSupportedCipherSuites(), violating the SSLEngine contract, and the Panama findCiphers() method could add null to the list later parsed by the cipher configuration parser, causing a null pointer exception when a PROFILE= expression resolved to one of these ciphers. - Aliases matching only an SSLv2 constant (RC2-CBC-MD5, DES-CBC3-MD5, the SSL_CK_*/SSL2_* constants) resolved to non-standard pseudo JSSE names derived from the enumeration. The conversion now excludes SSLv2 ciphers uniformly. Names shared with an SSLv2 constant resolve to the JSSE name of the modern cipher, names matching only SSLv2 ciphers, genuinely ambiguous names and unknown names resolve to null, and the two engine call sites that previously could propagate null now filter unmapped names. --- .../tomcat/util/net/openssl/OpenSSLEngine.java | 7 +++++- .../ciphers/OpenSSLCipherConfigurationParser.java | 29 ++++++++++++++++++---- .../util/net/openssl/panama/OpenSSLLibrary.java | 7 +++++- .../util/net/openssl/ciphers/TestCipher.java | 27 ++++++++++++++++++++ webapps/docs/changelog.xml | 11 ++++++++ 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java index 13340afc09..2ec5c8a894 100644 --- a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java +++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java @@ -87,7 +87,12 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn if (c == null || c.isEmpty() || availableCipherSuites.contains(c)) { continue; } - availableCipherSuites.add(OpenSSLCipherConfigurationParser.openSSLToJsse(c)); + String jsseName = OpenSSLCipherConfigurationParser.openSSLToJsse(c); + // Ignore ciphers with no JSSE equivalent. + if (jsseName == null) { + continue; + } + availableCipherSuites.add(jsseName); } } finally { SSL.freeSSL(ssl); diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java b/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java index 157d716a47..cfeb7bced3 100644 --- a/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java +++ b/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java @@ -898,21 +898,40 @@ public class OpenSSLCipherConfigurationParser { * * @param opensslCipherName The OpenSSL name for a cipher * - * @return The first JSSE name for the specified OpenSSL cipher, or {@code null} if the name is not a known, - * unambiguous OpenSSL cipher name + * @return The first JSSE name for the specified OpenSSL cipher, or {@code null} if the name is not a known + * OpenSSL cipher name, if it remains ambiguous once unimplemented SSLv2 ciphers are excluded or if + * only SSLv2 ciphers match */ public static String openSSLToJsse(String opensslCipherName) { if (!initialized) { init(); } List<Cipher> ciphers = aliases.get(opensslCipherName); - if (ciphers == null || ciphers.size() != 1) { + if (ciphers == null) { // Not an OpenSSL cipher name return null; } - Cipher cipher = ciphers.get(0); + // OpenSSL has not supported SSLv2 for many years and the SSLv2 + // ciphers here are unimplemented. Excluding them resolves the + // ambiguity for the OpenSSL names that an SSLv2 cipher shares with + // the equivalent modern cipher. + Cipher candidate = null; + for (Cipher cipher : ciphers) { + if (cipher.getProtocol() == Protocol.SSLv2) { + continue; + } + if (candidate != null) { + // Ambiguous name + return null; + } + candidate = cipher; + } + if (candidate == null) { + // Only SSLv2 ciphers matched + return null; + } // Each Cipher always has at least one JSSE name - return cipher.getJsseNames().iterator().next(); + return candidate.getJsseNames().iterator().next(); } diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java index b8691992d2..cac10c2a89 100644 --- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java +++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java @@ -456,7 +456,12 @@ public class OpenSSLLibrary { if (c == null || c.isEmpty() || ciphersList.contains(c)) { continue; } - ciphersList.add(OpenSSLCipherConfigurationParser.openSSLToJsse(c)); + String jsseName = OpenSSLCipherConfigurationParser.openSSLToJsse(c); + // Ignore ciphers with no JSSE equivalent. + if (jsseName == null) { + continue; + } + ciphersList.add(jsseName); } } finally { SSL_free(ssl); diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java index 46309bf581..3f224bacef 100644 --- a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java +++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java @@ -116,6 +116,33 @@ public class TestCipher { } + /** + * Check that OpenSSL aliases that are shared with unimplemented SSLv2 + * ciphers resolve to the JSSE name of the modern cipher, while genuinely + * ambiguous aliases, aliases that only match SSLv2 ciphers and unknown + * names resolve to null. + */ + @Test + public void testOpenSSLToJsseAmbiguousNames() { + Assert.assertEquals("SSL_RSA_WITH_RC4_128_MD5", + OpenSSLCipherConfigurationParser.openSSLToJsse("RC4-MD5")); + Assert.assertEquals("SSL_RSA_EXPORT_WITH_RC4_40_MD5", + OpenSSLCipherConfigurationParser.openSSLToJsse("EXP-RC4-MD5")); + Assert.assertEquals("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", + OpenSSLCipherConfigurationParser.openSSLToJsse("EXP-RC2-CBC-MD5")); + // Unambiguous name + Assert.assertEquals("TLS_RSA_WITH_AES_128_CBC_SHA", + OpenSSLCipherConfigurationParser.openSSLToJsse("AES128-SHA")); + // Unknown name + Assert.assertNull(OpenSSLCipherConfigurationParser.openSSLToJsse("Nonsense")); + // Alias shared by more than one modern cipher + Assert.assertNull(OpenSSLCipherConfigurationParser.openSSLToJsse("SHA1")); + // Aliases that only match an unimplemented SSLv2 cipher + Assert.assertNull(OpenSSLCipherConfigurationParser.openSSLToJsse("RC2-CBC-MD5")); + Assert.assertNull(OpenSSLCipherConfigurationParser.openSSLToJsse("SSL_CK_RC4_128_WITH_MD5")); + } + + /** * Check that the elements of the Cipher enumeration are all using standard * names from the TLS registry or are known exceptions. diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 7a2d8143f3..10463defe3 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -340,6 +340,17 @@ using only the known terms. This matches OpenSSL behavior, where such an expression results in no cipher match. (remm) </fix> + <fix> + In the OpenSSL cipher name conversion, ignore the unimplemented SSLv2 + ciphers so that names shared with a modern cipher, for example + <code>RC4-MD5</code>, resolve to the JSSE name of the modern cipher + rather than returning <code>null</code>, and so that names matching + only an SSLv2 cipher no longer resolve to a non-standard name. + Ambiguous and unknown names still resolve to <code>null</code>; the + OpenSSL engine implementations now filter such unmapped names rather + than adding <code>null</code> to the lists of cipher suites they + expose. (remm) + </fix> </changelog> </subsection> <subsection name="Web applications"> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
