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 18 package tests.api.javax.net.ssl; 19 20 import java.io.ByteArrayInputStream; 21 import java.io.InputStream; 22 import java.security.cert.CertificateFactory; 23 import java.security.cert.X509Certificate; 24 import javax.net.ssl.HostnameVerifier; 25 import javax.net.ssl.HttpsURLConnection; 26 import javax.security.auth.x500.X500Principal; 27 import junit.framework.TestCase; 28 import org.apache.harmony.xnet.tests.support.mySSLSession; 29 30 public 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 assertTrue(verifier.verify("foo.com", session)); 75 assertFalse(verifier.verify("a.foo.com", session)); 76 // these checks test alternative subjects. The test data contains an 77 // alternative subject starting with a japanese kanji character. This is 78 // not supported by Android because the underlying implementation from 79 // harmony follows the definition from rfc 1034 page 10 for alternative 80 // subject names. This causes the code to drop all alternative subjects. 81 // assertTrue(verifier.verify("bar.com", session)); 82 // assertFalse(verifier.verify("a.bar.com", session)); 83 // assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); 84 85 in = new ByteArrayInputStream(X509_NO_CNS_FOO); 86 x509 = (X509Certificate) cf.generateCertificate(in); 87 session = new mySSLSession(new X509Certificate[] {x509}); 88 assertTrue(verifier.verify("foo.com", session)); 89 assertFalse(verifier.verify("a.foo.com", session)); 90 91 in = new ByteArrayInputStream(X509_NO_CNS_FOO); 92 x509 = (X509Certificate) cf.generateCertificate(in); 93 session = new mySSLSession(new X509Certificate[] {x509}); 94 assertTrue(verifier.verify("foo.com", session)); 95 assertFalse(verifier.verify("a.foo.com", session)); 96 97 in = new ByteArrayInputStream(X509_THREE_CNS_FOO_BAR_HANAKO); 98 x509 = (X509Certificate) cf.generateCertificate(in); 99 session = new mySSLSession(new X509Certificate[] {x509}); 100 assertFalse(verifier.verify("foo.com", session)); 101 assertFalse(verifier.verify("a.foo.com", session)); 102 assertFalse(verifier.verify("bar.com", session)); 103 assertFalse(verifier.verify("a.bar.com", session)); 104 assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); 105 assertFalse(verifier.verify("a.\u82b1\u5b50.co.jp", session)); 106 107 in = new ByteArrayInputStream(X509_WILD_FOO); 108 x509 = (X509Certificate) cf.generateCertificate(in); 109 session = new mySSLSession(new X509Certificate[] {x509}); 110 assertTrue(verifier.verify("foo.com", session)); 111 assertTrue(verifier.verify("www.foo.com", session)); 112 assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session)); 113 assertFalse(verifier.verify("a.b.foo.com", session)); 114 115 in = new ByteArrayInputStream(X509_WILD_CO_JP); 116 x509 = (X509Certificate) cf.generateCertificate(in); 117 session = new mySSLSession(new X509Certificate[] {x509}); 118 assertTrue(verifier.verify("foo.co.jp", session)); 119 assertTrue(verifier.verify("\u82b1\u5b50.co.jp", session)); 120 121 in = new ByteArrayInputStream(X509_WILD_FOO_BAR_HANAKO); 122 x509 = (X509Certificate) cf.generateCertificate(in); 123 session = new mySSLSession(new X509Certificate[] {x509}); 124 // try the foo.com variations 125 assertTrue(verifier.verify("foo.com", session)); 126 assertTrue(verifier.verify("www.foo.com", session)); 127 assertTrue(verifier.verify("\u82b1\u5b50.foo.com", session)); 128 assertFalse(verifier.verify("a.b.foo.com", session)); 129 // these checks test alternative subjects. The test data contains an 130 // alternative subject starting with a japanese kanji character. This is 131 // not supported by Android because the underlying implementation from 132 // harmony follows the definition from rfc 1034 page 10 for alternative 133 // subject names. This causes the code to drop all alternative subjects. 134 // assertFalse(verifier.verify("bar.com", session)); 135 // assertTrue(verifier.verify("www.bar.com", session)); 136 // assertTrue(verifier.verify("\u82b1\u5b50.bar.com", session)); 137 // assertTrue(verifier.verify("a.b.bar.com", session)); 138 } 139 140 public void testSubjectAlt() throws Exception { 141 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 142 InputStream in = new ByteArrayInputStream(X509_MULTIPLE_SUBJECT_ALT); 143 X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); 144 mySSLSession session = new mySSLSession(new X509Certificate[] {x509}); 145 146 HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); 147 assertEquals(new X500Principal("CN=localhost"), x509.getSubjectX500Principal()); 148 149 assertTrue(verifier.verify("localhost", session)); 150 assertTrue(verifier.verify("localhost.localdomain", session)); 151 assertFalse(verifier.verify("local.host", session)); 152 } 153 154 public void testVerifyIpAddress() throws Exception { 155 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 156 InputStream in = new ByteArrayInputStream(X509_MULTIPLE_SUBJECT_ALT); 157 X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); 158 mySSLSession session = new mySSLSession(new X509Certificate[] { x509 }); 159 HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); 160 161 assertTrue(verifier.verify("127.0.0.1", session)); 162 assertFalse(verifier.verify("127.0.0.2", session)); 163 } 164 165 public void testWildcardsCannotMatchIpAddresses() throws Exception { 166 // openssl req -x509 -nodes -days 36500 -subj '/CN=*.0.0.1' -newkey rsa:512 -out cert.pem 167 String cert = "-----BEGIN CERTIFICATE-----\n" 168 + "MIIBkjCCATygAwIBAgIJAMdemqOwd/BEMA0GCSqGSIb3DQEBBQUAMBIxEDAOBgNV\n" 169 + "BAMUByouMC4wLjEwIBcNMTAxMjIwMTY0NDI1WhgPMjExMDExMjYxNjQ0MjVaMBIx\n" 170 + "EDAOBgNVBAMUByouMC4wLjEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqY8c9Qrt\n" 171 + "YPWCvb7lclI+aDHM6fgbJcHsS9Zg8nUOh5dWrS7AgeA25wyaokFl4plBbbHQe2j+\n" 172 + "cCjsRiJIcQo9HwIDAQABo3MwcTAdBgNVHQ4EFgQUJ436TZPJvwCBKklZZqIvt1Yt\n" 173 + "JjEwQgYDVR0jBDswOYAUJ436TZPJvwCBKklZZqIvt1YtJjGhFqQUMBIxEDAOBgNV\n" 174 + "BAMUByouMC4wLjGCCQDHXpqjsHfwRDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n" 175 + "BQUAA0EAk9i88xdjWoewqvE+iMC9tD2obMchgFDaHH0ogxxiRaIKeEly3g0uGxIt\n" 176 + "fl2WRY8hb4x+zRrwsFaLEpdEvqcjOQ==\n" 177 + "-----END CERTIFICATE-----"; 178 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 179 InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8")); 180 X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); 181 mySSLSession session = new mySSLSession(new X509Certificate[] { x509 }); 182 HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); 183 184 assertFalse(verifier.verify("127.0.0.1", session)); 185 } 186 187 /** 188 * Earlier implementations of Android's hostname verifier required that 189 * wildcard names wouldn't match "*.com" or similar. This was a nonstandard 190 * check that we've since dropped. It is the CA's responsibility to not hand 191 * out certificates that match so broadly. 192 */ 193 public void testWildcardsDoesNotNeedTwoDots() throws Exception { 194 // openssl req -x509 -nodes -days 36500 -subj '/CN=*.com' -newkey rsa:512 -out cert.pem 195 String cert = "-----BEGIN CERTIFICATE-----\n" 196 + "MIIBjDCCATagAwIBAgIJAOVulXCSu6HuMA0GCSqGSIb3DQEBBQUAMBAxDjAMBgNV\n" 197 + "BAMUBSouY29tMCAXDTEwMTIyMDE2NDkzOFoYDzIxMTAxMTI2MTY0OTM4WjAQMQ4w\n" 198 + "DAYDVQQDFAUqLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDJd8xqni+h7Iaz\n" 199 + "ypItivs9kPuiJUqVz+SuJ1C05SFc3PmlRCvwSIfhyD67fHcbMdl+A/LrIjhhKZJe\n" 200 + "1joO0+pFAgMBAAGjcTBvMB0GA1UdDgQWBBS4Iuzf5w8JdCp+EtBfdFNudf6+YzBA\n" 201 + "BgNVHSMEOTA3gBS4Iuzf5w8JdCp+EtBfdFNudf6+Y6EUpBIwEDEOMAwGA1UEAxQF\n" 202 + "Ki5jb22CCQDlbpVwkruh7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA0EA\n" 203 + "U6LFxmZr31lFyis2/T68PpjAppc0DpNQuA2m/Y7oTHBDi55Fw6HVHCw3lucuWZ5d\n" 204 + "qUYo4ES548JdpQtcLrW2sA==\n" 205 + "-----END CERTIFICATE-----"; 206 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 207 InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8")); 208 X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); 209 mySSLSession session = new mySSLSession(new X509Certificate[] { x509 }); 210 HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); 211 212 assertTrue(verifier.verify("google.com", session)); 213 } 214 215 public void testSubjectAltName() throws Exception { 216 /* 217 * $ cat ./cert.cnf 218 * [req] 219 * distinguished_name=distinguished_name 220 * req_extensions=req_extensions 221 * x509_extensions=x509_extensions 222 * [distinguished_name] 223 * [req_extensions] 224 * [x509_extensions] 225 * subjectAltName=DNS:bar.com,DNS:baz.com 226 * 227 * $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \ 228 * -newkey rsa:512 -out cert.pem 229 */ 230 String cert = "-----BEGIN CERTIFICATE-----\n" 231 + "MIIBPTCB6KADAgECAgkA7zoHaaqNGHQwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n" 232 + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODM5MzZaGA8yMTEwMTEyNjE4MzkzNlowEjEQ\n" 233 + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+gmoSxF+8\n" 234 + "hbV+rgRQqHIJd50216OWQJbU3BvdlPbca779NYO4+UZWTFdBM8BdQqs3H4B5Agvp\n" 235 + "y7HeSff1F7XRAgMBAAGjHzAdMBsGA1UdEQQUMBKCB2Jhci5jb22CB2Jhei5jb20w\n" 236 + "DQYJKoZIhvcNAQEFBQADQQBXpZZPOY2Dy1lGG81JTr8L4or9jpKacD7n51eS8iqI\n" 237 + "oTznPNuXHU5bFN0AAGX2ij47f/EahqTpo5RdS95P4sVm\n" 238 + "-----END CERTIFICATE-----"; 239 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 240 InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8")); 241 X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); 242 mySSLSession session = new mySSLSession(new X509Certificate[] { x509 }); 243 HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); 244 245 assertFalse(verifier.verify("foo.com", session)); 246 assertTrue(verifier.verify("bar.com", session)); 247 assertTrue(verifier.verify("baz.com", session)); 248 assertFalse(verifier.verify("a.foo.com", session)); 249 assertFalse(verifier.verify("quux.com", session)); 250 } 251 252 public void testSubjectAltNameWithWildcard() throws Exception { 253 /* 254 * $ cat ./cert.cnf 255 * [req] 256 * distinguished_name=distinguished_name 257 * req_extensions=req_extensions 258 * x509_extensions=x509_extensions 259 * [distinguished_name] 260 * [req_extensions] 261 * [x509_extensions] 262 * subjectAltName=DNS:bar.com,DNS:*.baz.com 263 * 264 * $ openssl req -x509 -nodes -days 36500 -subj '/CN=foo.com' -config ./cert.cnf \ 265 * -newkey rsa:512 -out cert.pem 266 */ 267 String cert = "-----BEGIN CERTIFICATE-----\n" 268 + "MIIBPzCB6qADAgECAgkAnv/7Jv5r7pMwDQYJKoZIhvcNAQEFBQAwEjEQMA4GA1UE\n" 269 + "AxMHZm9vLmNvbTAgFw0xMDEyMjAxODQ2MDFaGA8yMTEwMTEyNjE4NDYwMVowEjEQ\n" 270 + "MA4GA1UEAxMHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDAz2YXnyog\n" 271 + "YdYLSFr/OEgSumtwqtZKJTB4wqTW/eKbBCEzxnyUMxWZIqUGu353PzwfOuWp2re3\n" 272 + "nvVV+QDYQlh9AgMBAAGjITAfMB0GA1UdEQQWMBSCB2Jhci5jb22CCSouYmF6LmNv\n" 273 + "bTANBgkqhkiG9w0BAQUFAANBAB8yrSl8zqy07i0SNYx2B/FnvQY734pxioaqFWfO\n" 274 + "Bqo1ZZl/9aPHEWIwBrxYNVB0SGu/kkbt/vxqOjzzrkXukmI=\n" 275 + "-----END CERTIFICATE-----"; 276 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 277 InputStream in = new ByteArrayInputStream(cert.getBytes("UTF-8")); 278 X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); 279 mySSLSession session = new mySSLSession(new X509Certificate[] { x509 }); 280 HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); 281 282 assertFalse(verifier.verify("foo.com", session)); 283 assertTrue(verifier.verify("bar.com", session)); 284 assertTrue(verifier.verify("a.baz.com", session)); 285 assertTrue(verifier.verify("baz.com", session)); 286 assertFalse(verifier.verify("a.foo.com", session)); 287 assertFalse(verifier.verify("a.bar.com", session)); 288 assertFalse(verifier.verify("quux.com", session)); 289 } 290 } 291