View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
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.nio.charset.StandardCharsets;
35  import java.security.cert.CertificateFactory;
36  import java.security.cert.X509Certificate;
37  import java.util.Collections;
38  import java.util.List;
39  
40  import javax.net.ssl.SSLException;
41  
42  import org.apache.hc.client5.http.psl.DomainType;
43  import org.apache.hc.client5.http.psl.PublicSuffixList;
44  import org.apache.hc.client5.http.psl.PublicSuffixListParser;
45  import org.apache.hc.client5.http.psl.PublicSuffixMatcher;
46  import org.junit.jupiter.api.Assertions;
47  import org.junit.jupiter.api.BeforeEach;
48  import org.junit.jupiter.api.Test;
49  
50  /**
51   * Unit tests for {@link org.apache.hc.client5.http.ssl.DefaultHostnameVerifier}.
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      @BeforeEach
62      public void setup() throws IOException {
63          impl = new DefaultHostnameVerifier();
64  
65          // Load the test PublicSuffixMatcher
66          final ClassLoader classLoader = getClass().getClassLoader();
67          final InputStream in = classLoader.getResourceAsStream(PUBLIC_SUFFIX_MATCHER_SOURCE_FILE);
68          Assertions.assertNotNull(in);
69          final List<PublicSuffixList> lists = PublicSuffixListParser.INSTANCE.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            Java isn't extracting international subjectAlts properly.  (Or
109            OpenSSL isn't storing them properly).
110         */
111         // DEFAULT.verify("\u82b1\u5b50.co.jp", x509 );
112         // impl.verify("\u82b1\u5b50.co.jp", x509 );
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         // Silly test because no-one would ever be able to lookup an IP address
144         // using "*.co.jp".
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         // try the foo.com variations
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         // try the bar.com variations
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         impl.verify("dummy-value.com", x509);
181 
182         exceptionPlease(impl, "1.1.1.2", x509);
183         exceptionPlease(impl, "not-the-cn.com", x509);
184 
185         in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME);
186         x509 = (X509Certificate) cf.generateCertificate(in);
187         impl.verify("www.company.com", x509);
188     }
189 
190     @Test
191     public void testSubjectAlt() throws Exception {
192         final CertificateFactory cf = CertificateFactory.getInstance("X.509");
193         final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_SUBJECT_ALT);
194         final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
195 
196         Assertions.assertEquals("CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CH",
197                 x509.getSubjectDN().getName());
198 
199         impl.verify("localhost.localdomain", x509);
200         impl.verify("127.0.0.1", x509);
201 
202         Assertions.assertThrows(SSLException.class, () -> impl.verify("localhost", x509));
203         Assertions.assertThrows(SSLException.class, () -> impl.verify("local.host", x509));
204         Assertions.assertThrows(SSLException.class, () -> impl.verify("127.0.0.2", x509));
205     }
206 
207     public void exceptionPlease(final DefaultHostnameVerifier hv, final String host,
208                                 final X509Certificate x509) {
209         Assertions.assertThrows(SSLException.class, () -> hv.verify(host, x509));
210     }
211 
212     @Test
213     public void testDomainRootMatching() {
214 
215         Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", null));
216         Assertions.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "a.b.c"));
217         Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("aa.b.c", "a.b.c"));
218         Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "aa.b.c"));
219         Assertions.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.a.b.c", "a.b.c"));
220     }
221 
222     @Test
223     public void testIdentityMatching() {
224 
225         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c"));
226         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.c"));
227 
228         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.b.c", "*.b.c"));
229         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.b.c", "*.b.c")); // subdomain not OK
230 
231         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher));
232         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "*.gov.uk", publicSuffixMatcher));  // Bad 2TLD
233 
234         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
235         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
236 
237         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));
238         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));  // BBad 2TLD/no subdomain allowed
239 
240         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher));
241         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher));
242 
243         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher));
244         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); // no subdomain allowed
245 
246         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
247         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD
248 
249         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD
250         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD/no subdomain allowed
251 
252         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.*"));
253         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.*"));
254 
255         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.*.c"));
256         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.*.c"));
257 
258         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher));
259         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher));
260 
261         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher));
262         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher));
263 
264         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher));
265         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher));
266 
267         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher));
268         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher));
269     }
270 
271     @Test
272     public void testHTTPCLIENT_1097() {
273         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "a*.b.c"));
274         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "a*.b.c"));
275 
276         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.a.b.c", "a*.b.c"));
277         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.a.b.c", "a*.b.c"));
278     }
279 
280     @Test
281     public void testHTTPCLIENT_1255() {
282         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("mail.a.b.c.com", "m*.a.b.c.com"));
283         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com"));
284     }
285 
286     @Test
287     public void testHTTPCLIENT_1997_ANY() { // Only True on all domains
288         String domain;
289         // Unknown
290         domain = "dev.b.cloud.a";
291         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain));
292         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain));
293         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
294         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
295 
296         // ICANN
297         domain = "dev.b.cloud.com";
298         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain));
299         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain));
300         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
301         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
302 
303         // PRIVATE
304         domain = "dev.b.cloud.lan";
305         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain));
306         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain));
307         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
308         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher));
309     }
310 
311     @Test
312     public void testHTTPCLIENT_1997_ICANN() { // Only True on ICANN domains
313         String domain;
314         // Unknown
315         domain = "dev.b.cloud.a";
316         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
317         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
318 
319         // ICANN
320         domain = "dev.b.cloud.com";
321         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
322         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
323 
324         // PRIVATE
325         domain = "dev.b.cloud.lan";
326         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
327         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN));
328     }
329 
330     @Test
331     public void testHTTPCLIENT_1997_PRIVATE() { // Only True on PRIVATE domains
332         String domain;
333         // Unknown
334         domain = "dev.b.cloud.a";
335         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
336         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
337 
338         // ICANN
339         domain = "dev.b.cloud.com";
340         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
341         Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
342 
343         // PRIVATE
344         domain = "dev.b.cloud.lan";
345         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
346         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE));
347     }
348 
349     @Test
350     public void testHTTPCLIENT_1997_UNKNOWN() { // Only True on all domains (same as ANY)
351         String domain;
352         // Unknown
353         domain = "dev.b.cloud.a";
354         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
355         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
356 
357         // ICANN
358         domain = "dev.b.cloud.com";
359         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
360         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
361 
362         // PRIVATE
363         domain = "dev.b.cloud.lan";
364         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity(        "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
365         Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict(  "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN));
366     }
367 
368     @Test // Check compressed IPv6 hostname matching
369     public void testHTTPCLIENT_1316() throws Exception{
370         final String host1 = "2001:0db8:aaaa:bbbb:cccc:0:0:0001";
371         DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
372         DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
373         Assertions.assertThrows(SSLException.class, () ->
374                 DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10"))));
375         final String host2 = "2001:0db8:aaaa:bbbb:cccc::1";
376         DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
377         DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
378         Assertions.assertThrows(SSLException.class, () ->
379                 DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10"))));
380     }
381 
382     @Test
383     public void testHTTPCLIENT_2149() throws Exception {
384         final CertificateFactory cf = CertificateFactory.getInstance("X.509");
385         final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.SUBJECT_ALT_IP_ONLY);
386         final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
387 
388         Assertions.assertEquals("CN=www.foo.com", x509.getSubjectDN().getName());
389 
390         impl.verify("127.0.0.1", x509);
391         impl.verify("www.foo.com", x509);
392 
393         exceptionPlease(impl, "127.0.0.2", x509);
394         exceptionPlease(impl, "www.bar.com", x509);
395     }
396 
397     @Test
398     public void testExtractCN() throws Exception {
399         Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah"));
400         Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, cn=yada, cn=booh"));
401         Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = pampa ,  cn  =    blah    , ou = blah , o = blah"));
402         Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=\"blah\", ou=blah, o=blah"));
403         Assertions.assertEquals("blah  blah", DefaultHostnameVerifier.extractCN("cn=\"blah  blah\", ou=blah, o=blah"));
404         Assertions.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=\"blah, blah\", ou=blah, o=blah"));
405         Assertions.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=blah\\, blah, ou=blah, o=blah"));
406         Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = cn=uuh, cn=blah, ou=blah, o=blah"));
407         Assertions.assertThrows(SSLException.class, () ->
408                 DefaultHostnameVerifier.extractCN("blah,blah"));
409         Assertions.assertThrows(SSLException.class, () ->
410                 DefaultHostnameVerifier.extractCN("cn,o=blah"));
411     }
412 
413     @Test
414     public void testMatchDNSName() throws Exception {
415         DefaultHostnameVerifier.matchDNSName(
416                 "host.domain.com",
417                 Collections.singletonList(SubjectName.DNS("*.domain.com")),
418                 publicSuffixMatcher);
419         DefaultHostnameVerifier.matchDNSName(
420                 "host.xx",
421                 Collections.singletonList(SubjectName.DNS("*.xx")),
422                 publicSuffixMatcher);
423         DefaultHostnameVerifier.matchDNSName(
424                 "host.appspot.com",
425                 Collections.singletonList(SubjectName.DNS("*.appspot.com")),
426                 publicSuffixMatcher);
427         DefaultHostnameVerifier.matchDNSName(
428                 "demo-s3-bucket.s3.eu-central-1.amazonaws.com",
429                 Collections.singletonList(SubjectName.DNS("*.s3.eu-central-1.amazonaws.com")),
430                 publicSuffixMatcher);
431         DefaultHostnameVerifier.matchDNSName(
432                 "hostname-workspace-1.local",
433                 Collections.singletonList(SubjectName.DNS("hostname-workspace-1.local")),
434                 publicSuffixMatcher);
435 
436         Assertions.assertThrows(SSLException.class, () ->
437                 DefaultHostnameVerifier.matchDNSName(
438                         "host.domain.com",
439                         Collections.singletonList(SubjectName.DNS("some.other.com")),
440                         publicSuffixMatcher));
441 
442         DefaultHostnameVerifier.matchDNSName(
443                 "host.ec2.compute-1.amazonaws.com",
444                 Collections.singletonList(SubjectName.DNS("host.ec2.compute-1.amazonaws.com")),
445                 publicSuffixMatcher);
446         DefaultHostnameVerifier.matchDNSName(
447                 "host.ec2.compute-1.amazonaws.com",
448                 Collections.singletonList(SubjectName.DNS("*.ec2.compute-1.amazonaws.com")),
449                 publicSuffixMatcher);
450         DefaultHostnameVerifier.matchDNSName(
451                 "ec2.compute-1.amazonaws.com",
452                 Collections.singletonList(SubjectName.DNS("ec2.compute-1.amazonaws.com")),
453                 publicSuffixMatcher);
454         Assertions.assertThrows(SSLException.class, () ->
455                 DefaultHostnameVerifier.matchDNSName(
456                         "ec2.compute-1.amazonaws.com",
457                         Collections.singletonList(SubjectName.DNS("*.compute-1.amazonaws.com")),
458                         publicSuffixMatcher));
459     }
460 
461 }