1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.client5.http.ssl;
29
30 import java.io.ByteArrayInputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.InputStreamReader;
34 import java.math.BigInteger;
35 import java.net.InetAddress;
36 import java.nio.charset.StandardCharsets;
37 import java.security.InvalidKeyException;
38 import java.security.NoSuchAlgorithmException;
39 import java.security.NoSuchProviderException;
40 import java.security.Principal;
41 import java.security.PublicKey;
42 import java.security.SignatureException;
43 import java.security.cert.CertificateEncodingException;
44 import java.security.cert.CertificateException;
45 import java.security.cert.CertificateExpiredException;
46 import java.security.cert.CertificateFactory;
47 import java.security.cert.CertificateNotYetValidException;
48 import java.security.cert.X509Certificate;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.Date;
54 import java.util.List;
55 import java.util.Set;
56
57 import javax.net.ssl.SSLException;
58
59 import org.apache.hc.client5.http.psl.PublicSuffixList;
60 import org.apache.hc.client5.http.psl.PublicSuffixListParser;
61 import org.apache.hc.client5.http.psl.PublicSuffixMatcher;
62 import org.apache.hc.client5.http.utils.DnsUtils;
63 import org.junit.jupiter.api.Assertions;
64 import org.junit.jupiter.api.BeforeEach;
65 import org.junit.jupiter.api.Test;
66
67
68
69
70 class TestDefaultHostnameVerifier {
71
72 private DefaultHostnameVerifier impl;
73 private PublicSuffixMatcher publicSuffixMatcher;
74
75 private static final String PUBLIC_SUFFIX_MATCHER_SOURCE_FILE = "suffixlistmatcher.txt";
76
77 @BeforeEach
78 void setup() throws IOException {
79 impl = new DefaultHostnameVerifier();
80
81
82 final ClassLoader classLoader = getClass().getClassLoader();
83 final InputStream in = classLoader.getResourceAsStream(PUBLIC_SUFFIX_MATCHER_SOURCE_FILE);
84 Assertions.assertNotNull(in);
85 final List<PublicSuffixList> lists = PublicSuffixListParser.INSTANCE.parseByType(
86 new InputStreamReader(in, StandardCharsets.UTF_8));
87 publicSuffixMatcher = new PublicSuffixMatcher(lists);
88 }
89
90 @Test
91 void testVerify() throws Exception {
92 final CertificateFactory cf = CertificateFactory.getInstance("X.509");
93 InputStream in;
94 X509Certificate x509;
95 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO);
96 x509 = (X509Certificate) cf.generateCertificate(in);
97
98 impl.verify("foo.com", x509);
99 exceptionPlease(impl, "a.foo.com", x509);
100 exceptionPlease(impl, "bar.com", x509);
101
102 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_HANAKO);
103 x509 = (X509Certificate) cf.generateCertificate(in);
104 impl.verify("\u82b1\u5b50.co.jp", x509);
105 exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
106
107 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR);
108 x509 = (X509Certificate) cf.generateCertificate(in);
109 exceptionPlease(impl, "foo.com", x509);
110 exceptionPlease(impl, "a.foo.com", x509);
111 impl.verify("bar.com", x509);
112 exceptionPlease(impl, "a.bar.com", x509);
113
114 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR_HANAKO);
115 x509 = (X509Certificate) cf.generateCertificate(in);
116 exceptionPlease(impl, "foo.com", x509);
117 exceptionPlease(impl, "a.foo.com", x509);
118 impl.verify("bar.com", x509);
119 exceptionPlease(impl, "a.bar.com", x509);
120
121
122
123
124
125
126
127 exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
128
129 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
130 x509 = (X509Certificate) cf.generateCertificate(in);
131 impl.verify("foo.com", x509);
132 exceptionPlease(impl, "a.foo.com", x509);
133
134 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
135 x509 = (X509Certificate) cf.generateCertificate(in);
136 impl.verify("foo.com", x509);
137 exceptionPlease(impl, "a.foo.com", x509);
138
139 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_THREE_CNS_FOO_BAR_HANAKO);
140 x509 = (X509Certificate) cf.generateCertificate(in);
141 exceptionPlease(impl, "foo.com", x509);
142 exceptionPlease(impl, "a.foo.com", x509);
143 exceptionPlease(impl, "bar.com", x509);
144 exceptionPlease(impl, "a.bar.com", x509);
145 impl.verify("\u82b1\u5b50.co.jp", x509);
146 exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
147
148 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO);
149 x509 = (X509Certificate) cf.generateCertificate(in);
150 exceptionPlease(impl, "foo.com", x509);
151 impl.verify("www.foo.com", x509);
152 impl.verify("\u82b1\u5b50.foo.com", x509);
153 exceptionPlease(impl, "a.b.foo.com", x509);
154
155 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP);
156 x509 = (X509Certificate) cf.generateCertificate(in);
157
158
159 impl.verify("*.co.jp", x509);
160 impl.verify("foo.co.jp", x509);
161 impl.verify("\u82b1\u5b50.co.jp", x509);
162
163 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO_BAR_HANAKO);
164 x509 = (X509Certificate) cf.generateCertificate(in);
165
166 exceptionPlease(impl, "foo.com", x509);
167 exceptionPlease(impl, "www.foo.com", x509);
168 exceptionPlease(impl, "\u82b1\u5b50.foo.com", x509);
169 exceptionPlease(impl, "a.b.foo.com", x509);
170
171 exceptionPlease(impl, "bar.com", x509);
172 impl.verify("www.bar.com", x509);
173 impl.verify("\u82b1\u5b50.bar.com", x509);
174 exceptionPlease(impl, "a.b.bar.com", x509);
175
176 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA);
177 x509 = (X509Certificate) cf.generateCertificate(in);
178 impl.verify("repository.infonotary.com", x509);
179
180 in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM);
181 x509 = (X509Certificate) cf.generateCertificate(in);
182 impl.verify("*.google.com", x509);
183
184 in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM);
185 x509 = (X509Certificate) cf.generateCertificate(in);
186 impl.verify("*.Google.com", x509);
187
188 in = new ByteArrayInputStream(CertificatesToPlayWith.IP_1_1_1_1);
189 x509 = (X509Certificate) cf.generateCertificate(in);
190 impl.verify("1.1.1.1", x509);
191 impl.verify("dummy-value.com", x509);
192
193 exceptionPlease(impl, "1.1.1.2", x509);
194 exceptionPlease(impl, "not-the-cn.com", x509);
195
196 in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME);
197 x509 = (X509Certificate) cf.generateCertificate(in);
198 impl.verify("www.company.com", x509);
199 }
200
201 @Test
202 void testSubjectAlt() throws Exception {
203 final CertificateFactory cf = CertificateFactory.getInstance("X.509");
204 final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_SUBJECT_ALT);
205 final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
206
207 Assertions.assertEquals("CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CH",
208 x509.getSubjectDN().getName());
209
210 impl.verify("localhost.localdomain", x509);
211 impl.verify("127.0.0.1", x509);
212
213 Assertions.assertThrows(SSLException.class, () -> impl.verify("localhost", x509));
214 Assertions.assertThrows(SSLException.class, () -> impl.verify("local.host", x509));
215 Assertions.assertThrows(SSLException.class, () -> impl.verify("127.0.0.2", x509));
216 }
217
218 public void exceptionPlease(final DefaultHostnameVerifier hv, final String host,
219 final X509Certificate x509) {
220 Assertions.assertThrows(SSLException.class, () -> hv.verify(host, x509));
221 }
222
223 @Test
224 void testParseFQDN() {
225 Assertions.assertEquals(Arrays.asList("blah"),
226 DefaultHostnameVerifier.parseFQDN("blah"));
227 Assertions.assertEquals(Arrays.asList("blah", "blah"),
228 DefaultHostnameVerifier.parseFQDN("blah.blah"));
229 Assertions.assertEquals(Arrays.asList("blah", "blah", "blah"),
230 DefaultHostnameVerifier.parseFQDN("blah.blah.blah"));
231 Assertions.assertEquals(Arrays.asList("", "", "blah", ""),
232 DefaultHostnameVerifier.parseFQDN(".blah.."));
233 Assertions.assertEquals(Arrays.asList(""),
234 DefaultHostnameVerifier.parseFQDN(""));
235 Assertions.assertEquals(Arrays.asList("", ""),
236 DefaultHostnameVerifier.parseFQDN("."));
237 Assertions.assertEquals(Arrays.asList("com", "domain", "host"),
238 DefaultHostnameVerifier.parseFQDN("host.domain.com"));
239 }
240
241 @Test
242 void testDomainRootMatching() {
243 Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", null));
244 Assertions.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "a.b.c"));
245 Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("aa.b.c", "a.b.c"));
246 Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "aa.b.c"));
247 Assertions.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.a.b.c", "a.b.c"));
248 }
249
250 static boolean matchIdentity(final String host, final String identity,
251 final PublicSuffixMatcher publicSuffixMatcher, final boolean strict) {
252 return DefaultHostnameVerifier.matchIdentity(
253 DnsUtils.normalizeUnicode(host),
254 DnsUtils.normalizeUnicode(identity),
255 publicSuffixMatcher, strict);
256 }
257
258 static boolean matchIdentity(final String host, final String identity,
259 final PublicSuffixMatcher publicSuffixMatcher) {
260 return matchIdentity(host, identity, publicSuffixMatcher, false);
261 }
262
263 static boolean matchIdentity(final String host, final String identity) {
264 return matchIdentity(host, identity, null, false);
265 }
266
267 static boolean matchIdentityStrict(final String host, final String identity,
268 final PublicSuffixMatcher publicSuffixMatcher) {
269 return matchIdentity(host, identity, publicSuffixMatcher, true);
270 }
271
272 static boolean matchIdentityStrict(final String host, final String identity) {
273 return matchIdentity(host, identity, null, true);
274 }
275
276 @Test
277 void testIdentityMatching() {
278
279 Assertions.assertTrue(matchIdentity("a.b.c", "*.b.c"));
280 Assertions.assertTrue(matchIdentityStrict("a.b.c", "*.b.c"));
281
282 Assertions.assertTrue(matchIdentity("s.a.b.c", "*.b.c"));
283 Assertions.assertFalse(matchIdentityStrict("s.a.b.c", "*.b.c"));
284
285 Assertions.assertTrue(matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher));
286 Assertions.assertTrue(matchIdentityStrict("a.gov.uk", "*.gov.uk", publicSuffixMatcher));
287
288 Assertions.assertTrue(matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
289 Assertions.assertTrue(matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
290
291 Assertions.assertTrue(matchIdentity("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));
292 Assertions.assertFalse(matchIdentityStrict("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));
293
294 Assertions.assertTrue(matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher));
295 Assertions.assertTrue(matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher));
296
297 Assertions.assertTrue(matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher));
298 Assertions.assertFalse(matchIdentityStrict("s.a.gov.com", "*.gov.com", publicSuffixMatcher));
299
300 Assertions.assertTrue(matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
301 Assertions.assertTrue(matchIdentityStrict("a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
302
303 Assertions.assertFalse(matchIdentity("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
304 Assertions.assertFalse(matchIdentityStrict("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
305
306 Assertions.assertFalse(matchIdentity("a.b.c", "*.b.*"));
307 Assertions.assertFalse(matchIdentityStrict("a.b.c", "*.b.*"));
308
309 Assertions.assertFalse(matchIdentity("a.b.c", "*.*.c"));
310 Assertions.assertFalse(matchIdentityStrict("a.b.c", "*.*.c"));
311
312 Assertions.assertTrue(matchIdentity("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher));
313 Assertions.assertTrue(matchIdentityStrict("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher));
314
315 Assertions.assertTrue(matchIdentity("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher));
316 Assertions.assertTrue(matchIdentityStrict("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher));
317
318 Assertions.assertTrue(matchIdentity("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher));
319 Assertions.assertTrue(matchIdentityStrict("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher));
320
321 Assertions.assertTrue(matchIdentity("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher));
322 Assertions.assertTrue(matchIdentityStrict("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher));
323 }
324
325 @Test
326 void testHTTPCLIENT_1097() {
327 Assertions.assertTrue(matchIdentity("a.b.c", "a*.b.c"));
328 Assertions.assertTrue(matchIdentityStrict("a.b.c", "a*.b.c"));
329
330 Assertions.assertTrue(matchIdentity("a.a.b.c", "a*.b.c"));
331 Assertions.assertFalse(matchIdentityStrict("a.a.b.c", "a*.b.c"));
332 }
333
334 @Test
335 void testHTTPCLIENT_1255() {
336 Assertions.assertTrue(matchIdentity("mail.a.b.c.com", "m*.a.b.c.com"));
337 Assertions.assertTrue(matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com"));
338 }
339
340 @Test
341 void testHTTPCLIENT_1997() {
342 String domain;
343
344 domain = "dev.b.cloud.a";
345 Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain));
346 Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain));
347 Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
348 Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
349
350
351 domain = "dev.b.cloud.com";
352 Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain));
353 Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain));
354 Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
355 Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
356
357
358 domain = "dev.b.cloud.lan";
359 Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain));
360 Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain));
361 Assertions.assertTrue(matchIdentity("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
362 Assertions.assertTrue(matchIdentityStrict("service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
363 }
364
365 @Test
366 void testHTTPCLIENT_1316() throws Exception {
367 final String host1 = "2001:0db8:aaaa:bbbb:cccc:0:0:0001";
368 DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
369 DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
370 Assertions.assertThrows(SSLException.class, () ->
371 DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10"))));
372 final String host2 = "2001:0db8:aaaa:bbbb:cccc::1";
373 DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
374 DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
375 Assertions.assertThrows(SSLException.class, () ->
376 DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10"))));
377 }
378
379 @Test
380 void testHTTPCLIENT_2149() throws Exception {
381 final CertificateFactory cf = CertificateFactory.getInstance("X.509");
382 final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.SUBJECT_ALT_IP_ONLY);
383 final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
384
385 Assertions.assertEquals("CN=www.foo.com", x509.getSubjectDN().getName());
386
387 impl.verify("127.0.0.1", x509);
388 impl.verify("www.foo.com", x509);
389
390 exceptionPlease(impl, "127.0.0.2", x509);
391 exceptionPlease(impl, "www.bar.com", x509);
392 }
393
394 @Test
395 void testExtractCN() throws Exception {
396 Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah"));
397 Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, cn=yada, cn=booh"));
398 Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = pampa , cn = blah , ou = blah , o = blah"));
399 Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=\"blah\", ou=blah, o=blah"));
400 Assertions.assertEquals("blah blah", DefaultHostnameVerifier.extractCN("cn=\"blah blah\", ou=blah, o=blah"));
401 Assertions.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=\"blah, blah\", ou=blah, o=blah"));
402 Assertions.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=blah\\, blah, ou=blah, o=blah"));
403 Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = cn=uuh, cn=blah, ou=blah, o=blah"));
404 Assertions.assertThrows(SSLException.class, () ->
405 DefaultHostnameVerifier.extractCN("blah,blah"));
406 Assertions.assertThrows(SSLException.class, () ->
407 DefaultHostnameVerifier.extractCN("cn,o=blah"));
408 }
409
410 @Test
411 void testMatchDNSName() throws Exception {
412 DefaultHostnameVerifier.matchDNSName(
413 "host.domain.com",
414 Collections.singletonList(SubjectName.DNS("*.domain.com")),
415 publicSuffixMatcher);
416 DefaultHostnameVerifier.matchDNSName(
417 "host.xx",
418 Collections.singletonList(SubjectName.DNS("*.xx")),
419 publicSuffixMatcher);
420 DefaultHostnameVerifier.matchDNSName(
421 "host.appspot.com",
422 Collections.singletonList(SubjectName.DNS("*.appspot.com")),
423 publicSuffixMatcher);
424 DefaultHostnameVerifier.matchDNSName(
425 "demo-s3-bucket.s3.eu-central-1.amazonaws.com",
426 Collections.singletonList(SubjectName.DNS("*.s3.eu-central-1.amazonaws.com")),
427 publicSuffixMatcher);
428 DefaultHostnameVerifier.matchDNSName(
429 "hostname-workspace-1.local",
430 Collections.singletonList(SubjectName.DNS("hostname-workspace-1.local")),
431 publicSuffixMatcher);
432
433 Assertions.assertThrows(SSLException.class, () ->
434 DefaultHostnameVerifier.matchDNSName(
435 "host.domain.com",
436 Collections.singletonList(SubjectName.DNS("some.other.com")),
437 publicSuffixMatcher));
438
439 DefaultHostnameVerifier.matchDNSName(
440 "host.ec2.compute-1.amazonaws.com",
441 Collections.singletonList(SubjectName.DNS("host.ec2.compute-1.amazonaws.com")),
442 publicSuffixMatcher);
443 DefaultHostnameVerifier.matchDNSName(
444 "host.ec2.compute-1.amazonaws.com",
445 Collections.singletonList(SubjectName.DNS("*.ec2.compute-1.amazonaws.com")),
446 publicSuffixMatcher);
447 Assertions.assertThrows(SSLException.class, () ->
448 DefaultHostnameVerifier.matchDNSName(
449 "compute-1.amazonaws.com",
450 Collections.singletonList(SubjectName.DNS("*.compute-1.amazonaws.com")),
451 publicSuffixMatcher));
452 DefaultHostnameVerifier.matchDNSName(
453 "ec2.compute-1.amazonaws.com",
454 Collections.singletonList(SubjectName.DNS("*.compute-1.amazonaws.com")),
455 publicSuffixMatcher);
456 }
457
458 @Test
459 void testMatchIdentity() {
460
461 final String unicodeHost1 = "поиск-слов.рф";
462 final String punycodeHost1 = "xn----dtbqigoecuc.xn--p1ai";
463
464
465 Assertions.assertTrue(matchIdentity(unicodeHost1, punycodeHost1),
466 "Expected the Unicode host and its punycode to match"
467 );
468
469
470 Assertions.assertFalse(
471 matchIdentity("example.com", punycodeHost1),
472 "Expected mismatch between example.com and xn----dtbqigoecuc.xn--p1ai"
473 );
474
475
476 final String unicodeHost2 = "пример.рф";
477 final String unicodeIdentity2 = "пример.рф";
478 Assertions.assertTrue(matchIdentity(unicodeHost2, unicodeIdentity2),
479 "Expected Unicode host and Unicode identity to match"
480 );
481
482
483 final String unicodeHost3 = "пример.рф";
484 final String punycodeIdentity3 = "xn--e1afmkfd.xn--p1ai";
485 Assertions.assertTrue(matchIdentity(unicodeHost3, punycodeIdentity3),
486 "Expected Unicode host and punycode identity to match"
487 );
488
489
490 final String unicodeHost4 = "sub.пример.рф";
491 final String unicodeIdentity4 = "*.пример.рф";
492 Assertions.assertTrue(matchIdentity(unicodeHost4, unicodeIdentity4),
493 "Expected wildcard to match subdomain"
494 );
495
496
497 final String invalidHost = "invalid_host";
498 final String unicodeIdentity5 = "пример.рф";
499 Assertions.assertFalse(
500 matchIdentity(invalidHost, unicodeIdentity5),
501 "Expected invalid host to not match"
502 );
503
504
505 final String unicodeHost4b = "пример.рф";
506 final String invalidIdentity = "xn--invalid-punycode";
507 Assertions.assertFalse(
508 matchIdentity(unicodeHost4b, invalidIdentity),
509 "Expected invalid identity to not match"
510 );
511
512
513 final String unicodeHost5 = "ПрИмеР.рф";
514 final String unicodeIdentity6 = "пример.рф";
515 Assertions.assertTrue(matchIdentity(unicodeHost5, unicodeIdentity6),
516 "Expected case-insensitive Unicode comparison to match"
517 );
518
519
520
521 final String unicodeHost6 = "sub.пример.рф";
522 final String unicodeIdentity8 = "sub.*.рф";
523 Assertions.assertTrue(matchIdentity(unicodeHost6, unicodeIdentity8),
524 "Expected wildcard in the middle label to match"
525 );
526 }
527
528
529 @Test
530 void testSimulatedByteProperties() throws Exception {
531
532 final byte[] ipAsByteArray = {1, 1, 1, 1};
533
534 final List<List<?>> entries = new ArrayList<>();
535 final List<Object> entry = new ArrayList<>();
536 entry.add(SubjectName.IP);
537 entry.add(ipAsByteArray);
538 entries.add(entry);
539
540
541 final X509Certificate mockCert = generateX509Certificate(entries);
542
543 final List<SubjectName> result = DefaultHostnameVerifier.getSubjectAltNames(mockCert, -1);
544 Assertions.assertEquals(1, result.size(), "Should have one SubjectAltName");
545
546 final SubjectName sn = result.get(0);
547 Assertions.assertEquals(SubjectName.IP, sn.getType(), "Should be an IP type");
548
549 Assertions.assertEquals("1.1.1.1", sn.getValue(), "IP address should match after conversion");
550 }
551
552 @Test
553 void testSimulatedBytePropertiesIPv6() throws Exception {
554 final byte[] ipv6AsByteArray = InetAddress.getByName("2001:db8:85a3::8a2e:370:7334").getAddress();
555
556
557 final List<List<?>> entries = new ArrayList<>();
558 final List<Object> entry = new ArrayList<>();
559 entry.add(SubjectName.IP);
560 entry.add(ipv6AsByteArray);
561 entries.add(entry);
562
563
564 final X509Certificate mockCert = generateX509Certificate(entries);
565
566 final List<SubjectName> result = DefaultHostnameVerifier.getSubjectAltNames(mockCert, -1);
567 Assertions.assertEquals(1, result.size(), "Should have one SubjectAltName");
568
569 final SubjectName sn = result.get(0);
570 Assertions.assertEquals(SubjectName.IP, sn.getType(), "Should be an IP type");
571
572 Assertions.assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", sn.getValue(), "IP address should match after conversion");
573 }
574
575
576 private X509Certificate generateX509Certificate(final List<List<?>> entries) {
577 return new X509Certificate() {
578
579 @Override
580 public boolean hasUnsupportedCriticalExtension() {
581 return false;
582 }
583
584 @Override
585 public Set<String> getCriticalExtensionOIDs() {
586 return null;
587 }
588
589 @Override
590 public Set<String> getNonCriticalExtensionOIDs() {
591 return null;
592 }
593
594 @Override
595 public byte[] getExtensionValue(final String oid) {
596 return new byte[0];
597 }
598
599 @Override
600 public byte[] getEncoded() throws CertificateEncodingException {
601 return new byte[0];
602 }
603
604 @Override
605 public void verify(final PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {
606
607 }
608
609 @Override
610 public void verify(final PublicKey key, final String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {
611
612 }
613
614 @Override
615 public String toString() {
616 return "";
617 }
618
619 @Override
620 public PublicKey getPublicKey() {
621 return null;
622 }
623
624 @Override
625 public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
626
627 }
628
629 @Override
630 public void checkValidity(final Date date) throws CertificateExpiredException, CertificateNotYetValidException {
631
632 }
633
634 @Override
635 public int getVersion() {
636 return 0;
637 }
638
639 @Override
640 public BigInteger getSerialNumber() {
641 return null;
642 }
643
644 @Override
645 public Principal getIssuerDN() {
646 return null;
647 }
648
649 @Override
650 public Principal getSubjectDN() {
651 return null;
652 }
653
654 @Override
655 public Date getNotBefore() {
656 return null;
657 }
658
659 @Override
660 public Date getNotAfter() {
661 return null;
662 }
663
664 @Override
665 public byte[] getTBSCertificate() throws CertificateEncodingException {
666 return new byte[0];
667 }
668
669 @Override
670 public byte[] getSignature() {
671 return new byte[0];
672 }
673
674 @Override
675 public String getSigAlgName() {
676 return "";
677 }
678
679 @Override
680 public String getSigAlgOID() {
681 return "";
682 }
683
684 @Override
685 public byte[] getSigAlgParams() {
686 return new byte[0];
687 }
688
689 @Override
690 public boolean[] getIssuerUniqueID() {
691 return new boolean[0];
692 }
693
694 @Override
695 public boolean[] getSubjectUniqueID() {
696 return new boolean[0];
697 }
698
699 @Override
700 public boolean[] getKeyUsage() {
701 return new boolean[0];
702 }
703
704 @Override
705 public int getBasicConstraints() {
706 return 0;
707 }
708
709 @Override
710 public Collection<List<?>> getSubjectAlternativeNames() {
711 return entries;
712 }
713 };
714
715 }
716
717 }