This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 36a181413a4d601937fbeafd58734bd32fcc370f 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 1322f42fcc..4d8d71f0b4 100644 --- a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java +++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java @@ -90,7 +90,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 1805b6a2e7..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.getFirst(); + // 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 61baf56e99..2aa6c6addf 100644 --- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java +++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java @@ -458,7 +458,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 08fcac256f..6c6f279586 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -547,6 +547,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="Jasper"> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
