From cbff6bf2d1459d1c7f45144d43fff2ec21bcc457 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 3 Aug 2026 07:22:22 -0400 Subject: [PATCH 1/3] RFC10015 support in TlsCiphers - TlsCiphers.isWeak(String) return true for deprecated and discouraged cipher suites. - No change to TlsCiphers.isH2Blacklisted(String) --- .../apache/hc/core5/http/ssl/TlsCiphers.java | 12 +- .../hc/core5/http/ssl/TestTlsCiphers.java | 317 +++++++++++++++++- 2 files changed, 316 insertions(+), 13 deletions(-) diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java index 140f925637..eb57109eff 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java @@ -332,9 +332,15 @@ public static boolean isH2Blacklisted(final String cipherSuite) { private static final String WEAK_CIPHERS = "^(TLS|SSL)_(.*)_WITH_(NULL|DES_CBC|DES40_CBC|DES_CBC_40|3DES_EDE_CBC|RC4_128|RC4_40|RC2_CBC_40)_(.*)"; + /** + * RFC 10015 Deprecating Obsolete Key Exchange Methods in TLS 1.2 and DTLS 1.2. + */ + private static final String RC100015_DEPRECATED_CIPHERS = "^TLS_(DHE?|PSK_DHE_WITH_AES|ECDH|RSA)_(.*)"; + private static final List WEAK_CIPHER_SUITE_PATTERNS = Collections.unmodifiableList(Arrays.asList( Pattern.compile(WEAK_KEY_EXCHANGES, Pattern.CASE_INSENSITIVE), - Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE))); + Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE), + Pattern.compile(RC100015_DEPRECATED_CIPHERS, Pattern.CASE_INSENSITIVE))); public static boolean isWeak(final String cipherSuite) { for (final Pattern pattern : WEAK_CIPHER_SUITE_PATTERNS) { @@ -371,4 +377,8 @@ public static String[] excludeWeak(final String... ciphers) { return !enabledCiphers.isEmpty() ? enabledCiphers.toArray(new String[0]) : ciphers; } + static Set getH2Blacklisted() { + return H2_BLACKLISTED; + } + } diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java index f2c192910c..5e7cf1cbf8 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java @@ -28,6 +28,7 @@ package org.apache.hc.core5.http.ssl; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -40,13 +41,13 @@ class TestTlsCiphers { static String[] testExcludeH2Blacklisted() { final String[] mixCipherSuites = { - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", - "TLS_RSA_WITH_AES_256_CBC_SHA256", - "AES_SHA_US", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "NULL_SHA", - "TLS_RSA_WITH_AES_256_GCM_SHA384" - }; + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "AES_SHA_US", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "NULL_SHA", + "TLS_RSA_WITH_AES_256_GCM_SHA384" + }; return TlsCiphers.excludeH2Blacklisted(mixCipherSuites); } @@ -94,14 +95,306 @@ void testExcludeWeakNull() { Assertions.assertNull(TlsCiphers.excludeWeak((String[]) null)); } + @Disabled("https://lists.apache.org/thread/2z09rjxv0wpss2gvv9jl8yq9wlst8qmh") + @ParameterizedTest + @MethodSource("org.apache.hc.core5.http.ssl.TlsCiphers#getH2Blacklisted()") + void testH2BlacklistedIsWeak(final String h2BlacklistedCipherSuite) { + // Sanity assert + Assertions.assertTrue(TlsCiphers.isH2Blacklisted(h2BlacklistedCipherSuite), h2BlacklistedCipherSuite); + // Test + Assertions.assertTrue(TlsCiphers.isWeak(h2BlacklistedCipherSuite), h2BlacklistedCipherSuite); + } + + /** + * Tests DH cipher suites deprecated by RFC10015 Section 5.1. + * + * @param deprecatedCipherSuite DH cipher suites deprecated by RFC10015 Section 5.1. + */ + @ParameterizedTest + @ValueSource(strings = { + "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", // RFC4346 + "TLS_DH_DSS_WITH_DES_CBC_SHA", // RFC8996 + "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", // RFC5246 + "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", // RFC4346 + "TLS_DH_RSA_WITH_DES_CBC_SHA", // RFC8996 + "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", // RFC5246 + "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", // RFC4346, RFC6347 + "TLS_DH_anon_WITH_RC4_128_MD5", // RFC5246, RFC6347 + "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", // RFC4346 + "TLS_DH_anon_WITH_DES_CBC_SHA", // RFC8996 + "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", // RFC5246 + "TLS_DH_DSS_WITH_AES_128_CBC_SHA", // RFC5246 + "TLS_DH_RSA_WITH_AES_128_CBC_SHA", // RFC5246 + "TLS_DH_anon_WITH_AES_128_CBC_SHA", // RFC5246 + "TLS_DH_DSS_WITH_AES_256_CBC_SHA", // RFC5246 + "TLS_DH_RSA_WITH_AES_256_CBC_SHA", // RFC5246 + "TLS_DH_anon_WITH_AES_256_CBC_SHA", // RFC5246 + "TLS_DH_DSS_WITH_AES_128_CBC_SHA256", // RFC5246 + "TLS_DH_RSA_WITH_AES_128_CBC_SHA256", // RFC5246 + "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", // RFC5932 + "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", // RFC5932 + "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", // RFC5932 + "TLS_DH_DSS_WITH_AES_256_CBC_SHA256", // RFC5246 + "TLS_DH_RSA_WITH_AES_256_CBC_SHA256", // RFC5246 + "TLS_DH_anon_WITH_AES_128_CBC_SHA256", // RFC5246 + "TLS_DH_anon_WITH_AES_256_CBC_SHA256", // RFC5246 + "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", // RFC5932 + "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", // RFC5932 + "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", // RFC5932 + "TLS_DH_DSS_WITH_SEED_CBC_SHA", // RFC4162 + "TLS_DH_RSA_WITH_SEED_CBC_SHA", // RFC4162 + "TLS_DH_anon_WITH_SEED_CBC_SHA", // RFC4162 + "TLS_DH_RSA_WITH_AES_128_GCM_SHA256", // RFC5288 + "TLS_DH_RSA_WITH_AES_256_GCM_SHA384", // RFC5288 + "TLS_DH_DSS_WITH_AES_128_GCM_SHA256", // RFC5288 + "TLS_DH_DSS_WITH_AES_256_GCM_SHA384", // RFC5288 + "TLS_DH_anon_WITH_AES_128_GCM_SHA256", // RFC5288 + "TLS_DH_anon_WITH_AES_256_GCM_SHA384", // RFC5288 + "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", // RFC5932 + "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", // RFC5932 + "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", // RFC5932 + "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", // RFC5932 + "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", // RFC5932 + "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", // RFC5932 + "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + }) + void testRfc10015Section5_1_DhDepreacted(final String deprecatedCipherSuite) { + Assertions.assertTrue(TlsCiphers.isWeak(deprecatedCipherSuite), deprecatedCipherSuite); + } + + /** + * Tests ECDH cipher suites whose use is discouraged by RFC10015 Section 5.2. + * + * @param discouragedCipherSuite ECDH cipher suites whose use is discouraged by RFC10015 Section 5.2 + */ + @ParameterizedTest + @ValueSource(strings = { + "TLS_ECDH_ECDSA_WITH_NULL_SHA", // RFC8422 + "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", // RFC8422, RFC6347 + "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", // RFC8422 + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", // RFC8422 + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", // RFC8422 + "TLS_ECDH_RSA_WITH_NULL_SHA", // RFC8422 + "TLS_ECDH_RSA_WITH_RC4_128_SHA", // RFC8422, RFC6347 + "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", // RFC8422 + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", // RFC8422 + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", // RFC8422 + "TLS_ECDH_anon_WITH_NULL_SHA", // RFC8422 + "TLS_ECDH_anon_WITH_RC4_128_SHA", // RFC8422, RFC6347 + "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", // RFC8422 + "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", // RFC8422 + "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", // RFC8422 + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", // RFC5289 + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", // RFC5289 + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", // RFC5289 + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", // RFC5289 + "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", // RFC5289 + "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", // RFC5289 + "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", // RFC5289 + "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", // RFC5289 + "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", // RFC6367 + "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", // RFC6367 + "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", // RFC6367 + "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", // RFC6367 + "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + }) + void testRfc10015Section5_2_EcdhDiscouraged(final String discouragedCipherSuite) { + Assertions.assertTrue(TlsCiphers.isWeak(discouragedCipherSuite), discouragedCipherSuite); + } + + /** + * Tests DHE cipher suites deprecated by RFC10015 Section 5.3. + * + * @param deprecatedCipherSuite DHE cipher suites deprecated by RFC10015 Section 5.3. + */ + @ParameterizedTest + @ValueSource(strings = { + "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", // RFC4346 + "TLS_DHE_DSS_WITH_DES_CBC_SHA", // RFC8996 + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", // RFC5246 + "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", // RFC4346 + "TLS_DHE_RSA_WITH_DES_CBC_SHA", // RFC8996 + "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", // RFC5246 + "TLS_DHE_PSK_WITH_NULL_SHA", // RFC4785 + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", // RFC5246 + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", // RFC5246 + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", // RFC5246 + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", // RFC5246 + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", // RFC5246 + "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", // RFC5932 + "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", // RFC5932 + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", // RFC5246 + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", // RFC5246 + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", // RFC5246 + "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", // RFC5932 + "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", // RFC5932 + "TLS_DHE_PSK_WITH_RC4_128_SHA", // RFC4279, RFC6347 + "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", // RFC4279 + "TLS_DHE_PSK_WITH_AES_128_CBC_SHA", // RFC4279 + "TLS_DHE_PSK_WITH_AES_256_CBC_SHA", // RFC4279 + "TLS_DHE_DSS_WITH_SEED_CBC_SHA", // RFC4162 + "TLS_DHE_RSA_WITH_SEED_CBC_SHA", // RFC4162 + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", // RFC5288 + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", // RFC5288 + "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", // RFC5288 + "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", // RFC5288 + "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", // RFC5487 + "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", // RFC5487 + "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", // RFC5487 + "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", // RFC5487 + "TLS_DHE_PSK_WITH_NULL_SHA256", // RFC5487 + "TLS_DHE_PSK_WITH_NULL_SHA384", // RFC5487 + "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", // RFC5932 + "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", // RFC5932 + "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", // RFC5932 + "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", // RFC5932 + "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", // RFC6367 + "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", // RFC6367 + "TLS_DHE_RSA_WITH_AES_128_CCM", // RFC6655 + "TLS_DHE_RSA_WITH_AES_256_CCM", // RFC6655 + "TLS_DHE_RSA_WITH_AES_128_CCM_8", // RFC6655 + "TLS_DHE_RSA_WITH_AES_256_CCM_8", // RFC6655 + "TLS_DHE_PSK_WITH_AES_128_CCM", // RFC6655 + "TLS_DHE_PSK_WITH_AES_256_CCM", // RFC6655 + "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", // RFC7905 + "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", // RFC7905 + "TLS_PSK_DHE_WITH_AES_128_CCM_8", // RFC6655 + "TLS_PSK_DHE_WITH_AES_256_CCM_8", // RFC6655 + }) + void testRfc10015Section5_3_DheDeprecated(final String deprecatedCipherSuite) { + Assertions.assertTrue(TlsCiphers.isWeak(deprecatedCipherSuite), deprecatedCipherSuite); + } + + /** + * Tests RSA cipher suites deprecated by RFC10015 Section 5.4. + * + * @param deprecatedCipherSuite RSA cipher suites deprecated by RFC10015 Section 5.4. + */ + @ParameterizedTest + @ValueSource(strings = { + "TLS_RSA_WITH_NULL_MD5", // RFC5246 + "TLS_RSA_WITH_NULL_SHA", // RFC5246 + "TLS_RSA_EXPORT_WITH_RC4_40_MD5", // RFC4346 RFC6347 + "TLS_RSA_WITH_RC4_128_MD5", // RFC5246 RFC6347 + "TLS_RSA_WITH_RC4_128_SHA", // RFC5246 RFC6347 + "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", // RFC4346 + "TLS_RSA_WITH_IDEA_CBC_SHA", // RFC8996 + "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", // RFC4346 + "TLS_RSA_WITH_DES_CBC_SHA", // RFC8996 + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", // RFC5246 + "TLS_RSA_PSK_WITH_NULL_SHA", // RFC4785 + "TLS_RSA_WITH_AES_128_CBC_SHA", // RFC5246 + "TLS_RSA_WITH_AES_256_CBC_SHA", // RFC5246 + "TLS_RSA_WITH_NULL_SHA256", // RFC5246 + "TLS_RSA_WITH_AES_128_CBC_SHA256", // RFC5246 + "TLS_RSA_WITH_AES_256_CBC_SHA256", // RFC5246 + "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", // RFC5932 + "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", // RFC5932 + "TLS_RSA_PSK_WITH_RC4_128_SHA", // RFC4279 RFC6347 + "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", // RFC4279 + "TLS_RSA_PSK_WITH_AES_128_CBC_SHA", // RFC4279 + "TLS_RSA_PSK_WITH_AES_256_CBC_SHA", // RFC4279 + "TLS_RSA_WITH_SEED_CBC_SHA", // RFC4162 + "TLS_RSA_WITH_AES_128_GCM_SHA256", // RFC5288 + "TLS_RSA_WITH_AES_256_GCM_SHA384", // RFC5288 + "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", // RFC5487 + "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", // RFC5487 + "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", // RFC5487 + "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", // RFC5487 + "TLS_RSA_PSK_WITH_NULL_SHA256", // RFC5487 + "TLS_RSA_PSK_WITH_NULL_SHA384", // RFC5487 + "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", // RFC5932 + "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", // RFC5932 + "TLS_RSA_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_RSA_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_RSA_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_RSA_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", // RFC6209 + "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", // RFC6209 + "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", // RFC6209 + "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", // RFC6209 + "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", // RFC6367 + "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", // RFC6367 + "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", // RFC6367 + "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", // RFC6367 + "TLS_RSA_WITH_AES_128_CCM", // RFC6655 + "TLS_RSA_WITH_AES_256_CCM", // RFC6655 + "TLS_RSA_WITH_AES_128_CCM_8", // RFC6655 + "TLS_RSA_WITH_AES_256_CCM_8", // RFC6655 + "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256", // RFC7905 + }) + void testRfc10015Section5_4_RsaDeprecated(final String deprecatedCipherSuite) { + Assertions.assertTrue(TlsCiphers.isWeak(deprecatedCipherSuite), deprecatedCipherSuite); + } + @ParameterizedTest @ValueSource(strings = { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", - "TLS_RSA_WITH_AES_256_CBC_SHA256", - "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_256_GCM_SHA384" + // TLS 1.2 from https://www.ibm.com/docs/en/wm-integration-ipaas?topic=securing-understanding-cipher-suites + "TLS_AES_128_GCM_SHA256", // RFC8446 + "TLS_AES_256_GCM_SHA384", // RFC8446 + "TLS_CHACHA20_POLY1305_SHA256", // RFC8446 + "TLS_AES_128_CCM_SHA256", // RFC8446 + "TLS_AES_128_CCM_8_SHA256", // RFC8446 + // TLS 1.3 from https://www.ibm.com/docs/en/wm-integration-ipaas?topic=securing-understanding-cipher-suites + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", // RFC5289 + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", // RFC5289 + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // RFC5289 + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // RFC5289 + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", // RFC7905 + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", // RFC7905 + "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", // RFC7905 + "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256", // RFC8442 + "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384", // RFC8442 + "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256", // RFC8442 }) void testStrongCipherSuites(final String strongCipherSuite) { Assertions.assertFalse(TlsCiphers.isWeak(strongCipherSuite)); From 28918b45aea13ed8e847af468d9ec6f31824d457 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 3 Aug 2026 08:04:37 -0400 Subject: [PATCH 2/3] Javadoc --- .../java/org/apache/hc/core5/http/ssl/TlsCiphers.java | 7 +++++++ .../org/apache/hc/core5/http/ssl/TestTlsCiphers.java | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java index eb57109eff..673da270d6 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java @@ -322,6 +322,13 @@ public final class TlsCiphers { "TLS_PSK_WITH_AES_256_CCM_8" ))); + /** + * Tests whether RFC9113 Appendix A Prohibited TLS 1.2 Cipher Suites prohibits the use of a given cipher + * suite. + * + * @param cipherSuite The cipher suite name to test. + * @return Whether RFC9113 prohibits the use of a given cipher suites for HTTP/2. + */ public static boolean isH2Blacklisted(final String cipherSuite) { return H2_BLACKLISTED.contains(cipherSuite); } diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java index 5e7cf1cbf8..005911b30e 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java @@ -95,6 +95,15 @@ void testExcludeWeakNull() { Assertions.assertNull(TlsCiphers.excludeWeak((String[]) null)); } + /** + * RFC9113 "prohibits" the use of certain cipher suites in HTTP/2. + * + * RFC10015 "deprecates" some cipher suites, and discourages the use of others. + * + * So we have 3 classifications and our own "weak" classification. + * + * For leave test disabled. + */ @Disabled("https://lists.apache.org/thread/2z09rjxv0wpss2gvv9jl8yq9wlst8qmh") @ParameterizedTest @MethodSource("org.apache.hc.core5.http.ssl.TlsCiphers#getH2Blacklisted()") From 43bdeb085b28399a82b6d6f57b61d79d588484c5 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 3 Aug 2026 09:26:18 -0400 Subject: [PATCH 3/3] Enable org.apache.hc.core5.http.ssl.TestTlsCiphers.testExcludeH2Blacklisted(String) Add testCipherTLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384() because that cipher was removed from another fixture. --- .../apache/hc/core5/http/ssl/TlsCiphers.java | 12 ++++++++++++ .../hc/core5/http/ssl/TestTlsCiphers.java | 19 ++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java index 673da270d6..0cedccb22a 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/ssl/TlsCiphers.java @@ -349,7 +349,19 @@ public static boolean isH2Blacklisted(final String cipherSuite) { Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE), Pattern.compile(RC100015_DEPRECATED_CIPHERS, Pattern.CASE_INSENSITIVE))); + /** + * Tests whether a given cipher suite is considered weak. + *

+ * A cipher suite is considered weak if it is blacklisted for HTTP/2 or matches any of the weak cipher suite patterns. + *

+ * + * @param cipherSuite The cipher suite name to test. + * @return Whether the cipher suite is considered weak. + */ public static boolean isWeak(final String cipherSuite) { + if (isH2Blacklisted(cipherSuite)) { + return true; + } for (final Pattern pattern : WEAK_CIPHER_SUITE_PATTERNS) { if (pattern.matcher(cipherSuite).matches()) { return true; diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java index 005911b30e..a118704973 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java @@ -28,7 +28,6 @@ package org.apache.hc.core5.http.ssl; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -78,16 +77,24 @@ static String[] testExcludeWeak() { return TlsCiphers.excludeWeak(weakCiphersSuites); } + @Test + void testCipherTLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384() { + final String cipherSuite = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"; + Assertions.assertTrue(TlsCiphers.isH2Blacklisted(cipherSuite)); + Assertions.assertTrue(TlsCiphers.isWeak(cipherSuite)); + } + @ParameterizedTest @MethodSource void testExcludeH2Blacklisted(final String strongCipherSuite) { - Assertions.assertFalse(TlsCiphers.isWeak(strongCipherSuite)); + Assertions.assertFalse(TlsCiphers.isH2Blacklisted(strongCipherSuite), strongCipherSuite); + Assertions.assertFalse(TlsCiphers.isWeak(strongCipherSuite), strongCipherSuite); } @ParameterizedTest @MethodSource void testExcludeWeak(final String strongCipherSuite) { - Assertions.assertFalse(TlsCiphers.isWeak(strongCipherSuite)); + Assertions.assertTrue(TlsCiphers.isWeak(strongCipherSuite), strongCipherSuite); } @Test @@ -104,7 +111,6 @@ void testExcludeWeakNull() { * * For leave test disabled. */ - @Disabled("https://lists.apache.org/thread/2z09rjxv0wpss2gvv9jl8yq9wlst8qmh") @ParameterizedTest @MethodSource("org.apache.hc.core5.http.ssl.TlsCiphers#getH2Blacklisted()") void testH2BlacklistedIsWeak(final String h2BlacklistedCipherSuite) { @@ -386,7 +392,6 @@ void testRfc10015Section5_4_RsaDeprecated(final String deprecatedCipherSuite) { @ParameterizedTest @ValueSource(strings = { - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", // TLS 1.2 from https://www.ibm.com/docs/en/wm-integration-ipaas?topic=securing-understanding-cipher-suites "TLS_AES_128_GCM_SHA256", // RFC8446 "TLS_AES_256_GCM_SHA384", // RFC8446 @@ -406,7 +411,7 @@ void testRfc10015Section5_4_RsaDeprecated(final String deprecatedCipherSuite) { "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256", // RFC8442 }) void testStrongCipherSuites(final String strongCipherSuite) { - Assertions.assertFalse(TlsCiphers.isWeak(strongCipherSuite)); + Assertions.assertFalse(TlsCiphers.isWeak(strongCipherSuite), strongCipherSuite); } @ParameterizedTest @@ -428,7 +433,7 @@ void testStrongCipherSuites(final String strongCipherSuite) { "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5" }) void testWeakCiphersDisabledByDefault(final String weakCiphersSuite) { - Assertions.assertTrue(TlsCiphers.isWeak(weakCiphersSuite)); + Assertions.assertTrue(TlsCiphers.isWeak(weakCiphersSuite), weakCiphersSuite); } }