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