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 package com.android.internal.net; 17 18 import com.android.internal.net.DomainNameValidator; 19 import com.android.frameworks.coretests.R; 20 import com.android.frameworks.coretests.R.raw; 21 22 import android.test.AndroidTestCase; 23 24 import java.io.InputStream; 25 import java.math.BigInteger; 26 import java.security.InvalidKeyException; 27 import java.security.NoSuchAlgorithmException; 28 import java.security.NoSuchProviderException; 29 import java.security.Principal; 30 import java.security.PublicKey; 31 import java.security.SignatureException; 32 import java.security.cert.CertificateEncodingException; 33 import java.security.cert.CertificateException; 34 import java.security.cert.CertificateExpiredException; 35 import java.security.cert.CertificateFactory; 36 import java.security.cert.CertificateNotYetValidException; 37 import java.security.cert.CertificateParsingException; 38 import java.security.cert.X509Certificate; 39 import java.util.ArrayList; 40 import java.util.Collection; 41 import java.util.Date; 42 import java.util.LinkedList; 43 import java.util.List; 44 import java.util.Set; 45 46 import javax.security.auth.x500.X500Principal; 47 48 public class DomainNameValidatorTest extends AndroidTestCase { 49 private static final int ALT_UNKNOWN = 0; 50 private static final int ALT_DNS_NAME = 2; 51 private static final int ALT_IPA_NAME = 7; 52 53 /** 54 * Tests {@link DomainNameValidator#match}, using a simple {@link X509Certificate} 55 * implementation. 56 */ 57 public void testMatch() { 58 checkMatch("11", new StubX509Certificate("cn=imap.g.com"), "imap.g.com", true); 59 checkMatch("12", new StubX509Certificate("cn=imap2.g.com"), "imap.g.com", false); 60 checkMatch("13", new StubX509Certificate("cn=sub.imap.g.com"), "imap.g.com", false); 61 62 // If a subjectAltName extension of type dNSName is present, that MUST 63 // be used as the identity 64 checkMatch("21", new StubX509Certificate("") 65 .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com") 66 , "imap.g.com", false); 67 checkMatch("22", new StubX509Certificate("cn=imap.g.com") // This cn should be ignored 68 .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com") 69 , "imap.g.com", false); 70 checkMatch("23", new StubX509Certificate("") 71 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com") 72 , "imap.g.com", true); 73 74 // With wildcards 75 checkMatch("24", new StubX509Certificate("") 76 .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com") 77 , "imap.g.com", true); 78 79 // host name is ip address 80 checkMatch("31", new StubX509Certificate("") 81 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4") 82 , "1.2.3.4", true); 83 checkMatch("32", new StubX509Certificate("") 84 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4") 85 , "1.2.3.5", false); 86 checkMatch("32", new StubX509Certificate("") 87 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4") 88 .addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1") 89 , "192.168.100.1", true); 90 91 // Has unknown subject alternative names 92 checkMatch("41", new StubX509Certificate("") 93 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1") 94 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2") 95 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d") 96 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com") 97 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com") 98 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55") 99 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3") 100 , "imap.g.com", true); 101 102 checkMatch("42", new StubX509Certificate("") 103 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1") 104 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2") 105 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d") 106 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com") 107 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com") 108 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55") 109 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3") 110 , "2.33.44.55", true); 111 112 checkMatch("43", new StubX509Certificate("") 113 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1") 114 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2") 115 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d") 116 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com") 117 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com") 118 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55") 119 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3") 120 , "g.com", false); 121 122 checkMatch("44", new StubX509Certificate("") 123 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 1") 124 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 2") 125 .addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d") 126 .addSubjectAlternativeName(ALT_DNS_NAME, "*.google.com") 127 .addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com") 128 .addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55") 129 .addSubjectAlternativeName(ALT_UNKNOWN, "random string 3") 130 , "2.33.44.1", false); 131 } 132 133 private void checkMatch(String message, X509Certificate certificate, String thisDomain, 134 boolean expected) { 135 Boolean actual = DomainNameValidator.match(certificate, thisDomain); 136 assertEquals(message, (Object) expected, (Object) actual); 137 } 138 139 /** 140 * Tests {@link DomainNameValidator#matchDns} 141 */ 142 public void testMatchDns() { 143 checkMatchDns("11", "a.b.c.d", "a.b.c.d", true); 144 checkMatchDns("12", "a.b.c.d", "*.b.c.d", true); 145 checkMatchDns("13", "b.c.d", "*.b.c.d", true); 146 checkMatchDns("14", "b.c.d", "b*.c.d", true); 147 148 checkMatchDns("15", "a.b.c.d", "*.*.c.d", false); 149 checkMatchDns("16", "a.b.c.d", "*.c.d", false); 150 151 checkMatchDns("21", "imap.google.com", "imap.google.com", true); 152 checkMatchDns("22", "imap2.google.com", "imap.google.com", false); 153 checkMatchDns("23", "imap.google.com", "*.google.com", true); 154 checkMatchDns("24", "imap2.google.com", "*.google.com", true); 155 checkMatchDns("25", "imap.google.com", "*.googl.com", false); 156 checkMatchDns("26", "imap2.google2.com", "*.google3.com", false); 157 checkMatchDns("27", "imap.google.com", "ima*.google.com", true); 158 checkMatchDns("28", "imap.google.com", "imap*.google.com", true); 159 checkMatchDns("29", "imap.google.com", "*.imap.google.com", true); 160 161 checkMatchDns("41", "imap.google.com", "a*.google.com", false); 162 checkMatchDns("42", "imap.google.com", "ix*.google.com", false); 163 164 checkMatchDns("51", "imap.google.com", "iMap.Google.Com", true); 165 } 166 167 private void checkMatchDns(String message, String thisDomain, String thatDomain, 168 boolean expected) { 169 boolean actual = DomainNameValidator.matchDns(thisDomain, thatDomain); 170 assertEquals(message, expected, actual); 171 } 172 173 /** 174 * Test {@link DomainNameValidator#match} with actual certificates. 175 */ 176 public void testWithActualCert() throws Exception { 177 // subject_only 178 // 179 // subject: C=JP, CN=www.example.com 180 // subject alt names: n/a 181 checkWithActualCert("11", R.raw.subject_only, "www.example.com", true); 182 checkWithActualCert("12", R.raw.subject_only, "www2.example.com", false); 183 184 // subject_alt_only 185 // 186 // subject: C=JP (no CN) 187 // subject alt names: DNS:www.example.com 188 checkWithActualCert("21", R.raw.subject_alt_only, "www.example.com", true); 189 checkWithActualCert("22", R.raw.subject_alt_only, "www2.example.com", false); 190 191 // subject_with_alt_names 192 // 193 // subject: C=JP, CN=www.example.com 194 // subject alt names: DNS:www2.example.com, DNS:www3.example.com 195 // * Subject should be ignored, because it has subject alt names. 196 checkWithActualCert("31", R.raw.subject_with_alt_names, "www.example.com", false); 197 checkWithActualCert("32", R.raw.subject_with_alt_names, "www2.example.com", true); 198 checkWithActualCert("33", R.raw.subject_with_alt_names, "www3.example.com", true); 199 checkWithActualCert("34", R.raw.subject_with_alt_names, "www4.example.com", false); 200 201 // subject_with_wild_alt_name 202 // 203 // subject: C=JP, CN=www.example.com 204 // subject alt names: DNS:*.example2.com 205 // * Subject should be ignored, because it has subject alt names. 206 checkWithActualCert("41", R.raw.subject_with_wild_alt_name, "www.example.com", false); 207 checkWithActualCert("42", R.raw.subject_with_wild_alt_name, "www2.example.com", false); 208 checkWithActualCert("43", R.raw.subject_with_wild_alt_name, "www.example2.com", true); 209 checkWithActualCert("44", R.raw.subject_with_wild_alt_name, "abc.example2.com", true); 210 checkWithActualCert("45", R.raw.subject_with_wild_alt_name, "www.example3.com", false); 211 212 // wild_alt_name_only 213 // 214 // subject: C=JP 215 // subject alt names: DNS:*.example.com 216 checkWithActualCert("51", R.raw.wild_alt_name_only, "www.example.com", true); 217 checkWithActualCert("52", R.raw.wild_alt_name_only, "www2.example.com", true); 218 checkWithActualCert("53", R.raw.wild_alt_name_only, "www.example2.com", false); 219 220 // wild_alt_name_only 221 // 222 // subject: C=JP 223 // subject alt names: IP Address:192.168.10.1 224 checkWithActualCert("61", R.raw.alt_ip_only, "192.168.10.1", true); 225 checkWithActualCert("61", R.raw.alt_ip_only, "192.168.10.2", false); 226 } 227 228 private void checkWithActualCert(String message, int resId, String domain, 229 boolean expected) throws Exception { 230 CertificateFactory factory = CertificateFactory.getInstance("X509"); 231 InputStream certStream = getContext().getResources().openRawResource(resId); 232 X509Certificate certificate = (X509Certificate) factory.generateCertificate(certStream); 233 234 checkMatch(message, certificate, domain, expected); 235 } 236 237 /** 238 * Minimal {@link X509Certificate} implementation for {@link DomainNameValidator}. 239 */ 240 private static class StubX509Certificate extends X509Certificate { 241 private final X500Principal subjectX500Principal; 242 private Collection<List<?>> subjectAlternativeNames; 243 244 public StubX509Certificate(String subjectDn) { 245 subjectX500Principal = new X500Principal(subjectDn); 246 subjectAlternativeNames = null; 247 } 248 249 public StubX509Certificate addSubjectAlternativeName(int type, String name) { 250 if (subjectAlternativeNames == null) { 251 subjectAlternativeNames = new ArrayList<List<?>>(); 252 } 253 LinkedList<Object> entry = new LinkedList<Object>(); 254 entry.add(type); 255 entry.add(name); 256 subjectAlternativeNames.add(entry); 257 return this; 258 } 259 260 @Override 261 public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException { 262 return subjectAlternativeNames; 263 } 264 265 @Override 266 public X500Principal getSubjectX500Principal() { 267 return subjectX500Principal; 268 } 269 270 @Override 271 public void checkValidity() throws CertificateExpiredException, 272 CertificateNotYetValidException { 273 throw new RuntimeException("Method not implemented"); 274 } 275 276 @Override 277 public void checkValidity(Date date) throws CertificateExpiredException, 278 CertificateNotYetValidException { 279 throw new RuntimeException("Method not implemented"); 280 } 281 282 @Override 283 public int getBasicConstraints() { 284 throw new RuntimeException("Method not implemented"); 285 } 286 287 @Override 288 public Principal getIssuerDN() { 289 throw new RuntimeException("Method not implemented"); 290 } 291 292 @Override 293 public boolean[] getIssuerUniqueID() { 294 throw new RuntimeException("Method not implemented"); 295 } 296 297 @Override 298 public boolean[] getKeyUsage() { 299 throw new RuntimeException("Method not implemented"); 300 } 301 302 @Override 303 public Date getNotAfter() { 304 throw new RuntimeException("Method not implemented"); 305 } 306 307 @Override 308 public Date getNotBefore() { 309 throw new RuntimeException("Method not implemented"); 310 } 311 312 @Override 313 public BigInteger getSerialNumber() { 314 throw new RuntimeException("Method not implemented"); 315 } 316 317 @Override 318 public String getSigAlgName() { 319 throw new RuntimeException("Method not implemented"); 320 } 321 322 @Override 323 public String getSigAlgOID() { 324 throw new RuntimeException("Method not implemented"); 325 } 326 327 @Override 328 public byte[] getSigAlgParams() { 329 throw new RuntimeException("Method not implemented"); 330 } 331 332 @Override 333 public byte[] getSignature() { 334 throw new RuntimeException("Method not implemented"); 335 } 336 337 @Override 338 public Principal getSubjectDN() { 339 throw new RuntimeException("Method not implemented"); 340 } 341 342 @Override 343 public boolean[] getSubjectUniqueID() { 344 throw new RuntimeException("Method not implemented"); 345 } 346 347 @Override 348 public byte[] getTBSCertificate() throws CertificateEncodingException { 349 throw new RuntimeException("Method not implemented"); 350 } 351 352 @Override 353 public int getVersion() { 354 throw new RuntimeException("Method not implemented"); 355 } 356 357 @Override 358 public byte[] getEncoded() throws CertificateEncodingException { 359 throw new RuntimeException("Method not implemented"); 360 } 361 362 @Override 363 public PublicKey getPublicKey() { 364 throw new RuntimeException("Method not implemented"); 365 } 366 367 @Override 368 public String toString() { 369 throw new RuntimeException("Method not implemented"); 370 } 371 372 @Override 373 public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, 374 InvalidKeyException, NoSuchProviderException, SignatureException { 375 throw new RuntimeException("Method not implemented"); 376 } 377 378 @Override 379 public void verify(PublicKey key, String sigProvider) throws CertificateException, 380 NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, 381 SignatureException { 382 throw new RuntimeException("Method not implemented"); 383 } 384 385 public Set<String> getCriticalExtensionOIDs() { 386 throw new RuntimeException("Method not implemented"); 387 } 388 389 public byte[] getExtensionValue(String oid) { 390 throw new RuntimeException("Method not implemented"); 391 } 392 393 public Set<String> getNonCriticalExtensionOIDs() { 394 throw new RuntimeException("Method not implemented"); 395 } 396 397 public boolean hasUnsupportedCriticalExtension() { 398 throw new RuntimeException("Method not implemented"); 399 } 400 } 401 } 402