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.junit.Assert;
39  import org.junit.Test;
40  
41  /**
42   * Unit tests for deprecated {@link X509HostnameVerifier} implementations.
43   *
44   * @deprecated Tests deprecated code.
45   */
46  @Deprecated
47  public class TestHostnameVerifier {
48  
49      @Test
50      public void testVerify() throws Exception {
51          final X509HostnameVerifier DEFAULT = new BrowserCompatHostnameVerifier();
52          final X509HostnameVerifier STRICT = new StrictHostnameVerifier();
53          final X509HostnameVerifier ALLOW_ALL = new AllowAllHostnameVerifier();
54          final CertificateFactory cf = CertificateFactory.getInstance("X.509");
55          InputStream in;
56          X509Certificate x509;
57          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO);
58          x509 = (X509Certificate) cf.generateCertificate(in);
59  
60          DEFAULT.verify("foo.com", x509);
61          STRICT.verify("foo.com", x509);
62          exceptionPlease(DEFAULT, "a.foo.com", x509);
63          exceptionPlease(STRICT, "a.foo.com", x509);
64          exceptionPlease(DEFAULT, "bar.com", x509);
65          exceptionPlease(STRICT, "bar.com", x509);
66          ALLOW_ALL.verify("foo.com", x509);
67          ALLOW_ALL.verify("a.foo.com", x509);
68          ALLOW_ALL.verify("bar.com", x509);
69  
70          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_HANAKO);
71          x509 = (X509Certificate) cf.generateCertificate(in);
72          DEFAULT.verify("\u82b1\u5b50.co.jp", x509);
73          STRICT.verify("\u82b1\u5b50.co.jp", x509);
74          exceptionPlease(DEFAULT, "a.\u82b1\u5b50.co.jp", x509);
75          exceptionPlease(STRICT, "a.\u82b1\u5b50.co.jp", x509);
76  
77          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR);
78          x509 = (X509Certificate) cf.generateCertificate(in);
79          exceptionPlease(DEFAULT, "foo.com", x509);
80          exceptionPlease(STRICT, "foo.com", x509);
81          exceptionPlease(DEFAULT, "a.foo.com", x509);
82          exceptionPlease(STRICT, "a.foo.com", x509);
83          DEFAULT.verify("bar.com", x509);
84          STRICT.verify("bar.com", x509);
85          exceptionPlease(DEFAULT, "a.bar.com", x509);
86          exceptionPlease(STRICT, "a.bar.com", x509);
87  
88          in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR_HANAKO);
89          x509 = (X509Certificate) cf.generateCertificate(in);
90          exceptionPlease(DEFAULT, "foo.com", x509);
91          exceptionPlease(STRICT, "foo.com", x509);
92          exceptionPlease(DEFAULT, "a.foo.com", x509);
93          exceptionPlease(STRICT, "a.foo.com", x509);
94          DEFAULT.verify("bar.com", x509);
95          STRICT.verify("bar.com", x509);
96          exceptionPlease(DEFAULT, "a.bar.com", x509);
97          exceptionPlease(STRICT, "a.bar.com", x509);
98  
99          /*
100            Java isn't extracting international subjectAlts properly.  (Or
101            OpenSSL isn't storing them properly).
102         */
103         // DEFAULT.verify("\u82b1\u5b50.co.jp", x509 );
104         // STRICT.verify("\u82b1\u5b50.co.jp", x509 );
105         exceptionPlease(DEFAULT, "a.\u82b1\u5b50.co.jp", x509);
106         exceptionPlease(STRICT, "a.\u82b1\u5b50.co.jp", x509);
107 
108         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
109         x509 = (X509Certificate) cf.generateCertificate(in);
110         DEFAULT.verify("foo.com", x509);
111         STRICT.verify("foo.com", x509);
112         exceptionPlease(DEFAULT, "a.foo.com", x509);
113         exceptionPlease(STRICT, "a.foo.com", x509);
114 
115         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO);
116         x509 = (X509Certificate) cf.generateCertificate(in);
117         DEFAULT.verify("foo.com", x509);
118         STRICT.verify("foo.com", x509);
119         exceptionPlease(DEFAULT, "a.foo.com", x509);
120         exceptionPlease(STRICT, "a.foo.com", x509);
121 
122         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_THREE_CNS_FOO_BAR_HANAKO);
123         x509 = (X509Certificate) cf.generateCertificate(in);
124         exceptionPlease(DEFAULT, "foo.com", x509);
125         exceptionPlease(STRICT, "foo.com", x509);
126         exceptionPlease(DEFAULT, "a.foo.com", x509);
127         exceptionPlease(STRICT, "a.foo.com", x509);
128         exceptionPlease(DEFAULT, "bar.com", x509);
129         exceptionPlease(STRICT, "bar.com", x509);
130         exceptionPlease(DEFAULT, "a.bar.com", x509);
131         exceptionPlease(STRICT, "a.bar.com", x509);
132         DEFAULT.verify("\u82b1\u5b50.co.jp", x509);
133         STRICT.verify("\u82b1\u5b50.co.jp", x509);
134         exceptionPlease(DEFAULT, "a.\u82b1\u5b50.co.jp", x509);
135         exceptionPlease(STRICT, "a.\u82b1\u5b50.co.jp", x509);
136 
137         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO);
138         x509 = (X509Certificate) cf.generateCertificate(in);
139         exceptionPlease(DEFAULT, "foo.com", x509);
140         exceptionPlease(STRICT, "foo.com", x509);
141         DEFAULT.verify("www.foo.com", x509);
142         STRICT.verify("www.foo.com", x509);
143         DEFAULT.verify("\u82b1\u5b50.foo.com", x509);
144         STRICT.verify("\u82b1\u5b50.foo.com", x509);
145         DEFAULT.verify("a.b.foo.com", x509);
146         exceptionPlease(STRICT, "a.b.foo.com", x509);
147 
148         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP);
149         x509 = (X509Certificate) cf.generateCertificate(in);
150         // Silly test because no-one would ever be able to lookup an IP address
151         // using "*.co.jp".
152         DEFAULT.verify("*.co.jp", x509);
153         STRICT.verify("*.co.jp", x509);
154         DEFAULT.verify("foo.co.jp", x509);
155         exceptionPlease(STRICT, "foo.co.jp", x509);
156         DEFAULT.verify("\u82b1\u5b50.co.jp", x509);
157         exceptionPlease(STRICT, "\u82b1\u5b50.co.jp", x509);
158 
159         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO_BAR_HANAKO);
160         x509 = (X509Certificate) cf.generateCertificate(in);
161         // try the foo.com variations
162         exceptionPlease(DEFAULT, "foo.com", x509);
163         exceptionPlease(STRICT, "foo.com", x509);
164         exceptionPlease(DEFAULT, "www.foo.com", x509);
165         exceptionPlease(STRICT, "www.foo.com", x509);
166         exceptionPlease(DEFAULT, "\u82b1\u5b50.foo.com", x509);
167         exceptionPlease(STRICT, "\u82b1\u5b50.foo.com", x509);
168         exceptionPlease(DEFAULT, "a.b.foo.com", x509);
169         exceptionPlease(STRICT, "a.b.foo.com", x509);
170         // try the bar.com variations
171         exceptionPlease(DEFAULT, "bar.com", x509);
172         exceptionPlease(STRICT, "bar.com", x509);
173         DEFAULT.verify("www.bar.com", x509);
174         STRICT.verify("www.bar.com", x509);
175         DEFAULT.verify("\u82b1\u5b50.bar.com", x509);
176         STRICT.verify("\u82b1\u5b50.bar.com", x509);
177         DEFAULT.verify("a.b.bar.com", x509);
178         exceptionPlease(STRICT, "a.b.bar.com", x509);
179 
180         in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA);
181         x509 = (X509Certificate) cf.generateCertificate(in);
182         ALLOW_ALL.verify("repository.infonotary.com", x509);
183         DEFAULT.verify("repository.infonotary.com", x509);
184         STRICT.verify("repository.infonotary.com", x509);
185     }
186 
187     @Test
188     public void testSubjectAlt() throws Exception {
189         final CertificateFactory cf = CertificateFactory.getInstance("X.509");
190         final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_SUBJECT_ALT);
191         final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
192 
193         final X509HostnameVerifier verifier = BrowserCompatHostnameVerifier.INSTANCE;
194 
195         Assert.assertEquals("CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CH",
196                 x509.getSubjectDN().getName());
197 
198         verifier.verify("localhost.localdomain", x509);
199         verifier.verify("127.0.0.1", x509);
200 
201         try {
202             verifier.verify("localhost", x509);
203             Assert.fail("SSLException should have been thrown");
204         } catch (final SSLException ex) {
205             // expected
206         }
207         try {
208             verifier.verify("local.host", x509);
209             Assert.fail("SSLException should have been thrown");
210         } catch (final SSLException ex) {
211             // expected
212         }
213         try {
214             verifier.verify("127.0.0.2", x509);
215             Assert.fail("SSLException should have been thrown");
216         } catch (final SSLException ex) {
217             // expected
218         }
219 
220     }
221 
222     public void exceptionPlease(final X509HostnameVerifier hv, final String host,
223                                  final X509Certificate x509) {
224         try {
225             hv.verify(host, x509);
226             Assert.fail("HostnameVerifier shouldn't allow [" + host + "]");
227         }
228         catch(final SSLException e) {
229             // whew!  we're okay!
230         }
231     }
232 
233     // Test helper method
234     private void checkMatching(final X509HostnameVerifier hv, final String host,
235             final String[] cns, final String[] alts, final boolean shouldFail) {
236         try {
237             hv.verify(host, cns, alts);
238             if (shouldFail) {
239                 Assert.fail("HostnameVerifier should not allow [" + host + "] to match "
240                         +Arrays.toString(cns)
241                         +" or "
242                         +Arrays.toString(alts));
243             }
244         }
245         catch(final SSLException e) {
246             if (!shouldFail) {
247                 Assert.fail("HostnameVerifier should have allowed [" + host + "] to match "
248                         +Arrays.toString(cns)
249                         +" or "
250                         +Arrays.toString(alts));
251             }
252         }
253     }
254 
255     @Test
256     // Check standard wildcard matching
257     public void testMatching() {
258         String cns[] = {};
259         String alt[] = {};
260         final X509HostnameVerifier bhv = new BrowserCompatHostnameVerifier();
261         final X509HostnameVerifier shv = new StrictHostnameVerifier();
262         checkMatching(bhv, "a.b.c", cns, alt, true); // empty
263         checkMatching(shv, "a.b.c", cns, alt, true); // empty
264 
265         cns = new String []{"*.b.c"};
266         checkMatching(bhv, "a.b.c", cns, alt, false); // OK
267         checkMatching(shv, "a.b.c", cns, alt, false); // OK
268 
269         checkMatching(bhv, "s.a.b.c", cns, alt, false); // OK
270         checkMatching(shv, "s.a.b.c", cns, alt, true); // subdomain not OK
271 
272         cns = new String []{};
273         alt = new String []{"dummy", "*.b.c"}; // check matches against all alts
274         checkMatching(bhv, "a.b.c", cns, alt, false); // OK
275         checkMatching(shv, "a.b.c", cns, alt, false); // OK
276 
277         checkMatching(bhv, "s.a.b.c", cns, alt, false); // OK
278         checkMatching(shv, "s.a.b.c", cns, alt, true); // subdomain not OK
279 
280         alt = new String []{"*.gov.uk"};
281         checkMatching(bhv, "a.gov.uk", cns, alt, false); // OK
282         checkMatching(shv, "a.gov.uk", cns, alt, true); // Bad 2TLD
283 
284         checkMatching(bhv, "s.a.gov.uk", cns, alt, false); // OK
285         checkMatching(shv, "s.a.gov.uk", cns, alt, true); // Bad 2TLD/no subdomain allowed
286         alt = new String []{"*.gov.com"};
287         checkMatching(bhv, "a.gov.com", cns, alt, false); // OK, gov not 2TLD here
288         checkMatching(shv, "a.gov.com", cns, alt, false); // OK, gov not 2TLD here
289 
290         checkMatching(bhv, "s.a.gov.com", cns, alt, false); // OK, gov not 2TLD here
291         checkMatching(shv, "s.a.gov.com", cns, alt, true); // no subdomain allowed
292 
293         alt = new String []{"a*.gov.uk"}; // 2TLD check applies to wildcards
294         checkMatching(bhv, "a.gov.uk", cns, alt, false); // OK
295         checkMatching(shv, "a.gov.uk", cns, alt, true); // Bad 2TLD
296 
297         checkMatching(bhv, "s.a.gov.uk", cns, alt, true); // Bad 2TLD
298         checkMatching(shv, "s.a.gov.uk", cns, alt, true); // Bad 2TLD/no subdomain allowed
299 
300     }
301 
302     @Test
303     // Check compressed IPv6 hostname matching
304     public void testHTTPCLIENT_1316() throws Exception{
305         final String cns[] = {"2001:0db8:aaaa:bbbb:cccc:0:0:0001"};
306         final String alt[] = {};
307         final X509HostnameVerifier bhv = new BrowserCompatHostnameVerifier();
308         final X509HostnameVerifier shv = new StrictHostnameVerifier();
309         checkMatching(bhv, "2001:0db8:aaaa:bbbb:cccc:0:0:0001", cns, alt, false);
310         checkMatching(shv, "2001:0db8:aaaa:bbbb:cccc:0:0:0001", cns, alt, false);
311         checkMatching(bhv, "2001:0db8:aaaa:bbbb:cccc::1", cns, alt, false);
312         checkMatching(shv, "2001:0db8:aaaa:bbbb:cccc::1", cns, alt, false);
313         checkMatching(bhv, "2001:0db8:aaaa:bbbb:cccc::10", cns, alt, true);
314         checkMatching(shv, "2001:0db8:aaaa:bbbb:cccc::10", cns, alt, true);
315         // TODO need some more samples
316     }
317 
318 
319     @Test
320     public void testHTTPCLIENT_1097() {
321         final String cns[];
322         final String alt[] = {};
323         final X509HostnameVerifier bhv = new BrowserCompatHostnameVerifier();
324         final X509HostnameVerifier shv = new StrictHostnameVerifier();
325 
326         cns = new String []{"a*.b.c"}; // component part
327         checkMatching(bhv, "a.b.c", cns, alt, false); // OK
328         checkMatching(shv, "a.b.c", cns, alt, false); // OK
329 
330         checkMatching(bhv, "a.a.b.c", cns, alt, false); // OK
331         checkMatching(shv, "a.a.b.c", cns, alt, true); // subdomain not OK
332     }
333 
334     @Test
335     public void testHTTPCLIENT_1255() {
336         final X509HostnameVerifier bhv = new BrowserCompatHostnameVerifier();
337         final X509HostnameVerifier shv = new StrictHostnameVerifier();
338 
339         final String cns[] = new String []{"m*.a.b.c.com"}; // component part
340         final String alt[] = {};
341         checkMatching(bhv, "mail.a.b.c.com", cns, alt, false); // OK
342         checkMatching(shv, "mail.a.b.c.com", cns, alt, false); // OK
343     }
344 
345 }