Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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.getDomainRoot(identity, domainType) == null) {
if (publicSuffixMatcher.getDomainRoot(normalizedIdentity, domainType) == null) {
return false;
}
}
Expand All @@ -239,10 +250,11 @@ 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 = normalizedIdentity.indexOf('*');
if (asteriskIdx != -1) {
final String prefix = identity.substring(0, asteriskIdx);
final String suffix = identity.substring(asteriskIdx + 1);
final String prefix = normalizedIdentity.substring(0, asteriskIdx);
final String suffix = normalizedIdentity.substring(asteriskIdx + 1);

if (!prefix.isEmpty() && !host.startsWith(prefix)) {
return false;
}
Expand All @@ -252,12 +264,16 @@ private static boolean matchIdentity(final String host, final String identity,
// 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());
prefix.length(),
host.length() - suffix.length()
);
return !remainder.contains(".");
}
return true;
}
return host.equalsIgnoreCase(identity);

// Direct Unicode comparison
return host.equalsIgnoreCase(normalizedIdentity);
}

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,80 @@ void testMatchDNSName() throws Exception {
publicSuffixMatcher));
}

@Test
void testMatchIdentity() {
// Test 1: IDN matching punycode
final String unicodeHost1 = "поиск-слов.рф";
final String punycodeHost1 = "xn----dtbqigoecuc.xn--p1ai";

// These should now match, thanks to IDN.toASCII():
Assertions.assertTrue(
DefaultHostnameVerifier.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),
"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),
"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),
"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),
"Expected wildcard to match subdomain"
);

// Test 5: Invalid host
final String invalidHost = "invalid_host";
final String unicodeIdentity5 = "пример.рф";
Assertions.assertFalse(
DefaultHostnameVerifier.matchIdentity(invalidHost, unicodeIdentity5),
"Expected invalid host to not match"
);

// Test 6: Invalid identity
final String unicodeHost4b = "пример.рф";
final String invalidIdentity = "xn--invalid-punycode";
Assertions.assertFalse(
DefaultHostnameVerifier.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),
"Expected case-insensitive Unicode comparison to match"
);


// 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),
"Expected wildcard in the middle label to match"
);
}

}
Loading