1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with this
4 * work for additional information regarding copyright ownership. The ASF
5 * licenses this file to You under the Apache License, Version 2.0 (the
6 * "License"); you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17
18package org.apache.harmony.tests.javax.net.ssl;
19
20import java.io.ByteArrayInputStream;
21import java.io.InputStream;
22import java.security.cert.CertificateFactory;
23import java.security.cert.X509Certificate;
24import javax.net.ssl.HostnameVerifier;
25import javax.net.ssl.HttpsURLConnection;
26import javax.security.auth.x500.X500Principal;
27import junit.framework.TestCase;
28import org.apache.harmony.xnet.tests.support.mySSLSession;
29
30public class HostnameVerifierTest extends TestCase implements
31        CertificatesToPlayWith {
32
33    /**
34     * javax.net.ssl.HostnameVerifier#verify(String hostname, SSLSession
35     *        session)
36     */
37    public final void test_verify() throws Exception {
38        mySSLSession session = new mySSLSession("localhost", 1080, null);
39        HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
40        assertFalse(hv.verify("localhost", session));
41    }
42
43    // copied and modified from apache http client test suite.
44    public void testVerify() throws Exception {
45        CertificateFactory cf = CertificateFactory.getInstance("X.509");
46        InputStream in;
47        X509Certificate x509;
48        in = new ByteArrayInputStream(X509_FOO);
49        x509 = (X509Certificate) cf.generateCertificate(in);
50        mySSLSession session = new mySSLSession(new X509Certificate[] {x509});
51
52        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
53        assertTrue(verifier.verify("foo.com", session));
54        assertFalse(verifier.verify("a.foo.com", session));
55        assertFalse(verifier.verify("bar.com", session));
56
57        in = new ByteArrayInputStream(X509_HANAKO);
58        x509 = (X509Certificate) cf.generateCertificate(in);
59        session = new mySSLSession(new X509Certificate[] {x509});
60        assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session));
61        assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session));
62
63        in = new ByteArrayInputStream(X509_FOO_BAR);
64        x509 = (X509Certificate) cf.generateCertificate(in);
65        session = new mySSLSession(new X509Certificate[] {x509});
66        assertFalse(verifier.verify("foo.com", session));
67        assertFalse(verifier.verify("a.foo.com", session));
68        assertTrue(verifier.verify("bar.com", session));
69        assertFalse(verifier.verify("a.bar.com", session));
70
71        in = new ByteArrayInputStream(X509_FOO_BAR_HANAKO);
72        x509 = (X509Certificate) cf.generateCertificate(in);
73        session = new mySSLSession(new X509Certificate[] {x509});
74        assertFalse(verifier.verify("foo.com", session));
75        assertFalse(verifier.verify("a.foo.com", session));
76        assertTrue(verifier.verify("bar.com", session));
77        assertFalse(verifier.verify("a.bar.com", session));
78        // The certificate has this name in the altnames section, but Conscrypt drops
79        // any altnames that are improperly encoded according to RFC 5280, which requires
80        // non-ASCII characters to be encoded in ASCII via Punycode.
81        assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session));
82        assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session));
83
84        in = new ByteArrayInputStream(X509_NO_CNS_FOO);
85        x509 = (X509Certificate) cf.generateCertificate(in);
86        session = new mySSLSession(new X509Certificate[] {x509});
87        assertTrue(verifier.verify("foo.com", session));
88        assertFalse(verifier.verify("a.foo.com", session));
89
90        in = new ByteArrayInputStream(X509_THREE_CNS_FOO_BAR_HANAKO);
91        x509 = (X509Certificate) cf.generateCertificate(in);
92        session = new mySSLSession(new X509Certificate[] {x509});
93        assertFalse(verifier.verify("foo.com", session));
94        assertFalse(verifier.verify("a.foo.com", session));
95        assertFalse(verifier.verify("bar.com", session));
96        assertFalse(verifier.verify("a.bar.com", session));
97        assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session));
98        assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session));
99
100        in = new ByteArrayInputStream(X509_WILD_FOO);
101        x509 = (X509Certificate) cf.generateCertificate(in);
102        session = new mySSLSession(new X509Certificate[] {x509});
103        assertFalse(verifier.verify("foo.com", session));
104        assertTrue(verifier.verify("www.foo.com", session));
105        assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session));
106        assertFalse(verifier.verify("a.b.foo.com", session));
107
108        in = new ByteArrayInputStream(X509_WILD_CO_JP);
109        x509 = (X509Certificate) cf.generateCertificate(in);
110        session = new mySSLSession(new X509Certificate[] {x509});
111        assertTrue(verifier.verify("foo.co.jp", session));
112        assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session));
113
114        in = new ByteArrayInputStream(X509_WILD_FOO_BAR_HANAKO);
115        x509 = (X509Certificate) cf.generateCertificate(in);
116        session = new mySSLSession(new X509Certificate[] {x509});
117        // try the foo.com variations
118        assertFalse(verifier.verify("foo.com", session));
119        assertFalse(verifier.verify("www.foo.com", session));
120        assertFalse(verifier.verify("\u82b1\u5b50.foo.com", session));
121        assertFalse(verifier.verify("a.b.foo.com", session));
122        assertFalse(verifier.verify("bar.com", session));
123        assertTrue(verifier.verify("www.bar.com", session));
124        assertTrue(verifier.verify("\u82b1\u5b50.bar.com", session));
125        assertFalse(verifier.verify("a.b.bar.com", session));
126        // The certificate has this name in the altnames section, but Conscrypt drops
127        // any altnames that are improperly encoded according to RFC 5280, which requires
128        // non-ASCII characters to be encoded in ASCII via Punycode.
129        assertFalse(verifier.verify("\u82b1\u5b50.co.jp", session));
130        assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session));
131    }
132
133    public void testSubjectAlt() throws Exception {
134        CertificateFactory cf = CertificateFactory.getInstance("X.509");
135        InputStream in = new ByteArrayInputStream(X509_MULTIPLE_SUBJECT_ALT);
136        X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
137        mySSLSession session = new mySSLSession(new X509Certificate[] {x509});
138
139        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
140        assertEquals(new X500Principal("CN=localhost"), x509.getSubjectX500Principal());
141
142        assertTrue(verifier.verify("localhost", session));
143        assertTrue(verifier.verify("localhost.localdomain", session));
144        assertFalse(verifier.verify("local.host", session));
145    }
146
147    public void testVerifyIpAddress() throws Exception {
148        CertificateFactory cf = CertificateFactory.getInstance("X.509");
149        InputStream in = new ByteArrayInputStream(X509_MULTIPLE_SUBJECT_ALT);
150        X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
151        mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
152        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
153
154        assertTrue(verifier.verify("127.0.0.1", session));
155        assertFalse(verifier.verify("127.0.0.2", session));
156    }
157
158    public void testWildcardsCannotMatchIpAddresses() throws Exception {
159        // openssl req -x509 -nodes -days 36500 -subj '/CN=*.0.0.1' -newkey rsa:512 -out cert.pem
160        String cert = "-----BEGIN CERTIFICATE-----\n"
161                + "MIIBkjCCATygAwIBAgIJAMdemqOwd/BEMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV\n"
162                + "BAMUByouMC4wLjEwIBcNMTAxMjIwMTY0NDI1WhgPMjExMDExMjYxNjQ0MjVaMBIx\n"
163                + "EDAOBgNVBAMUByouMC4wLjEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqY8c9Qrt\n"
164                + "YPWCvb7lclI+aDHM6fgbJcHsS9Zg8nUOh5dWrS7AgeA25wyaokFl4plBbbHQe2j+\n"
165                + "cCjsRiJIcQo9HwIDAQABo3MwcTAdBgNVHQ4EFgQUJ436TZPJvwCBKklZZqIvt1Yt\n"
166                + "JjEwQgYDVR0jBDswOYAUJ436TZPJvwCBKklZZqIvt1YtJjGhFqQUMBIxEDAOBgNV\n"
167                + "BAMUByouMC4wLjGCCQDHXpqjsHfwRDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n"
168                + "BQUAA0EAk9i88xdjWoewqvE+iMC9tD2obMchgFDaHH0ogxxiRaIKeEly3g0uGxIt\n"
169                + "fl2WRY8hb4x+zRrwsFaLEpdEvqcjOQ==\n"
170                + "-----END CERTIFICATE-----";
171        CertificateFactory cf = CertificateFactory.getInstance("X.509");
172        InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8"));
173        X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
174        mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
175        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
176
177        assertFalse(verifier.verify("127.0.0.1", session));
178    }
179
180    /**
181     * Earlier implementations of Android's hostname verifier required that
182     * wildcard names wouldn't match "*.com" or similar. This was a nonstandard
183     * check that we've since dropped. It is the CA's responsibility to not hand
184     * out certificates that match so broadly.
185     */
186    public void testWildcardsDoesNotNeedTwoDots() throws Exception {
187        // openssl req -x509 -nodes -days 36500 -subj '/CN=*.com' -newkey rsa:512 -out cert.pem
188        String cert = "-----BEGIN CERTIFICATE-----\n"
189                + "MIIBjDCCATagAwIBAgIJAOVulXCSu6HuMA0GCSqGSIb3DQEBBQUAMBAxDjAMBgNV\n"
190                + "BAMUBSouY29tMCAXDTEwMTIyMDE2NDkzOFoYDzIxMTAxMTI2MTY0OTM4WjAQMQ4w\n"
191                + "DAYDVQQDFAUqLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDJd8xqni+h7Iaz\n"
192                + "ypItivs9kPuiJUqVz+SuJ1C05SFc3PmlRCvwSIfhyD67fHcbMdl+A/LrIjhhKZJe\n"
193                + "1joO0+pFAgMBAAGjcTBvMB0GA1UdDgQWBBS4Iuzf5w8JdCp+EtBfdFNudf6+YzBA\n"
194                + "BgNVHSMEOTA3gBS4Iuzf5w8JdCp+EtBfdFNudf6+Y6EUpBIwEDEOMAwGA1UEAxQF\n"
195                + "Ki5jb22CCQDlbpVwkruh7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA0EA\n"
196                + "U6LFxmZr31lFyis2/T68PpjAppc0DpNQuA2m/Y7oTHBDi55Fw6HVHCw3lucuWZ5d\n"
197                + "qUYo4ES548JdpQtcLrW2sA==\n"
198                + "-----END CERTIFICATE-----";
199        CertificateFactory cf = CertificateFactory.getInstance("X.509");
200        InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8"));
201        X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
202        mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
203        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
204
205        assertTrue(verifier.verify("google.com", session));
206    }
207
208    public void testSubjectAltName() throws Exception {
209        /*
210         * $ cat ./cert.cnf
211         * [req]
212         * distinguished_name=distinguished_name
213         * req_extensions=req_extensions
214         * x509_extensions=x509_extensions
215         * [distinguished_name]
216         * [req_extensions]
217         * [x509_extensions]
218         * subjectAltName=DNS:bar.com,DNS:baz.com
219         *
220         * $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \
221         *     -newkey rsa:512 -out cert.pem
222         */
223        String cert = "-----BEGIN CERTIFICATE-----\n"
224                + "MIIBPTCB6KADAgECAgkA7zoHaaqNGHQwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n"
225                + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODM5MzZaGA8yMTEwMTEyNjE4MzkzNlowEjEQ\n"
226                + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+gmoSxF+8\n"
227                + "hbV+rgRQqHIJd50216OWQJbU3BvdlPbca779NYO4+UZWTFdBM8BdQqs3H4B5Agvp\n"
228                + "y7HeSff1F7XRAgMBAAGjHzAdMBsGA1UdEQQUMBKCB2Jhci5jb22CB2Jhei5jb20w\n"
229                + "DQYJKoZIhvcNAQEFBQADQQBXpZZPOY2Dy1lGG81JTr8L4or9jpKacD7n51eS8iqI\n"
230                + "oTznPNuXHU5bFN0AAGX2ij47f/EahqTpo5RdS95P4sVm\n"
231                + "-----END CERTIFICATE-----";
232        CertificateFactory cf = CertificateFactory.getInstance("X.509");
233        InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8"));
234        X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
235        mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
236        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
237
238        assertFalse(verifier.verify("foo.com", session));
239        assertTrue(verifier.verify("bar.com", session));
240        assertTrue(verifier.verify("baz.com", session));
241        assertFalse(verifier.verify("a.foo.com", session));
242        assertFalse(verifier.verify("quux.com", session));
243    }
244
245    public void testSubjectAltNameWithWildcard() throws Exception {
246        /*
247         * $ cat ./cert.cnf
248         * [req]
249         * distinguished_name=distinguished_name
250         * req_extensions=req_extensions
251         * x509_extensions=x509_extensions
252         * [distinguished_name]
253         * [req_extensions]
254         * [x509_extensions]
255         * subjectAltName=DNS:bar.com,DNS:*.baz.com
256         *
257         * $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \
258         *     -newkey rsa:512 -out cert.pem
259         */
260        String cert = "-----BEGIN CERTIFICATE-----\n"
261                + "MIIBPzCB6qADAgECAgkAnv/7Jv5r7pMwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n"
262                + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODQ2MDFaGA8yMTEwMTEyNjE4NDYwMVowEjEQ\n"
263                + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDAz2YXnyog\n"
264                + "YdYLSFr/OEgSumtwqtZKJTB4wqTW/eKbBCEzxnyUMxWZIqUGu353PzwfOuWp2re3\n"
265                + "nvVV+QDYQlh9AgMBAAGjITAfMB0GA1UdEQQWMBSCB2Jhci5jb22CCSouYmF6LmNv\n"
266                + "bTANBgkqhkiG9w0BAQUFAANBAB8yrSl8zqy07i0SNYx2B/FnvQY734pxioaqFWfO\n"
267                + "Bqo1ZZl/9aPHEWIwBrxYNVB0SGu/kkbt/vxqOjzzrkXukmI=\n"
268                + "-----END CERTIFICATE-----";
269        CertificateFactory cf = CertificateFactory.getInstance("X.509");
270        InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8"));
271        X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
272        mySSLSession session = new mySSLSession(new X509Certificate[] { x509 });
273        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
274
275        assertFalse(verifier.verify("foo.com", session));
276        assertTrue(verifier.verify("bar.com", session));
277        assertTrue(verifier.verify("a.baz.com", session));
278        assertFalse(verifier.verify("baz.com", session));
279        assertFalse(verifier.verify("a.foo.com", session));
280        assertFalse(verifier.verify("a.bar.com", session));
281        assertFalse(verifier.verify("quux.com", session));
282    }
283}
284