1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.javax.net.ssl;
18
19import java.io.ByteArrayInputStream;
20import java.io.InputStream;
21import java.math.BigInteger;
22import java.nio.charset.StandardCharsets;
23import java.security.Principal;
24import java.security.PublicKey;
25import java.security.cert.Certificate;
26import java.security.cert.CertificateFactory;
27import java.security.cert.X509Certificate;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.Date;
31import java.util.LinkedList;
32import java.util.List;
33import java.util.Set;
34import javax.net.ssl.HostnameVerifier;
35import javax.net.ssl.HttpsURLConnection;
36import javax.net.ssl.SSLPeerUnverifiedException;
37import javax.net.ssl.SSLSession;
38import javax.net.ssl.SSLSessionContext;
39import javax.security.auth.x500.X500Principal;
40import junit.framework.TestCase;
41
42/**
43 * Tests for the platform-default {@link HostnameVerifier} as provided by
44 * {@link HttpsURLConnection#getDefaultHostnameVerifier()}.
45 */
46public final class DefaultHostnameVerifierTest extends TestCase {
47    private static final int ALT_UNKNOWN = 0;
48    private static final int ALT_DNS_NAME = 2;
49    private static final int ALT_IPA_NAME = 7;
50
51    private final HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
52
53    public void testVerify() {
54        assertTrue(verifyWithServerCertificate(
55                "imap.g.com", new StubX509Certificate("cn=imap.g.com")));
56        assertFalse(verifyWithServerCertificate(
57                "imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
58        assertFalse(verifyWithServerCertificate(
59                "imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
60    }
61
62    /**
63     * If a subjectAltName extension of type ALT_DNS_NAME is present, that MUST
64     * be used as the identity and the CN should be ignored.
65     */
66    public void testSubjectAltNameAndCn() {
67        assertFalse(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
68                .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
69        assertFalse(
70                verifyWithServerCertificate("imap.g.com", new StubX509Certificate("cn=imap.g.com")
71                        .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
72        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
73                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")));
74    }
75
76    public void testSubjectAltNameWithWildcard() {
77        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
78                .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")));
79    }
80
81    public void testSubjectAltNameWithIpAddress() {
82        assertTrue(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("")
83                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
84        assertFalse(verifyWithServerCertificate("1.2.3.5", new StubX509Certificate("")
85                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
86        assertTrue(verifyWithServerCertificate("192.168.100.1", new StubX509Certificate("")
87                .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
88                .addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")));
89    }
90
91    public void testUnknownSubjectAltName() {
92        // Has unknown subject alternative names
93        assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
94                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
95                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
96                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
97                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
98                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
99                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
100                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
101        assertTrue(verifyWithServerCertificate("2.33.44.55", new StubX509Certificate("")
102                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
103                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
104                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
105                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
106                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
107                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
108                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
109        assertFalse(verifyWithServerCertificate("g.com", new StubX509Certificate("")
110                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
111                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
112                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
113                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
114                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
115                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
116                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
117        assertFalse(verifyWithServerCertificate("2.33.44.1", new StubX509Certificate("")
118                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
119                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
120                .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
121                .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com")
122                .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
123                .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
124                .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
125    }
126
127    public void testWildcardsRejectedForIpAddress() {
128        assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")));
129        assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")
130                .addSubjectAlternativeName(ALT_IPA_NAME, "*.2.3.4")
131                .addSubjectAlternativeName(ALT_DNS_NAME, "*.2.3.4")));
132        assertFalse(verifyWithServerCertificate(
133                "2001:1234::1", new StubX509Certificate("cn=*:1234::1")));
134        assertFalse(verifyWithServerCertificate(
135                "2001:1234::1", new StubX509Certificate("cn=*:1234::1")
136                .addSubjectAlternativeName(ALT_IPA_NAME, "*:1234::1")
137                .addSubjectAlternativeName(ALT_DNS_NAME, "*:1234::1")));
138    }
139
140    public void testNullParameters() {
141        // Confirm that neither of the parameters used later in the test cause the verifier to blow
142        // up
143        String hostname = "www.example.com";
144        StubSSLSession session = new StubSSLSession();
145        session.peerCertificates =
146                new Certificate[] {new StubX509Certificate("cn=www.example.com")};
147        verifier.verify(hostname, session);
148
149        try {
150            verifier.verify(hostname, null);
151            fail();
152        } catch (NullPointerException expected) {
153        }
154
155        try {
156            verifier.verify(null, session);
157            fail();
158        } catch (NullPointerException expected) {
159        }
160    }
161
162    public void testInvalidDomainNames() {
163        assertFalse(verifyWithDomainNamePattern("", ""));
164        assertFalse(verifyWithDomainNamePattern(".test.example.com", ".test.example.com"));
165        assertFalse(verifyWithDomainNamePattern("ex*ample.com", "ex*ample.com"));
166        assertFalse(verifyWithDomainNamePattern("example.com..", "example.com."));
167        assertFalse(verifyWithDomainNamePattern("example.com.", "example.com.."));
168    }
169
170    public void testWildcardCharacterMustBeLeftMostLabelOnly() {
171        assertFalse(verifyWithDomainNamePattern("test.www.example.com", "test.*.example.com"));
172        assertFalse(verifyWithDomainNamePattern("www.example.com", "www.*.com"));
173        assertFalse(verifyWithDomainNamePattern("www.example.com", "www.example.*"));
174        assertFalse(verifyWithDomainNamePattern("www.example.com", "*www.example.com"));
175        assertFalse(verifyWithDomainNamePattern("www.example.com", "*w.example.com"));
176        assertFalse(verifyWithDomainNamePattern("www.example.com", "w*w.example.com"));
177        assertFalse(verifyWithDomainNamePattern("www.example.com", "w*.example.com"));
178        assertFalse(verifyWithDomainNamePattern("www.example.com", "www*.example.com"));
179    }
180
181    public void testWildcardCannotMatchEmptyLabel() {
182        assertFalse(verifyWithDomainNamePattern("example.com", "*.example.com"));
183        assertFalse(verifyWithDomainNamePattern(".example.com", "*.example.com"));
184    }
185
186    public void testWildcardCannotMatchChildDomain() {
187        assertFalse(verifyWithDomainNamePattern("sub.www.example.com", "*.example.com"));
188    }
189
190    public void testWildcardRejectedForSingleLabelPatterns() {
191        assertFalse(verifyWithDomainNamePattern("d", "*"));
192        assertFalse(verifyWithDomainNamePattern("d.", "*."));
193        assertFalse(verifyWithDomainNamePattern("d", "d*"));
194        assertFalse(verifyWithDomainNamePattern("d.", "d*."));
195        assertFalse(verifyWithDomainNamePattern("d", "*d"));
196        assertFalse(verifyWithDomainNamePattern("d.", "*d."));
197        assertFalse(verifyWithDomainNamePattern("ddd", "d*d"));
198        assertFalse(verifyWithDomainNamePattern("ddd.", "d*d."));
199    }
200
201    public void testNoPrefixMatch() {
202        assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "imap.google.com"));
203        assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "*.google.com"));
204    }
205
206    public void testVerifyHostName() {
207        assertTrue(verifyWithDomainNamePattern("a.b.c.d", "a.b.c.d"));
208        assertTrue(verifyWithDomainNamePattern("a.b.c.d", "*.b.c.d"));
209        assertFalse(verifyWithDomainNamePattern("a.b.c.d", "*.*.c.d"));
210        assertTrue(verifyWithDomainNamePattern("imap.google.com", "imap.google.com"));
211        assertFalse(verifyWithDomainNamePattern("imap2.google.com", "imap.google.com"));
212        assertTrue(verifyWithDomainNamePattern("imap.google.com", "*.google.com"));
213        assertTrue(verifyWithDomainNamePattern("imap2.google.com", "*.google.com"));
214        assertFalse(verifyWithDomainNamePattern("imap.google.com", "*.googl.com"));
215        assertFalse(verifyWithDomainNamePattern("imap2.google2.com", "*.google3.com"));
216        assertFalse(verifyWithDomainNamePattern("imap.google.com", "a*.google.com"));
217        assertFalse(verifyWithDomainNamePattern("imap.google.com", "ix*.google.com"));
218        assertTrue(verifyWithDomainNamePattern("imap.google.com", "iMap.Google.Com"));
219        assertTrue(verifyWithDomainNamePattern("weird", "weird"));
220        assertTrue(verifyWithDomainNamePattern("weird", "weird."));
221
222        // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
223        assertFalse(verifyWithDomainNamePattern("weird", "weird*"));
224        assertFalse(verifyWithDomainNamePattern("weird", "*weird"));
225        assertFalse(verifyWithDomainNamePattern("weird", "weird*."));
226        assertFalse(verifyWithDomainNamePattern("weird", "weird.*"));
227    }
228
229    public void testVerifyAbsoluteHostName() {
230        assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "a.b.c.d"));
231        assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "*.b.c.d"));
232        assertFalse(verifyWithDomainNamePattern("a.b.c.d.", "*.*.c.d"));
233        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "imap.google.com"));
234        assertFalse(verifyWithDomainNamePattern("imap2.google.com.", "imap.google.com"));
235        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "*.google.com"));
236        assertTrue(verifyWithDomainNamePattern("imap2.google.com.", "*.google.com"));
237        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "*.googl.com"));
238        assertFalse(verifyWithDomainNamePattern("imap2.google2.com.", "*.google3.com"));
239        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "a*.google.com"));
240        assertFalse(verifyWithDomainNamePattern("imap.google.com.", "ix*.google.com"));
241        assertTrue(verifyWithDomainNamePattern("imap.google.com.", "iMap.Google.Com"));
242        assertTrue(verifyWithDomainNamePattern("weird.", "weird"));
243        assertTrue(verifyWithDomainNamePattern("weird.", "weird."));
244
245        // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
246        assertFalse(verifyWithDomainNamePattern("weird.", "*weird"));
247        assertFalse(verifyWithDomainNamePattern("weird.", "weird*"));
248        assertFalse(verifyWithDomainNamePattern("weird.", "weird*."));
249        assertFalse(verifyWithDomainNamePattern("weird.", "weird.*"));
250    }
251
252    public void testSubjectOnlyCert() throws Exception {
253        // subject: C=JP, CN=www.example.com
254        // subject alt names: n/a
255        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
256                + "MIIC0TCCAbmgAwIBAgIJANCQbJPPw31SMA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV\n"
257                + "BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjA1ODE4\n"
258                + "WhgPMjA2NDEwMTUyMDU4MThaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu\n"
259                + "ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsdUJk\n"
260                + "4KxADA3vlDHxNbyC27Ozw4yiSVzPTHUct471YmdDRW3orO2P5a5hRnUGV70gjH9X\n"
261                + "MU4oeOdWYAgXB9pxfLyr6621k1+uNrmaZtzp0ECH9twcwxNJJFDZsN7o9vt7V6Ej\n"
262                + "NN9weeqDr/aeQXo07a12vyVfR6jWO8jHB0e4aemwZNoYjNvM69fivQTse2ZoRVfj\n"
263                + "eSHhjRTX6I8ry4a31Hwt+fT1QiWWNN6o7+WOtpJAhX3eg4smhSD1svi2kOT8tdUe\n"
264                + "NS4hWlmXmumU9G4tI8PBurcLNTm7PB2lUlbn/IV18WavqKE/Uy/1WgAx+a1EJNdp\n"
265                + "i07AG1PsqaONKkf1AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAJrNsuL7fZZNC8gL\n"
266                + "BdePJ7DYW2e7mXANU3bCBe2BZqmXKQxKwibZnEsqA+yMLqcSd8uxISlyHY2tw9wT\n"
267                + "4wB9KPIttfNLbwn/rk+MbOTHpvyF60d9WhJJVUkPBl8D4VuPSl+VnlA54kU9dtZN\n"
268                + "+ZYdxYbNtSsI/Flz9SCoOV79W9GhN+uYJhv6RwyIMIHeMpZpyX1xSUVx5dZlmerQ\n"
269                + "WAUvghDH3fFRt2ZdnA4OXoKkTAaM3Pv7PUMsnah8bux6MQi0AuLMWFWOI1H34koH\n"
270                + "rs2oQLwOLnuifH52ey9+tJguabo+brlYYigAuWWFEzJfBzikDkIwnE/L7wlrypIk\n"
271                + "taXDWI4=\n"
272                + "-----END CERTIFICATE-----");
273        assertTrue(verifyWithServerCertificate("www.example.com", cert));
274        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
275    }
276
277    public void testSubjectAltOnlyCert() throws Exception {
278        // subject: C=JP (no CN)
279        // subject alt names: DNS:www.example.com
280        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
281                + "MIICvTCCAaWgAwIBAgIJALbA0TZk2YmNMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV\n"
282                + "BAYTAkpQMCAXDTEwMDExMjIwNTg1NFoYDzIwNjQxMDE1MjA1ODU0WjANMQswCQYD\n"
283                + "VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEg6acVC9V4\n"
284                + "xNGoLNVLPbqBc8IvMvcsc88dF6MW3d9VagX3aeWU8c79tI/KOV/1AOakH7WYxw/w\n"
285                + "yD8aOX7+9BK1Hu0qKKKbSM+ycqaMthXd6xytrNDsIx5WiGUz8zTko0Gk3orIR7p7\n"
286                + "rPcNzB/zwtESkscqPv85aEn7S/yClNkzLfEzm3CtaYOc0tfhBMyzi/ipXzGMxUmx\n"
287                + "PvOLr3v/Oz5pZEQw7Kxlm4+tAtn7bJlHziQ1UW4WPIy+T3hySBEpODFiqZi7Ok3X\n"
288                + "Zjxdii62fgo5B2Ee7q5Amo0mUIwcQTDjJ2CLAqzYnSh3tpiPJGjEIjmRyCoMQ1bx\n"
289                + "7D+y7nSPIq8CAwEAAaMeMBwwGgYDVR0RBBMwEYIPd3d3LmV4YW1wbGUuY29tMA0G\n"
290                + "CSqGSIb3DQEBBQUAA4IBAQBsGEh+nHc0l9FJTzWqvG3qs7i6XoJZdtThCDx4HjKJ\n"
291                + "8GMrJtreNN4JvIxn7KC+alVbnILjzCRO+c3rsnpxKBi5cp2imjuw5Kf/x2Seimb9\n"
292                + "UvZbaJvBVOzy4Q1IGef9bLy3wZzy2/WfBFyvPTAkgkRaX7LN2jnYOYVhNoNFrwqe\n"
293                + "EWxkA6fzrpyseUEFeGFFjGxRSRCDcQ25Eq6d9rkC1x21zNtt4QwZBO0wHrTy155M\n"
294                + "JPRynf9244Pn0Sr/wsnmdsTRFIFYynrc51hQ7DkwbUxpcaewkZzilru/SwZ3+pPT\n"
295                + "9JSqm5hJ1pg5WDlPkW7c/1VA0/141N52Q8MIU+2ZpuOj\n"
296                + "-----END CERTIFICATE-----");
297        assertTrue(verifyWithServerCertificate("www.example.com", cert));
298        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
299    }
300
301    public void testSubjectWithAltNamesCert() throws Exception {
302        // subject: C=JP, CN=www.example.com
303        // subject alt names: DNS:www2.example.com, DNS:www3.example.com
304        // * Subject should be ignored, because it has subject alt names.
305        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
306                + "MIIDBDCCAeygAwIBAgIJALv14qjcuhw9MA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV\n"
307                + "BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjA1OTM4\n"
308                + "WhgPMjA2NDEwMTUyMDU5MzhaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu\n"
309                + "ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiTVgU\n"
310                + "kBO9KNYZZLmiPR0eBrk8u61CLnm35BGKW8EFpDaINLbbIFIQvqOMekURON/N+xFY\n"
311                + "D8roo7aFZVuHWAUqFcOJ4e6NmviK5qocLihtzAexsw4f4AzZxM3A8kcLlWLyAt7e\n"
312                + "EVLxhcMHogY7GaF6q+33Z8p+zp6x3tj07mwyPrriCLse2PeRsRunZl/fp/VvRlr6\n"
313                + "YbC7CbRrhnIv5nqohs8BsbBiiFpxQftsMQmiXhY2LUzqY2RXUIOw24fHjoQkHTL2\n"
314                + "4z5nUM3b6ueQe+CBnobUS6fzK/36Nct4dRpev9i/ORdRLuIDKJ+QR16G1V/BJYBR\n"
315                + "dAK+3iXvg6z8vP1XAgMBAAGjMTAvMC0GA1UdEQQmMCSCEHd3dzIuZXhhbXBsZS5j\n"
316                + "b22CEHd3dzMuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAJQNf38uXm3h\n"
317                + "0vsF+Yd6/HqM48Su7tWnTDAfTXnQZZkzjzITq3JXzquMXICktAVN2cLnT9zPfRAE\n"
318                + "8V8A3BNO5zXiR5W3o/mJP5HQ3/WxpzBGM2N+YmDCJyBoQrIVaAZaXAZUaBBvn5A+\n"
319                + "kEVfGWquwIFuvA67xegbJOCRLD4eUzRdNsn5+NFiakWO1tkFqEzqyQ0PNPviRjgu\n"
320                + "z9NxdPvd1JQOhydkucsPKJzlEBbGyL5QL/Jkot3Qy+FOeuNzgQUfAGtQgzRrsZDK\n"
321                + "hrTVypLSoRXuTB2aWilu4p6aNh84xTdyqo2avtNr2MiQMZIcdamBq8LdBIAShFXI\n"
322                + "h5G2eVGXH/Y=\n"
323                + "-----END CERTIFICATE-----");
324        assertFalse(verifyWithServerCertificate("www.example.com", cert));
325        assertTrue(verifyWithServerCertificate("www2.example.com", cert));
326        assertTrue(verifyWithServerCertificate("www3.example.com", cert));
327        assertFalse(verifyWithServerCertificate("www4.example.com", cert));
328    }
329
330    public void testSubjectWithWildAltNamesCert() throws Exception {
331        // subject: C=JP, CN=www.example.com
332        // subject alt names: DNS:*.example2.com
333        // * Subject should be ignored, because it has subject alt names.
334        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
335                + "MIIC8DCCAdigAwIBAgIJAL/oWJ64VAdXMA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV\n"
336                + "BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjEwMDAx\n"
337                + "WhgPMjA2NDEwMTUyMTAwMDFaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu\n"
338                + "ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbx1QB\n"
339                + "92iea7VybLYICA4MX4LWipYrRsgXUXQrcIQ3YLTQ9rH0VwScrHL4O4JDxgXCQnR+\n"
340                + "4VOzD42q1KXHJAqzqGUYCNPyvZEzkGCnQ4FBIUEmxZd5SNEefJVH3Z6GizYJomTh\n"
341                + "p78yDcoqymD9umxRC2cWFu8GscfFGMVyhsqLlOofu7UWOs22mkXPo43jDx+VOAoV\n"
342                + "n48YP3P57a2Eo0gcd4zVL00y62VegqBO/1LW38aTS7teiCBFc1TkNYa5I40yN9lP\n"
343                + "rB9ICHYQWyzf/7OxU9iauEK2w6DmSsQoLs9JzEhgeNZddkcc77ciSUCo2Hx0VpOJ\n"
344                + "BFyf2rbryJeAk+FDAgMBAAGjHTAbMBkGA1UdEQQSMBCCDiouZXhhbXBsZTIuY29t\n"
345                + "MA0GCSqGSIb3DQEBBQUAA4IBAQA2a14pRL+4laJ8sscQlucaDB/oSdb0cwhk4IkE\n"
346                + "kKl/ZKr6rKwPZ81sJRgzvI4imLbUAKt4AJHdpI9cIQUq1gw9bzil7LKwmFtFSPmC\n"
347                + "MYb1iadaYrvp7RE4yXrWCcSbU0hup9JQLHTrHLlqLtRuU48NHMvWYThBcS9Q/hQp\n"
348                + "nJ/JxYy3am99MHALWLAfuRxQXhE4C5utDmBwI2KD6A8SA30s+CnuegmkYScuSqBu\n"
349                + "Y3R0HZvKzNIU3pwAm69HCJoG+/9MZEIDJb0WJc5UygxDT45XE9zQMQe4dBOTaNXT\n"
350                + "+ntgaB62kE10HzrzpqXAgoAWxWK4RzFcUpBWw9qYq9xOCewJ\n"
351                + "-----END CERTIFICATE-----");
352        assertFalse(verifyWithServerCertificate("www.example.com", cert));
353        assertFalse(verifyWithServerCertificate("www2.example.com", cert));
354        assertTrue(verifyWithServerCertificate("www.example2.com", cert));
355        assertTrue(verifyWithServerCertificate("abc.example2.com", cert));
356        assertFalse(verifyWithServerCertificate("www.example3.com", cert));
357    }
358
359    public void testWildAltNameOnlyCert() throws Exception {
360        // subject: C=JP
361        // subject alt names: DNS:*.example.com
362        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
363                + "MIICuzCCAaOgAwIBAgIJAP82tgcvmAGxMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV\n"
364                + "BAYTAkpQMCAXDTEwMDExMjIxMDAyN1oYDzIwNjQxMDE1MjEwMDI3WjANMQswCQYD\n"
365                + "VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALs528EQbcB1\n"
366                + "x4BwxthQBZrgDJzoO7KPV3dhGYoeP8EnRjapZm+T/sj9P/O4HvfxjnB+fsjYSdmE\n"
367                + "WWUtnFrP7wtG9DUC748Ea2PMV8WFhOG58dqBNIko5XzkHB7SxkNZD5S/0KQYMGLr\n"
368                + "rchDsDlmsEf2Qb6qiqpNEU70aSkExZJcH+B9nWdeBpsVFu7wtezwSWEc2NUa2bhW\n"
369                + "gcXQ/aafwHZ4o2PyGwy0sgS/UifqO9tEllC2tPleSNJOmYsVudv5Bz4Q0GG38BSz\n"
370                + "Pc0IcOoln0ZWpXbGr03V2vlXWCwzaFAl3I1T3O7YVqDiaSWoP+d0tHZzmw8aJLXd\n"
371                + "B+KaUUGxRPsCAwEAAaMcMBowGAYDVR0RBBEwD4INKi5leGFtcGxlLmNvbTANBgkq\n"
372                + "hkiG9w0BAQUFAAOCAQEAJbVan4QgJ0cvpJnK9UWIVJNC+UbP87RC5go2fQiTnmGv\n"
373                + "prOrIuMqz1+vGcpIheLTLctJRHPoadXq0+UbQEIaU3pQbY6C4nNdfl+hcvmJeqrt\n"
374                + "kOCcvmIamO68iNvTSeszuHuu4O38PefrW2Xd0nn7bjFZrzBzHFhTudmnqNliP3ue\n"
375                + "KKQpqkUt5lCytnH8V/u/UCWdvVx5LnUa2XFGVLi3ongBIojW5fvF+yxn9ADqxdrI\n"
376                + "va++ow5r1VxQXFJc0ZPzsDo+6TlktoDHaRQJGMqQomqHWT4i7F5UZgf6BHGfEUPU\n"
377                + "qep+GsF3QRHSBtpObWkVDZNFvky3a1iZ2q25+hFIqQ==\n"
378                + "-----END CERTIFICATE-----");
379        assertTrue(verifyWithServerCertificate("www.example.com", cert));
380        assertTrue(verifyWithServerCertificate("www2.example.com", cert));
381        assertFalse(verifyWithServerCertificate("www.example2.com", cert));
382    }
383
384    public void testAltIpOnlyCert() throws Exception {
385        // subject: C=JP
386        // subject alt names: IP Address:192.168.10.1
387        X509Certificate cert = parseCertificate("-----BEGIN CERTIFICATE-----\n"
388                + "MIICsjCCAZqgAwIBAgIJALrC37YAXFIeMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV\n"
389                + "BAYTAkpQMCAXDTEwMDExMjIxMzk0NloYDzIwNjQxMDE1MjEzOTQ2WjANMQswCQYD\n"
390                + "VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr8s/4Abpby\n"
391                + "IYks5YCJE2nbWH7kj6XbwnRzsVP9RVC33bPoQ1M+2ZY24HqkigjQS/HEXR0s0bYh\n"
392                + "dewNUnTj1uGyGs6cYzsbu7x114vmVYqjxUo3hKjwfYiPeF6f3IE1vpLI7I2G32gq\n"
393                + "Zwm9c1/vXNHIdWQxCpFcuPA8P3YGfoApFX4pQPFplBUNAQqnjdmA68cbxxMC+1F3\n"
394                + "mX42D7iIEVwyVpah5HjyxjIZQlf3X7QBj0bCmkL+ibIHTALrkNNwNM6i4xzYLz/5\n"
395                + "14GkN9ncHY87eSOk6r53ptER6mQMhCe9qPRjSHnpWTTyj6IXTaYe+dDQw657B80w\n"
396                + "cSHL7Ed25zUCAwEAAaMTMBEwDwYDVR0RBAgwBocEwKgKATANBgkqhkiG9w0BAQUF\n"
397                + "AAOCAQEAgrwrtOWZT3fbi1AafpGaAiOBWSJqYqRhtQy0AfiZBxv1U0XaYqmZmpnq\n"
398                + "DVAqr0NkljowD28NBrxIFO5gBNum2ZOPDl2/5vjFn+IirUCJ9u9wS7zYkTCW2lQR\n"
399                + "xE7Ic3mfWv7wUbKDfjlWqP1IDHUxwkrBTAl+HnwOPiaKKk1ttwcrgS8AHlqASe03\n"
400                + "mlwnvJ+Stk54IneRaegL0L93sNAy63RZqnPCTxGz7eHcFwX8Jdr4sbxTxQqV6pIc\n"
401                + "WPjHQcWfpkFzAF5wyOq0kveVfx0g5xPhOVDd+U+q7WastbXICpCoHp9FxISmZVik\n"
402                + "sAyifp8agkYdzaSh55fFmKXlFnRsQw==\n"
403                + "-----END CERTIFICATE-----");
404        assertTrue(verifyWithServerCertificate("192.168.10.1", cert));
405        assertFalse(verifyWithServerCertificate("192.168.10.2", cert));
406    }
407
408    /**
409     * Verifies the provided hostname against the provided domain name pattern from server
410     * certificate.
411     */
412    private boolean verifyWithDomainNamePattern(String hostname, String pattern) {
413        StubSSLSession session = new StubSSLSession();
414
415        // Verify using a certificate where the pattern is in the CN
416        session.peerCertificates = new Certificate[] {
417                new StubX509Certificate("cn=\"" + pattern + "\"")
418        };
419        boolean resultWhenPatternInCn = verifier.verify(hostname, session);
420
421        // Verify using a certificate where the pattern is in a DNS SubjectAltName
422        session.peerCertificates = new Certificate[] {
423                new StubX509Certificate("ou=test")
424                        .addSubjectAlternativeName(ALT_DNS_NAME, pattern)
425        };
426        boolean resultWhenPatternInSubjectAltName = verifier.verify(hostname, session);
427
428        // Assert that in both cases the verifier gives the same result
429        if (resultWhenPatternInCn != resultWhenPatternInSubjectAltName) {
430            fail("Different results between pattern in CN and SubjectAltName."
431                    + " hostname : " + hostname + ", pattern: " + pattern
432                    + ", when pattern in CN: " + resultWhenPatternInCn
433                    + ", when pattern in SubjectAltName: " + resultWhenPatternInSubjectAltName);
434        }
435        return resultWhenPatternInCn;
436    }
437
438    /**
439     * Verifies the provided hostname against the provided server certificate.
440     */
441    private boolean verifyWithServerCertificate(String hostname, X509Certificate certificate) {
442        StubSSLSession session = new StubSSLSession();
443        session.peerCertificates =
444                (certificate != null) ? new Certificate[] {certificate} : new Certificate[0];
445        return verifier.verify(hostname, session);
446    }
447
448    X509Certificate parseCertificate(String encoded) throws Exception {
449        InputStream in = new ByteArrayInputStream(encoded.getBytes(StandardCharsets.US_ASCII));
450        return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(in);
451    }
452
453    private static class StubSSLSession implements SSLSession {
454
455        public Certificate[] peerCertificates = new Certificate[0];
456
457        @Override
458        public int getApplicationBufferSize() {
459            throw new UnsupportedOperationException();
460        }
461
462        @Override
463        public String getCipherSuite() {
464            throw new UnsupportedOperationException();
465        }
466
467        @Override
468        public long getCreationTime() {
469            throw new UnsupportedOperationException();
470        }
471
472        @Override
473        public byte[] getId() {
474            throw new UnsupportedOperationException();
475        }
476
477        @Override
478        public long getLastAccessedTime() {
479            throw new UnsupportedOperationException();
480        }
481
482        @Override
483        public Certificate[] getLocalCertificates() {
484            throw new UnsupportedOperationException();
485        }
486
487        @Override
488        public Principal getLocalPrincipal() {
489            throw new UnsupportedOperationException();
490        }
491
492        @Override
493        public int getPacketBufferSize() {
494            throw new UnsupportedOperationException();
495        }
496
497        @Override
498        public javax.security.cert.X509Certificate[] getPeerCertificateChain()
499                throws SSLPeerUnverifiedException {
500            throw new UnsupportedOperationException();
501        }
502
503        @Override
504        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
505            return peerCertificates;
506        }
507
508        @Override
509        public String getPeerHost() {
510            throw new UnsupportedOperationException();
511        }
512
513        @Override
514        public int getPeerPort() {
515            throw new UnsupportedOperationException();
516        }
517
518        @Override
519        public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
520            throw new UnsupportedOperationException();
521        }
522
523        @Override
524        public String getProtocol() {
525            throw new UnsupportedOperationException();
526        }
527
528        @Override
529        public SSLSessionContext getSessionContext() {
530            throw new UnsupportedOperationException();
531        }
532
533        @Override
534        public Object getValue(String name) {
535            throw new UnsupportedOperationException();
536        }
537
538        @Override
539        public String[] getValueNames() {
540            throw new UnsupportedOperationException();
541        }
542
543        @Override
544        public void invalidate() {
545            throw new UnsupportedOperationException();
546        }
547
548        @Override
549        public boolean isValid() {
550            return true;
551        }
552
553        @Override
554        public void putValue(String name, Object value) {
555            throw new UnsupportedOperationException();
556        }
557
558        @Override
559        public void removeValue(String name) {
560            throw new UnsupportedOperationException();
561        }
562    }
563
564    private static class StubX509Certificate extends X509Certificate {
565        private final X500Principal subjectX500Principal;
566        private Collection<List<?>> subjectAlternativeNames;
567
568        public StubX509Certificate(String subjectDn) {
569            subjectX500Principal = new X500Principal(subjectDn);
570            subjectAlternativeNames = null;
571        }
572
573        public StubX509Certificate addSubjectAlternativeName(int type, String name) {
574            if (subjectAlternativeNames == null) {
575                subjectAlternativeNames = new ArrayList<List<?>>();
576            }
577            LinkedList<Object> entry = new LinkedList<Object>();
578            entry.add(type);
579            entry.add(name);
580            subjectAlternativeNames.add(entry);
581            return this;
582        }
583
584        @Override public Collection<List<?>> getSubjectAlternativeNames() {
585            return subjectAlternativeNames;
586        }
587
588        @Override public X500Principal getSubjectX500Principal() {
589            return subjectX500Principal;
590        }
591
592        @Override public void checkValidity() {
593            throw new UnsupportedOperationException();
594        }
595
596        @Override public void checkValidity(Date date) {
597            throw new UnsupportedOperationException();
598        }
599
600        @Override public int getBasicConstraints() {
601            throw new UnsupportedOperationException();
602        }
603
604        @Override public Principal getIssuerDN() {
605            throw new UnsupportedOperationException();
606        }
607
608        @Override public boolean[] getIssuerUniqueID() {
609            throw new UnsupportedOperationException();
610        }
611
612        @Override public boolean[] getKeyUsage() {
613            throw new UnsupportedOperationException();
614        }
615
616        @Override public Date getNotAfter() {
617            throw new UnsupportedOperationException();
618        }
619
620        @Override public Date getNotBefore() {
621            throw new UnsupportedOperationException();
622        }
623
624        @Override public BigInteger getSerialNumber() {
625            throw new UnsupportedOperationException();
626        }
627
628        @Override public String getSigAlgName() {
629            throw new UnsupportedOperationException();
630        }
631
632        @Override public String getSigAlgOID() {
633            throw new UnsupportedOperationException();
634        }
635
636        @Override public byte[] getSigAlgParams() {
637            throw new UnsupportedOperationException();
638        }
639
640        @Override public byte[] getSignature() {
641            throw new UnsupportedOperationException();
642        }
643
644        @Override public Principal getSubjectDN() {
645            throw new UnsupportedOperationException();
646        }
647
648        @Override public boolean[] getSubjectUniqueID() {
649            throw new UnsupportedOperationException();
650        }
651
652        @Override public byte[] getTBSCertificate() {
653            throw new UnsupportedOperationException();
654        }
655
656        @Override public int getVersion() {
657            throw new UnsupportedOperationException();
658        }
659
660        @Override public byte[] getEncoded() {
661            throw new UnsupportedOperationException();
662        }
663
664        @Override public PublicKey getPublicKey() {
665            throw new UnsupportedOperationException();
666        }
667
668        @Override public String toString() {
669            throw new UnsupportedOperationException();
670        }
671
672        @Override public void verify(PublicKey key) {
673            throw new UnsupportedOperationException();
674        }
675
676        @Override public void verify(PublicKey key, String sigProvider) {
677            throw new UnsupportedOperationException();
678        }
679
680        @Override public Set<String> getCriticalExtensionOIDs() {
681            throw new UnsupportedOperationException();
682        }
683
684        @Override public byte[] getExtensionValue(String oid) {
685            throw new UnsupportedOperationException();
686        }
687
688        @Override public Set<String> getNonCriticalExtensionOIDs() {
689            throw new UnsupportedOperationException();
690        }
691
692        @Override public boolean hasUnsupportedCriticalExtension() {
693            throw new UnsupportedOperationException();
694        }
695    }
696}
697