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.http.conn.ssl;
29  
30  import java.io.ByteArrayInputStream;
31  import java.io.InputStream;
32  import java.security.cert.CertificateFactory;
33  import java.security.cert.X509Certificate;
34  import java.util.Arrays;
35  
36  import javax.net.ssl.SSLException;
37  
38  import org.apache.http.conn.util.DomainType;
39  import org.apache.http.conn.util.PublicSuffixMatcher;
40  import org.junit.Assert;
41  import org.junit.Before;
42  import org.junit.Test;
43  
44  /**
45   * Unit tests for {@link org.apache.http.conn.ssl.DefaultHostnameVerifier}.
46   */
47  public class TestDefaultHostnameVerifier {
48  
49      private DefaultHostnameVerifier impl;
50      private PublicSuffixMatcher publicSuffixMatcher;
51      private DefaultHostnameVerifier implWithPublicSuffixCheck;
52  
53      @Before
54      public void setup() {
55          impl = new DefaultHostnameVerifier();
56          publicSuffixMatcher = new PublicSuffixMatcher(DomainType.ICANN, Arrays.asList("com", "co.jp", "gov.uk"), null);
57          implWithPublicSuffixCheck = new DefaultHostnameVerifier(publicSuffixMatcher);
58      }
59  
60      @Test
61      public void testVerify() throws Exception {
62          final CertificateFactory cf = CertificateFactory.getInstance("X.509");
63          InputStream in;
64          X509Certificate x509;
65          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO);
66          x509 = (X509Certificate) cf.generateCertificate(in);
67  
68          impl.verify("foo.com", x509);
69          exceptionPlease(impl, "a.foo.com", x509);
70          exceptionPlease(impl, "bar.com", x509);
71  
72          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_HANAKO);
73          x509 = (X509Certificate) cf.generateCertificate(in);
74          impl.verify("\u82b1\u5b50.co.jp", x509);
75          exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
76  
77          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR);
78          x509 = (X509Certificate) cf.generateCertificate(in);
79          exceptionPlease(impl, "foo.com", x509);
80          exceptionPlease(impl, "a.foo.com", x509);
81          impl.verify("bar.com", x509);
82          exceptionPlease(impl, "a.bar.com", x509);
83  
84          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR_HANAKO);
85          x509 = (X509Certificate) cf.generateCertificate(in);
86          exceptionPlease(impl, "foo.com", x509);
87          exceptionPlease(impl, "a.foo.com", x509);
88          impl.verify("bar.com", x509);
89          exceptionPlease(impl, "a.bar.com", x509);
90  
91          /*
92             Java isn't extracting international subjectAlts properly.  (Or
93             OpenSSL isn't storing them properly).
94          */
95          // DEFAULT.verify("\u82b1\u5b50.co.jp", x509 );
96          // impl.verify("\u82b1\u5b50.co.jp", x509 );
97          exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
98  
99          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
100         x509 = (X509Certificate) cf.generateCertificate(in);
101         impl.verify("foo.com", x509);
102         exceptionPlease(impl, "a.foo.com", x509);
103 
104         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
105         x509 = (X509Certificate) cf.generateCertificate(in);
106         impl.verify("foo.com", x509);
107         exceptionPlease(impl, "a.foo.com", x509);
108 
109         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_THREE_CNS_FOO_BAR_HANAKO);
110         x509 = (X509Certificate) cf.generateCertificate(in);
111         exceptionPlease(impl, "foo.com", x509);
112         exceptionPlease(impl, "a.foo.com", x509);
113         exceptionPlease(impl, "bar.com", x509);
114         exceptionPlease(impl, "a.bar.com", x509);
115         impl.verify("\u82b1\u5b50.co.jp", x509);
116         exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509);
117 
118         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO);
119         x509 = (X509Certificate) cf.generateCertificate(in);
120         exceptionPlease(impl, "foo.com", x509);
121         impl.verify("www.foo.com", x509);
122         impl.verify("\u82b1\u5b50.foo.com", x509);
123         exceptionPlease(impl, "a.b.foo.com", x509);
124 
125         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP);
126         x509 = (X509Certificate) cf.generateCertificate(in);
127         // Silly test because no-one would ever be able to lookup an IP address
128         // using "*.co.jp".
129         impl.verify("*.co.jp", x509);
130         impl.verify("foo.co.jp", x509);
131         impl.verify("\u82b1\u5b50.co.jp", x509);
132 
133         exceptionPlease(implWithPublicSuffixCheck, "foo.co.jp", x509);
134         exceptionPlease(implWithPublicSuffixCheck, "\u82b1\u5b50.co.jp", x509);
135 
136         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO_BAR_HANAKO);
137         x509 = (X509Certificate) cf.generateCertificate(in);
138         // try the foo.com variations
139         exceptionPlease(impl, "foo.com", x509);
140         exceptionPlease(impl, "www.foo.com", x509);
141         exceptionPlease(impl, "\u82b1\u5b50.foo.com", x509);
142         exceptionPlease(impl, "a.b.foo.com", x509);
143         // try the bar.com variations
144         exceptionPlease(impl, "bar.com", x509);
145         impl.verify("www.bar.com", x509);
146         impl.verify("\u82b1\u5b50.bar.com", x509);
147         exceptionPlease(impl, "a.b.bar.com", x509);
148 
149         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA);
150         x509 = (X509Certificate) cf.generateCertificate(in);
151         impl.verify("repository.infonotary.com", x509);
152 
153         in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM);
154         x509 = (X509Certificate) cf.generateCertificate(in);
155         impl.verify("*.google.com", x509);
156 
157         in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM);
158         x509 = (X509Certificate) cf.generateCertificate(in);
159         impl.verify("*.Google.com", x509);
160 
161         in = new ByteArrayInputStream(CertificatesToPlayWith.IP_1_1_1_1);
162         x509 = (X509Certificate) cf.generateCertificate(in);
163         impl.verify("1.1.1.1", x509);
164 
165         exceptionPlease(impl, "1.1.1.2", x509);
166         exceptionPlease(impl, "dummy-value.com", x509);
167 
168         in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME);
169         x509 = (X509Certificate) cf.generateCertificate(in);
170         impl.verify("www.company.com", x509);
171     }
172 
173     @Test
174     public void testSubjectAlt() throws Exception {
175         final CertificateFactory cf = CertificateFactory.getInstance("X.509");
176         final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_SUBJECT_ALT);
177         final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
178 
179         Assert.assertEquals("CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CH",
180                 x509.getSubjectDN().getName());
181 
182         impl.verify("localhost.localdomain", x509);
183         impl.verify("127.0.0.1", x509);
184 
185         try {
186             impl.verify("localhost", x509);
187             Assert.fail("SSLException should have been thrown");
188         } catch (final SSLException ex) {
189             // expected
190         }
191         try {
192             impl.verify("local.host", x509);
193             Assert.fail("SSLException should have been thrown");
194         } catch (final SSLException ex) {
195             // expected
196         }
197         try {
198             impl.verify("127.0.0.2", x509);
199             Assert.fail("SSLException should have been thrown");
200         } catch (final SSLException ex) {
201             // expected
202         }
203     }
204 
205     public void exceptionPlease(final DefaultHostnameVerifier hv, final String host,
206                                 final X509Certificate x509) {
207         try {
208             hv.verify(host, x509);
209             Assert.fail("HostnameVerifier shouldn't allow [" + host + "]");
210         }
211         catch(final SSLException e) {
212             // whew!  we're okay!
213         }
214     }
215 
216     @Test
217     public void testDomainRootMatching() {
218 
219         Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", null));
220         Assert.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "a.b.c"));
221         Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("aa.b.c", "a.b.c"));
222         Assert.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "aa.b.c"));
223         Assert.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.a.b.c", "a.b.c"));
224     }
225 
226     @Test
227     public void testIdentityMatching() {
228 
229         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c"));
230         Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.c"));
231 
232         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.b.c", "*.b.c"));
233         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.b.c", "*.b.c")); // subdomain not OK
234 
235         Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher));
236         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "*.gov.uk", publicSuffixMatcher));  // Bad 2TLD
237 
238         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
239         Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher));
240 
241         Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));
242         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher));  // BBad 2TLD/no subdomain allowed
243 
244         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher));
245         Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher));
246 
247         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher));
248         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); // no subdomain allowed
249 
250         Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher));
251         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD
252 
253         Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD
254         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD/no subdomain allowed
255 
256         Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.*"));
257         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.*"));
258 
259         Assert.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.*.c"));
260         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.*.c"));
261     }
262 
263     @Test
264     public void testHTTPCLIENT_1097() {
265         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "a*.b.c"));
266         Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "a*.b.c"));
267 
268         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("a.a.b.c", "a*.b.c"));
269         Assert.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.a.b.c", "a*.b.c"));
270     }
271 
272     @Test
273     public void testHTTPCLIENT_1255() {
274         Assert.assertTrue(DefaultHostnameVerifier.matchIdentity("mail.a.b.c.com", "m*.a.b.c.com"));
275         Assert.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com"));
276     }
277 
278     @Test // Check compressed IPv6 hostname matching
279     public void testHTTPCLIENT_1316() throws Exception{
280         final String host1 = "2001:0db8:aaaa:bbbb:cccc:0:0:0001";
281         DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
282         DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
283         try {
284             DefaultHostnameVerifier.matchIPv6Address(host1, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10")));
285             Assert.fail("SSLException expected");
286         } catch (final SSLException expected) {
287         }
288         final String host2 = "2001:0db8:aaaa:bbbb:cccc::1";
289         DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001")));
290         DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1")));
291         try {
292             DefaultHostnameVerifier.matchIPv6Address(host2, Arrays.asList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10")));
293             Assert.fail("SSLException expected");
294         } catch (final SSLException expected) {
295         }
296     }
297 
298     @Test
299     public void testExtractCN() throws Exception {
300         Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah"));
301         Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, cn=yada, cn=booh"));
302         Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = pampa ,  cn  =    blah    , ou = blah , o = blah"));
303         Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=\"blah\", ou=blah, o=blah"));
304         Assert.assertEquals("blah  blah", DefaultHostnameVerifier.extractCN("cn=\"blah  blah\", ou=blah, o=blah"));
305         Assert.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=\"blah, blah\", ou=blah, o=blah"));
306         Assert.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=blah\\, blah, ou=blah, o=blah"));
307         Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = cn=uuh, cn=blah, ou=blah, o=blah"));
308         try {
309             DefaultHostnameVerifier.extractCN("blah,blah");
310             Assert.fail("SSLException expected");
311         } catch (final SSLException expected) {
312         }
313         try {
314             DefaultHostnameVerifier.extractCN("cn,o=blah");
315             Assert.fail("SSLException expected");
316         } catch (final SSLException expected) {
317         }
318     }
319 
320 }