Skip to content

Commit

Permalink
HTTPCLIENT-2353: Fix IDN hostname mismatch by normalizing host and id…
Browse files Browse the repository at this point in the history
…entity with IDN.toASCII before comparison so that Unicode and punycode forms match correctly.
  • Loading branch information
arturobernalg committed Jan 4, 2025
1 parent 4b2a365 commit 83bdee3
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

package org.apache.hc.client5.http.ssl;

import java.net.IDN;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.cert.Certificate;
Expand Down Expand Up @@ -228,8 +229,18 @@ private static boolean matchIdentity(final String host, final String identity,
final PublicSuffixMatcher publicSuffixMatcher,
final DomainType domainType,
final boolean strict) {

final String punycodeHost;
final String punycodeIdentity;
try {
punycodeHost = IDN.toASCII(host);
punycodeIdentity = IDN.toASCII(identity);
} catch (final IllegalArgumentException e) {
return false;
}

if (publicSuffixMatcher != null && host.contains(".")) {
if (publicSuffixMatcher.getDomainRoot(identity, domainType) == null) {
if (publicSuffixMatcher.getDomainRoot(punycodeIdentity, domainType) == null) {
return false;
}
}
Expand All @@ -239,25 +250,25 @@ 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 = identity.indexOf('*');
final int asteriskIdx = punycodeIdentity.indexOf('*');
if (asteriskIdx != -1) {
final String prefix = identity.substring(0, asteriskIdx);
final String suffix = identity.substring(asteriskIdx + 1);
if (!prefix.isEmpty() && !host.startsWith(prefix)) {
final String prefix = punycodeIdentity.substring(0, asteriskIdx);
final String suffix = punycodeIdentity.substring(asteriskIdx + 1);
if (!prefix.isEmpty() && !punycodeHost.startsWith(prefix)) {
return false;
}
if (!suffix.isEmpty() && !host.endsWith(suffix)) {
if (!suffix.isEmpty() && !punycodeHost.endsWith(suffix)) {
return false;
}
// Additional sanity checks on content selected by wildcard can be done here
if (strict) {
final String remainder = host.substring(
prefix.length(), host.length() - suffix.length());
final String remainder = punycodeHost.substring(
prefix.length(), punycodeHost.length() - suffix.length());
return !remainder.contains(".");
}
return true;
}
return host.equalsIgnoreCase(identity);
return punycodeHost.equalsIgnoreCase(punycodeIdentity);
}

static boolean matchIdentity(final String host, final String identity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,4 +472,22 @@ void testMatchDNSName() throws Exception {
publicSuffixMatcher));
}

@Test
void testMatchIdentityWithIDN() {
final String unicodeHost = "поиск-слов.рф";
final String punycodeHost = "xn----dtbqigoecuc.xn--p1ai";

// These should now match, thanks to IDN.toASCII():
Assertions.assertTrue(
DefaultHostnameVerifier.matchIdentity(unicodeHost, punycodeHost),
"Expected the Unicode host and its punycode to match"
);

// ‘example.com’ vs. an unrelated punycode domain should fail:
Assertions.assertFalse(
DefaultHostnameVerifier.matchIdentity("example.com", punycodeHost),
"Expected mismatch between example.com and xn----dtbqigoecuc.xn--p1ai"
);
}

}

0 comments on commit 83bdee3

Please sign in to comment.