From 26ace65516e6e728033d3e1938f5b4d70f62ff89 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Tue, 1 Apr 2025 21:29:09 +0200 Subject: [PATCH] DNS host / identity normalization should be performed only once per public API call --- .../client5/http/psl/PublicSuffixMatcher.java | 35 +++-- .../http/ssl/DefaultHostnameVerifier.java | 53 ++----- .../hc/client5/http/utils/DnsUtils.java | 19 +++ .../http/ssl/TestDefaultHostnameVerifier.java | 147 ++++++++++-------- 4 files changed, 133 insertions(+), 121 deletions(-) diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/psl/PublicSuffixMatcher.java b/httpclient5/src/main/java/org/apache/hc/client5/http/psl/PublicSuffixMatcher.java index 1a39fa808c..5a5f27812f 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/psl/PublicSuffixMatcher.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/psl/PublicSuffixMatcher.java @@ -34,6 +34,7 @@ import org.apache.hc.client5.http.utils.DnsUtils; import org.apache.hc.core5.annotation.Contract; +import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; @@ -137,8 +138,17 @@ public String getDomainRoot(final String domain, final DomainType expectedType) if (domain.startsWith(".")) { return null; } - final DomainRootInfo match = resolveDomainRoot(domain, expectedType); - return match != null ? match.root : null; + String normalized = DnsUtils.normalize(domain); + final boolean punyCoded = normalized.contains("xn-"); + if (punyCoded) { + normalized = IDN.toUnicode(normalized); + } + final DomainRootInfo match = resolveDomainRoot(normalized, expectedType); + String domainRoot = match != null ? match.root : null; + if (domainRoot != null && punyCoded) { + domainRoot = IDN.toASCII(domainRoot); + } + return domainRoot; } static final class DomainRootInfo { @@ -155,11 +165,11 @@ static final class DomainRootInfo { } DomainRootInfo resolveDomainRoot(final String domain, final DomainType expectedType) { - String segment = DnsUtils.normalize(domain); + String segment = domain; String result = null; while (segment != null) { // An exception rule takes priority over any other matching rule. - final String key = IDN.toUnicode(segment); + final String key = segment; final DomainType exceptionRule = findEntry(exceptions, key); if (match(exceptionRule, expectedType)) { return new DomainRootInfo(segment, key, exceptionRule); @@ -173,7 +183,7 @@ DomainRootInfo resolveDomainRoot(final String domain, final DomainType expectedT final String nextSegment = nextdot != -1 ? segment.substring(nextdot + 1) : null; // look for wildcard entries - final String wildcardKey = (nextSegment == null) ? "*" : "*." + IDN.toUnicode(nextSegment); + final String wildcardKey = (nextSegment == null) ? "*" : "*." + nextSegment; final DomainType wildcardDomainRule = findEntry(rules, wildcardKey); if (match(wildcardDomainRule, expectedType)) { return new DomainRootInfo(result, wildcardKey, wildcardDomainRule); @@ -239,20 +249,11 @@ public boolean verify(final String domain) { if (domain == null) { return false; } - return verifyStrict(domain.startsWith(".") ? domain.substring(1) : domain); + return verifyInternal(domain.startsWith(".") ? domain.substring(1) : domain); } - /** - * Verifies if the given domain does not represent a public domain root and is - * allowed to set cookies, have an identity represented by a certificate, etc. - * - * @since 5.5 - */ - public boolean verifyStrict(final String domain) { - Args.notNull(domain, "Domain"); - if (domain.startsWith(".")) { - return false; - } + @Internal + public boolean verifyInternal(final String domain) { final DomainRootInfo domainRootInfo = resolveDomainRoot(domain, null); if (domainRootInfo == null) { return false; diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java index 3b0f0b3b34..ecb7da5c57 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java @@ -27,7 +27,6 @@ package org.apache.hc.client5.http.ssl; -import java.net.IDN; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.cert.Certificate; @@ -159,11 +158,11 @@ static void matchIPv6Address(final String host, final List subjectA static void matchDNSName(final String host, final List subjectAlts, final PublicSuffixMatcher publicSuffixMatcher) throws SSLPeerUnverifiedException { - final String normalizedHost = DnsUtils.normalize(host); + final String normalizedHost = DnsUtils.normalizeUnicode(host); for (final SubjectName subjectAlt : subjectAlts) { if (subjectAlt.getType() == SubjectName.DNS) { - final String normalizedSubjectAlt = DnsUtils.normalize(subjectAlt.getValue()); - if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt, publicSuffixMatcher)) { + final String normalizedSubjectAlt = DnsUtils.normalizeUnicode(subjectAlt.getValue()); + if (matchIdentity(normalizedHost, normalizedSubjectAlt, publicSuffixMatcher, true)) { return; } } @@ -180,9 +179,9 @@ static void matchCN(final String host, final X509Certificate cert, throw new SSLPeerUnverifiedException("Certificate subject for <" + host + "> doesn't contain " + "a common name and does not have alternative names"); } - final String normalizedHost = DnsUtils.normalize(host); - final String normalizedCn = DnsUtils.normalize(cn); - if (!matchIdentityStrict(normalizedHost, normalizedCn, publicSuffixMatcher)) { + final String normalizedHost = DnsUtils.normalizeUnicode(host); + final String normalizedCn = DnsUtils.normalizeUnicode(cn); + if (!matchIdentity(normalizedHost, normalizedCn, publicSuffixMatcher, true)) { throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match " + "common name of the certificate subject: " + cn); } @@ -224,21 +223,11 @@ static boolean matchDomainRoot(final String host, final String domainRoot) { return false; } - private static boolean matchIdentity(final String host, final String identity, + static boolean matchIdentity(final String host, final String identity, final PublicSuffixMatcher publicSuffixMatcher, final boolean strict) { - - final String normalizedIdentity; - try { - // Convert only the identity to its Unicode form - normalizedIdentity = IDN.toUnicode(identity); - } catch (final IllegalArgumentException e) { - return false; - } - - // Public suffix check on the Unicode identity if (publicSuffixMatcher != null && host.contains(".")) { - if (!publicSuffixMatcher.verifyStrict(normalizedIdentity)) { + if (!publicSuffixMatcher.verifyInternal(identity)) { if (LOG.isDebugEnabled()) { LOG.debug("Public Suffix List verification failed for identity '{}'", identity); } @@ -251,10 +240,10 @@ private static boolean matchIdentity(final String host, final String identity, // character * which is considered to match any single domain name // component or component fragment..." // Based on this statement presuming only singular wildcard is legal - final int asteriskIdx = normalizedIdentity.indexOf('*'); + final int asteriskIdx = identity.indexOf('*'); if (asteriskIdx != -1) { - final String prefix = normalizedIdentity.substring(0, asteriskIdx); - final String suffix = normalizedIdentity.substring(asteriskIdx + 1); + final String prefix = identity.substring(0, asteriskIdx); + final String suffix = identity.substring(asteriskIdx + 1); if (!prefix.isEmpty() && !host.startsWith(prefix)) { return false; @@ -274,25 +263,7 @@ private static boolean matchIdentity(final String host, final String identity, } // Direct Unicode comparison - return host.equalsIgnoreCase(normalizedIdentity); - } - - static boolean matchIdentity(final String host, final String identity, - final PublicSuffixMatcher publicSuffixMatcher) { - return matchIdentity(host, identity, publicSuffixMatcher, false); - } - - static boolean matchIdentity(final String host, final String identity) { - return matchIdentity(host, identity, null, false); - } - - static boolean matchIdentityStrict(final String host, final String identity, - final PublicSuffixMatcher publicSuffixMatcher) { - return matchIdentity(host, identity, publicSuffixMatcher, true); - } - - static boolean matchIdentityStrict(final String host, final String identity) { - return matchIdentity(host, identity, null, true); + return host.equalsIgnoreCase(identity); } static String extractCN(final String subjectPrincipal) throws SSLException { diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DnsUtils.java b/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DnsUtils.java index 1e04cb118d..05a08b3cd1 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DnsUtils.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/utils/DnsUtils.java @@ -27,6 +27,8 @@ package org.apache.hc.client5.http.utils; +import java.net.IDN; + /** * A collection of utilities relating to Domain Name System. * @@ -72,4 +74,21 @@ public static String normalize(final String s) { return s; } + /** + * Decodes to Unicode and normalizes the given DNS name. + * @since 5.5 + */ + public static String normalizeUnicode(final String s) { + if (s == null) { + return null; + } + String decoded; + try { + decoded = IDN.toUnicode(s); + } catch (final IllegalArgumentException ignore) { + decoded = s; + } + return normalize(decoded); + } + } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java b/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java index f4b8f83ad8..e6e0f1f663 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java @@ -59,6 +59,7 @@ import org.apache.hc.client5.http.psl.PublicSuffixList; import org.apache.hc.client5.http.psl.PublicSuffixListParser; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; +import org.apache.hc.client5.http.utils.DnsUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -246,68 +247,94 @@ void testDomainRootMatching() { Assertions.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.a.b.c", "a.b.c")); } + static boolean matchIdentity(final String host, final String identity, + final PublicSuffixMatcher publicSuffixMatcher, final boolean strict) { + return DefaultHostnameVerifier.matchIdentity( + DnsUtils.normalizeUnicode(host), + DnsUtils.normalizeUnicode(identity), + publicSuffixMatcher, strict); + } + + static boolean matchIdentity(final String host, final String identity, + final PublicSuffixMatcher publicSuffixMatcher) { + return matchIdentity(host, identity, publicSuffixMatcher, false); + } + + static boolean matchIdentity(final String host, final String identity) { + return matchIdentity(host, identity, null, false); + } + + static boolean matchIdentityStrict(final String host, final String identity, + final PublicSuffixMatcher publicSuffixMatcher) { + return matchIdentity(host, identity, publicSuffixMatcher, true); + } + + static boolean matchIdentityStrict(final String host, final String identity) { + return matchIdentity(host, identity, null, true); + } + @Test void testIdentityMatching() { - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c")); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.c")); + Assertions.assertTrue(matchIdentity("a.b.c", "*.b.c")); + Assertions.assertTrue(matchIdentityStrict("a.b.c", "*.b.c")); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.b.c", "*.b.c")); - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.b.c", "*.b.c")); // subdomain not OK + Assertions.assertTrue(matchIdentity("s.a.b.c", "*.b.c")); + Assertions.assertFalse(matchIdentityStrict("s.a.b.c", "*.b.c")); // subdomain not OK - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "*.gov.uk", publicSuffixMatcher)); // Bad 2TLD + Assertions.assertTrue(matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("a.gov.uk", "*.gov.uk", publicSuffixMatcher)); // Bad 2TLD - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher)); - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher)); // BBad 2TLD/no subdomain allowed + Assertions.assertTrue(matchIdentity("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher)); + Assertions.assertFalse(matchIdentityStrict("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher)); // BBad 2TLD/no subdomain allowed - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); // no subdomain allowed + Assertions.assertTrue(matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); + Assertions.assertFalse(matchIdentityStrict("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); // no subdomain allowed - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD + Assertions.assertTrue(matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD/no subdomain allowed + Assertions.assertFalse(matchIdentity("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD + Assertions.assertFalse(matchIdentityStrict("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD/no subdomain allowed - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.*")); - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.*")); + Assertions.assertFalse(matchIdentity("a.b.c", "*.b.*")); + Assertions.assertFalse(matchIdentityStrict("a.b.c", "*.b.*")); - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.*.c")); - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.*.c")); + Assertions.assertFalse(matchIdentity("a.b.c", "*.*.c")); + Assertions.assertFalse(matchIdentityStrict("a.b.c", "*.*.c")); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher)); } @Test void testHTTPCLIENT_1097() { - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "a*.b.c")); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "a*.b.c")); + Assertions.assertTrue(matchIdentity("a.b.c", "a*.b.c")); + Assertions.assertTrue(matchIdentityStrict("a.b.c", "a*.b.c")); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.a.b.c", "a*.b.c")); - Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.a.b.c", "a*.b.c")); + Assertions.assertTrue(matchIdentity("a.a.b.c", "a*.b.c")); + Assertions.assertFalse(matchIdentityStrict("a.a.b.c", "a*.b.c")); } @Test void testHTTPCLIENT_1255() { - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("mail.a.b.c.com", "m*.a.b.c.com")); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com")); + Assertions.assertTrue(matchIdentity("mail.a.b.c.com", "m*.a.b.c.com")); + Assertions.assertTrue(matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com")); } @Test @@ -315,24 +342,24 @@ void testHTTPCLIENT_1997() { String domain; // Unknown domain = "dev.b.cloud.a"; - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("service.apps." + domain, "*.apps." + domain)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("service.apps." + domain, "*.apps." + domain)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain)); + Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain)); + Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); // ICANN domain = "dev.b.cloud.com"; - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("service.apps." + domain, "*.apps." + domain)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("service.apps." + domain, "*.apps." + domain)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain)); + Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain)); + Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); // PRIVATE domain = "dev.b.cloud.lan"; - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("service.apps." + domain, "*.apps." + domain)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("service.apps." + domain, "*.apps." + domain)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); - Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); + Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain)); + Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain)); + Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); + Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); } @Test // Check compressed IPv6 hostname matching @@ -435,38 +462,34 @@ void testMatchIdentity() { final String punycodeHost1 = "xn----dtbqigoecuc.xn--p1ai"; // These should now match, thanks to IDN.toASCII(): - Assertions.assertTrue( - DefaultHostnameVerifier.matchIdentity(unicodeHost1, punycodeHost1), + Assertions.assertTrue(matchIdentity(unicodeHost1, punycodeHost1), "Expected the Unicode host and its punycode to match" ); // ‘example.com’ vs. an unrelated punycode domain should fail: Assertions.assertFalse( - DefaultHostnameVerifier.matchIdentity("example.com", punycodeHost1), + matchIdentity("example.com", punycodeHost1), "Expected mismatch between example.com and xn----dtbqigoecuc.xn--p1ai" ); // Test 2: Unicode host and Unicode identity final String unicodeHost2 = "пример.рф"; final String unicodeIdentity2 = "пример.рф"; - Assertions.assertTrue( - DefaultHostnameVerifier.matchIdentity(unicodeHost2, unicodeIdentity2), + Assertions.assertTrue(matchIdentity(unicodeHost2, unicodeIdentity2), "Expected Unicode host and Unicode identity to match" ); // Test 3: Punycode host and Unicode identity final String unicodeHost3 = "пример.рф"; final String punycodeIdentity3 = "xn--e1afmkfd.xn--p1ai"; - Assertions.assertTrue( - DefaultHostnameVerifier.matchIdentity(unicodeHost3, punycodeIdentity3), + Assertions.assertTrue(matchIdentity(unicodeHost3, punycodeIdentity3), "Expected Unicode host and punycode identity to match" ); // Test 4: Wildcard matching in the left-most label final String unicodeHost4 = "sub.пример.рф"; final String unicodeIdentity4 = "*.пример.рф"; - Assertions.assertTrue( - DefaultHostnameVerifier.matchIdentity(unicodeHost4, unicodeIdentity4), + Assertions.assertTrue(matchIdentity(unicodeHost4, unicodeIdentity4), "Expected wildcard to match subdomain" ); @@ -474,7 +497,7 @@ void testMatchIdentity() { final String invalidHost = "invalid_host"; final String unicodeIdentity5 = "пример.рф"; Assertions.assertFalse( - DefaultHostnameVerifier.matchIdentity(invalidHost, unicodeIdentity5), + matchIdentity(invalidHost, unicodeIdentity5), "Expected invalid host to not match" ); @@ -482,15 +505,14 @@ void testMatchIdentity() { final String unicodeHost4b = "пример.рф"; final String invalidIdentity = "xn--invalid-punycode"; Assertions.assertFalse( - DefaultHostnameVerifier.matchIdentity(unicodeHost4b, invalidIdentity), + matchIdentity(unicodeHost4b, invalidIdentity), "Expected invalid identity to not match" ); // Test 7: Mixed case comparison final String unicodeHost5 = "ПрИмеР.рф"; final String unicodeIdentity6 = "пример.рф"; - Assertions.assertTrue( - DefaultHostnameVerifier.matchIdentity(unicodeHost5, unicodeIdentity6), + Assertions.assertTrue(matchIdentity(unicodeHost5, unicodeIdentity6), "Expected case-insensitive Unicode comparison to match" ); @@ -498,8 +520,7 @@ void testMatchIdentity() { // Test 8: Wildcard in the middle label (per RFC 2818, should match) final String unicodeHost6 = "sub.пример.рф"; final String unicodeIdentity8 = "sub.*.рф"; - Assertions.assertTrue( - DefaultHostnameVerifier.matchIdentity(unicodeHost6, unicodeIdentity8), + Assertions.assertTrue(matchIdentity(unicodeHost6, unicodeIdentity8), "Expected wildcard in the middle label to match" ); }