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 org.apache.hc.client5.http.psl.DomainType;
31 import org.apache.hc.client5.http.psl.PublicSuffixList;
32 import org.apache.hc.client5.http.psl.PublicSuffixListParser;
33 import org.apache.hc.client5.http.psl.PublicSuffixMatcher;
34 import org.junit.Assert;
35 import org.junit.Before;
36 import org.junit.Test;
37
38 import javax.net.ssl.SSLException;
39 import java.io.ByteArrayInputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.InputStreamReader;
43 import java.nio.charset.StandardCharsets;
44 import java.security.cert.CertificateFactory;
45 import java.security.cert.X509Certificate;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.List;
49
50
51
52
53 public class TestDefaultHostnameVerifier {
54
55 private DefaultHostnameVerifier impl;
56 private PublicSuffixMatcher publicSuffixMatcher;
57 private DefaultHostnameVerifier implWithPublicSuffixCheck;
58
59 private static final String PUBLIC_SUFFIX_MATCHER_SOURCE_FILE = "suffixlistmatcher.txt";
60
61 @Before
62 public void setup() throws IOException {
63 impl = new DefaultHostnameVerifier();
64
65
66 final ClassLoader classLoader = getClass().getClassLoader();
67 final InputStream in = classLoader.getResourceAsStream(PUBLIC_SUFFIX_MATCHER_SOURCE_FILE);
68 Assert.assertNotNull(in);
69 final List<PublicSuffixList> lists = new PublicSuffixListParser().parseByType(
70 new InputStreamReader(in, StandardCharsets.UTF_8));
71 publicSuffixMatcher = new PublicSuffixMatcher(lists);
72
73 implWithPublicSuffixCheck = new DefaultHostnameVerifier(publicSuffixMatcher);
74 }
75
76 @Test
77 public void testVerify() throws Exception {
78 final CertificateFactory cf = CertificateFactory.getInstance("X.509");
79 InputStream in;
80 X509Certificate x509;
81 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO);
82 x509 = (X509Certificate) cf.generateCertificate(in);
83
84 impl.verify("foo.com", x509);
85 exceptionPlease(impl, "a.foo.com", x509);
86 exceptionPlease(impl, "bar.com", x509);
87
88 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_HANAKO);
89 x509 = (X509Certificate) cf.generateCertificate(in);
90 impl.verify("\u82b1\u5b50.co.jp", x509);
91 exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
92
93 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR);
94 x509 = (X509Certificate) cf.generateCertificate(in);
95 exceptionPlease(impl, "foo.com", x509);
96 exceptionPlease(impl, "a.foo.com", x509);
97 impl.verify("bar.com", x509);
98 exceptionPlease(impl, "a.bar.com", x509);
99
100 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR_HANAKO);
101 x509 = (X509Certificate) cf.generateCertificate(in);
102 exceptionPlease(impl, "foo.com", x509);
103 exceptionPlease(impl, "a.foo.com", x509);
104 impl.verify("bar.com", x509);
105 exceptionPlease(impl, "a.bar.com", x509);
106
107
108
109
110
111
112
113 exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
114
115 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
116 x509 = (X509Certificate) cf.generateCertificate(in);
117 impl.verify("foo.com", x509);
118 exceptionPlease(impl, "a.foo.com", x509);
119
120 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
121 x509 = (X509Certificate) cf.generateCertificate(in);
122 impl.verify("foo.com", x509);
123 exceptionPlease(impl, "a.foo.com", x509);
124
125 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_THREE_CNS_FOO_BAR_HANAKO);
126 x509 = (X509Certificate) cf.generateCertificate(in);
127 exceptionPlease(impl, "foo.com", x509);
128 exceptionPlease(impl, "a.foo.com", x509);
129 exceptionPlease(impl, "bar.com", x509);
130 exceptionPlease(impl, "a.bar.com", x509);
131 impl.verify("\u82b1\u5b50.co.jp", x509);
132 exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
133
134 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO);
135 x509 = (X509Certificate) cf.generateCertificate(in);
136 exceptionPlease(impl, "foo.com", x509);
137 impl.verify("www.foo.com", x509);
138 impl.verify("\u82b1\u5b50.foo.com", x509);
139 exceptionPlease(impl, "a.b.foo.com", x509);
140
141 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP);
142 x509 = (X509Certificate) cf.generateCertificate(in);
143
144
145 impl.verify("*.co.jp", x509);
146 impl.verify("foo.co.jp", x509);
147 impl.verify("\u82b1\u5b50.co.jp", x509);
148
149 exceptionPlease(implWithPublicSuffixCheck, "foo.co.jp", x509);
150 exceptionPlease(implWithPublicSuffixCheck, "\u82b1\u5b50.co.jp", x509);
151
152 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO_BAR_HANAKO);
153 x509 = (X509Certificate) cf.generateCertificate(in);
154
155 exceptionPlease(impl, "foo.com", x509);
156 exceptionPlease(impl, "www.foo.com", x509);
157 exceptionPlease(impl, "\u82b1\u5b50.foo.com", x509);
158 exceptionPlease(impl, "a.b.foo.com", x509);
159
160 exceptionPlease(impl, "bar.com", x509);
161 impl.verify("www.bar.com", x509);
162 impl.verify("\u82b1\u5b50.bar.com", x509);
163 exceptionPlease(impl, "a.b.bar.com", x509);
164
165 in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA);
166 x509 = (X509Certificate) cf.generateCertificate(in);
167 impl.verify("repository.infonotary.com", x509);
168
169 in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM);
170 x509 = (X509Certificate) cf.generateCertificate(in);
171 impl.verify("*.google.com", x509);
172
173 in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM);
174 x509 = (X509Certificate) cf.generateCertificate(in);
175 impl.verify("*.Google.com", x509);
176
177 in = new ByteArrayInputStream(CertificatesToPlayWith.IP_1_1_1_1);
178 x509 = (X509Certificate) cf.generateCertificate(in);
179 impl.verify("1.1.1.1", x509);
180
181 exceptionPlease(impl, "1.1.1.2", x509);
182 exceptionPlease(impl, "dummy-value.com", x509);
183
184 in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME);
185 x509 = (X509Certificate) cf.generateCertificate(in);
186 impl.verify("www.company.com", x509);
187 }
188
189 @Test
190 public void testSubjectAlt() throws Exception {
191 final CertificateFactory cf = CertificateFactory.getInstance("X.509");
192 final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_SUBJECT_ALT);
193 final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
194
195 Assert.assertEquals("CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CH",
196 x509.getSubjectDN().getName());
197
198 impl.verify("localhost.localdomain", x509);
199 impl.verify("127.0.0.1", x509);
200
201 try {
202 impl.verify("localhost", x509);
203 Assert.fail("SSLException should have been thrown");
204 } catch (final SSLException ex) {
205
206 }
207 try {
208 impl.verify("local.host", x509);
209 Assert.fail("SSLException should have been thrown");
210 } catch (final SSLException ex) {
211
212 }
213 try {
214 impl.verify("127.0.0.2", x509);
215 Assert.fail("SSLException should have been thrown");
216 } catch (final SSLException ex) {
217
218 }
219 }
220
221 public void exceptionPlease(final DefaultHostnameVerifier hv, final String host,
222 final X509Certificate x509) {
223 try {
224 hv.verify(host, x509);
225 Assert.fail("HostnameVerifier shouldn't allow [" + host + "]");
226 }
227 catch(final SSLException e) {
228
229 }
230 }
231
232 @Test
233 public void testDomainRootMatching() {
234
235 Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", null));
236 Assert.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "a.b.c"));
237 Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("aa.b.c", "a.b.c"));
238 Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "aa.b.c"));
239 Assert.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.a.b.c", "a.b.c"));
240 }
241
242 @Test
243 public void testIdentityMatching() {
244
245 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c"));
246 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.c"));
247
248 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.b.c", "*.b.c"));
249 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.b.c", "*.b.c"));
250
251 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher));
252 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "*.gov.uk", publicSuffixMatcher));
253
254 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
255 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
256
257 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));
258 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));
259
260 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher));
261 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher));
262
263 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher));
264 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.com", "*.gov.com", publicSuffixMatcher));
265
266 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
267 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
268
269 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
270 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
271
272 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.*"));
273 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.*"));
274
275 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.*.c"));
276 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.*.c"));
277 }
278
279 @Test
280 public void testHTTPCLIENT_1097() {
281 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "a*.b.c"));
282 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "a*.b.c"));
283
284 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.a.b.c", "a*.b.c"));
285 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.a.b.c", "a*.b.c"));
286 }
287
288 @Test
289 public void testHTTPCLIENT_1255() {
290 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("mail.a.b.c.com", "m*.a.b.c.com"));
291 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com"));
292 }
293
294 @Test
295 public void testHTTPCLIENT_1997_ANY() {
296 String domain;
297
298 domain = "dev.b.cloud.a";
299 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain));
300 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain));
301 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
302 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
303
304
305 domain = "dev.b.cloud.com";
306 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain));
307 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain));
308 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
309 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
310
311
312 domain = "dev.b.cloud.lan";
313 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain));
314 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain));
315 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
316 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
317 }
318
319 @Test
320 public void testHTTPCLIENT_1997_ICANN() {
321 String domain;
322
323 domain = "dev.b.cloud.a";
324 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
325 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
326
327
328 domain = "dev.b.cloud.com";
329 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
330 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
331
332
333 domain = "dev.b.cloud.lan";
334 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
335 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
336 }
337
338 @Test
339 public void testHTTPCLIENT_1997_PRIVATE() {
340 String domain;
341
342 domain = "dev.b.cloud.a";
343 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
344 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
345
346
347 domain = "dev.b.cloud.com";
348 Assert.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
349 Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
350
351
352 domain = "dev.b.cloud.lan";
353 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
354 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
355 }
356
357 @Test
358 public void testHTTPCLIENT_1997_UNKNOWN() {
359 String domain;
360
361 domain = "dev.b.cloud.a";
362 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
363 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
364
365
366 domain = "dev.b.cloud.com";
367 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
368 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
369
370
371 domain = "dev.b.cloud.lan";
372 Assert.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
373 Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
374 }
375
376 @Test
377 public void testHTTPCLIENT_1316() throws Exception{
378 final String host1 = "2001:0db8:aaaa:bbbb:cccc:0:0:0001";
379 DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
380 DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
381 try {
382 DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10")));
383 Assert.fail("SSLException expected");
384 } catch (final SSLException expected) {
385 }
386 final String host2 = "2001:0db8:aaaa:bbbb:cccc::1";
387 DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
388 DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
389 try {
390 DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10")));
391 Assert.fail("SSLException expected");
392 } catch (final SSLException expected) {
393 }
394 }
395
396 @Test
397 public void testExtractCN() throws Exception {
398 Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah"));
399 Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, cn=yada, cn=booh"));
400 Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = pampa , cn = blah , ou = blah , o = blah"));
401 Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=\"blah\", ou=blah, o=blah"));
402 Assert.assertEquals("blah blah", DefaultHostnameVerifier.extractCN("cn=\"blah blah\", ou=blah, o=blah"));
403 Assert.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=\"blah, blah\", ou=blah, o=blah"));
404 Assert.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=blah\\, blah, ou=blah, o=blah"));
405 Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = cn=uuh, cn=blah, ou=blah, o=blah"));
406 try {
407 DefaultHostnameVerifier.extractCN("blah,blah");
408 Assert.fail("SSLException expected");
409 } catch (final SSLException expected) {
410 }
411 try {
412 DefaultHostnameVerifier.extractCN("cn,o=blah");
413 Assert.fail("SSLException expected");
414 } catch (final SSLException expected) {
415 }
416 }
417
418 @Test
419 public void testMatchDNSName() throws Exception {
420 DefaultHostnameVerifier.matchDNSName(
421 "host.domain.com",
422 Collections.singletonList(SubjectName.DNS("*.domain.com")),
423 publicSuffixMatcher);
424 DefaultHostnameVerifier.matchDNSName(
425 "host.xx",
426 Collections.singletonList(SubjectName.DNS("*.xx")),
427 publicSuffixMatcher);
428 DefaultHostnameVerifier.matchDNSName(
429 "host.appspot.com",
430 Collections.singletonList(SubjectName.DNS("*.appspot.com")),
431 publicSuffixMatcher);
432 DefaultHostnameVerifier.matchDNSName(
433 "demo-s3-bucket.s3.eu-central-1.amazonaws.com",
434 Collections.singletonList(SubjectName.DNS("*.s3.eu-central-1.amazonaws.com")),
435 publicSuffixMatcher);
436 DefaultHostnameVerifier.matchDNSName(
437 "hostname-workspace-1.local",
438 Collections.singletonList(SubjectName.DNS("hostname-workspace-1.local")),
439 publicSuffixMatcher);
440 }
441
442 }