1 /* 2 * Copyright (C) 2011 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 17 package org.conscrypt; 18 19 import java.io.File; 20 import java.io.FileOutputStream; 21 import java.io.OutputStream; 22 import java.security.KeyStore; 23 import java.security.PrivateKey; 24 import java.security.PublicKey; 25 import java.security.cert.Certificate; 26 import java.security.cert.X509Certificate; 27 import java.util.Arrays; 28 import java.util.Collections; 29 import java.util.Enumeration; 30 import java.util.HashSet; 31 import java.util.List; 32 import java.util.NoSuchElementException; 33 import java.util.Set; 34 import javax.security.auth.x500.X500Principal; 35 import junit.framework.TestCase; 36 import libcore.java.security.TestKeyStore; 37 38 public class TrustedCertificateStoreTest extends TestCase { 39 40 private static final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir")); 41 private static final File DIR_TEST = new File(DIR_TEMP, "test"); 42 private static final File DIR_SYSTEM = new File(DIR_TEST, "system"); 43 private static final File DIR_ADDED = new File(DIR_TEST, "added"); 44 private static final File DIR_DELETED = new File(DIR_TEST, "removed"); 45 46 private static X509Certificate CA1; 47 private static X509Certificate CA2; 48 49 private static KeyStore.PrivateKeyEntry PRIVATE; 50 private static X509Certificate[] CHAIN; 51 52 private static X509Certificate CA3_WITH_CA1_SUBJECT; 53 private static String ALIAS_SYSTEM_CA1; 54 private static String ALIAS_SYSTEM_CA2; 55 private static String ALIAS_USER_CA1; 56 private static String ALIAS_USER_CA2; 57 58 private static String ALIAS_SYSTEM_CHAIN0; 59 private static String ALIAS_SYSTEM_CHAIN1; 60 private static String ALIAS_SYSTEM_CHAIN2; 61 private static String ALIAS_USER_CHAIN0; 62 private static String ALIAS_USER_CHAIN1; 63 private static String ALIAS_USER_CHAIN2; 64 65 private static String ALIAS_SYSTEM_CA3; 66 private static String ALIAS_SYSTEM_CA3_COLLISION; 67 private static String ALIAS_USER_CA3; 68 private static String ALIAS_USER_CA3_COLLISION; 69 70 private static X509Certificate getCa1() { 71 initCerts(); 72 return CA1; 73 } 74 private static X509Certificate getCa2() { 75 initCerts(); 76 return CA2; 77 } 78 79 private static KeyStore.PrivateKeyEntry getPrivate() { 80 initCerts(); 81 return PRIVATE; 82 } 83 private static X509Certificate[] getChain() { 84 initCerts(); 85 return CHAIN; 86 } 87 88 private static X509Certificate getCa3WithCa1Subject() { 89 initCerts(); 90 return CA3_WITH_CA1_SUBJECT; 91 } 92 93 private static String getAliasSystemCa1() { 94 initCerts(); 95 return ALIAS_SYSTEM_CA1; 96 } 97 private static String getAliasSystemCa2() { 98 initCerts(); 99 return ALIAS_SYSTEM_CA2; 100 } 101 private static String getAliasUserCa1() { 102 initCerts(); 103 return ALIAS_USER_CA1; 104 } 105 private static String getAliasUserCa2() { 106 initCerts(); 107 return ALIAS_USER_CA2; 108 } 109 110 private static String getAliasSystemChain0() { 111 initCerts(); 112 return ALIAS_SYSTEM_CHAIN0; 113 } 114 private static String getAliasSystemChain1() { 115 initCerts(); 116 return ALIAS_SYSTEM_CHAIN1; 117 } 118 private static String getAliasSystemChain2() { 119 initCerts(); 120 return ALIAS_SYSTEM_CHAIN2; 121 } 122 private static String getAliasUserChain0() { 123 initCerts(); 124 return ALIAS_USER_CHAIN0; 125 } 126 private static String getAliasUserChain1() { 127 initCerts(); 128 return ALIAS_USER_CHAIN1; 129 } 130 private static String getAliasUserChain2() { 131 initCerts(); 132 return ALIAS_USER_CHAIN2; 133 } 134 135 private static String getAliasSystemCa3() { 136 initCerts(); 137 return ALIAS_SYSTEM_CA3; 138 } 139 private static String getAliasSystemCa3Collision() { 140 initCerts(); 141 return ALIAS_SYSTEM_CA3_COLLISION; 142 } 143 private static String getAliasUserCa3() { 144 initCerts(); 145 return ALIAS_USER_CA3; 146 } 147 private static String getAliasUserCa3Collision() { 148 initCerts(); 149 return ALIAS_USER_CA3_COLLISION; 150 } 151 152 /** 153 * Lazily create shared test certificates. 154 */ 155 private static synchronized void initCerts() { 156 if (CA1 != null) { 157 return; 158 } 159 try { 160 CA1 = TestKeyStore.getClient().getRootCertificate("RSA"); 161 CA2 = TestKeyStore.getClientCA2().getRootCertificate("RSA"); 162 PRIVATE = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 163 CHAIN = (X509Certificate[]) PRIVATE.getCertificateChain(); 164 CA3_WITH_CA1_SUBJECT = new TestKeyStore.Builder() 165 .aliasPrefix("unused") 166 .subject(CA1.getSubjectX500Principal()) 167 .ca(true) 168 .build().getRootCertificate("RSA"); 169 170 171 ALIAS_SYSTEM_CA1 = alias(false, CA1, 0); 172 ALIAS_SYSTEM_CA2 = alias(false, CA2, 0); 173 ALIAS_USER_CA1 = alias(true, CA1, 0); 174 ALIAS_USER_CA2 = alias(true, CA2, 0); 175 176 ALIAS_SYSTEM_CHAIN0 = alias(false, getChain()[0], 0); 177 ALIAS_SYSTEM_CHAIN1 = alias(false, getChain()[1], 0); 178 ALIAS_SYSTEM_CHAIN2 = alias(false, getChain()[2], 0); 179 ALIAS_USER_CHAIN0 = alias(true, getChain()[0], 0); 180 ALIAS_USER_CHAIN1 = alias(true, getChain()[1], 0); 181 ALIAS_USER_CHAIN2 = alias(true, getChain()[2], 0); 182 183 ALIAS_SYSTEM_CA3 = alias(false, CA3_WITH_CA1_SUBJECT, 0); 184 ALIAS_SYSTEM_CA3_COLLISION = alias(false, CA3_WITH_CA1_SUBJECT, 1); 185 ALIAS_USER_CA3 = alias(true, CA3_WITH_CA1_SUBJECT, 0); 186 ALIAS_USER_CA3_COLLISION = alias(true, CA3_WITH_CA1_SUBJECT, 1); 187 } catch (Exception e) { 188 throw new RuntimeException(e); 189 } 190 } 191 192 private TrustedCertificateStore store; 193 194 @Override protected void setUp() { 195 setupStore(); 196 } 197 198 private void setupStore() { 199 DIR_SYSTEM.mkdirs(); 200 createStore(); 201 } 202 203 private void createStore() { 204 store = new TrustedCertificateStore(DIR_SYSTEM, DIR_ADDED, DIR_DELETED); 205 } 206 207 @Override protected void tearDown() { 208 cleanStore(); 209 } 210 211 private void cleanStore() { 212 for (File dir : new File[] { DIR_SYSTEM, DIR_ADDED, DIR_DELETED, DIR_TEST }) { 213 File[] files = dir.listFiles(); 214 if (files == null) { 215 continue; 216 } 217 for (File file : files) { 218 assertTrue(file.delete()); 219 } 220 } 221 store = null; 222 } 223 224 private void resetStore() { 225 cleanStore(); 226 setupStore(); 227 } 228 229 public void testEmptyDirectories() throws Exception { 230 assertEmpty(); 231 } 232 233 public void testOneSystemOneDeleted() throws Exception { 234 install(getCa1(), getAliasSystemCa1()); 235 store.deleteCertificateEntry(getAliasSystemCa1()); 236 assertEmpty(); 237 assertDeleted(getCa1(), getAliasSystemCa1()); 238 } 239 240 public void testTwoSystemTwoDeleted() throws Exception { 241 install(getCa1(), getAliasSystemCa1()); 242 store.deleteCertificateEntry(getAliasSystemCa1()); 243 install(getCa2(), getAliasSystemCa2()); 244 store.deleteCertificateEntry(getAliasSystemCa2()); 245 assertEmpty(); 246 assertDeleted(getCa1(), getAliasSystemCa1()); 247 assertDeleted(getCa2(), getAliasSystemCa2()); 248 } 249 250 public void testPartialFileIsIgnored() throws Exception { 251 File file = file(getAliasSystemCa1()); 252 OutputStream os = new FileOutputStream(file); 253 os.write(0); 254 os.close(); 255 assertTrue(file.exists()); 256 assertEmpty(); 257 assertTrue(file.exists()); 258 } 259 260 private void assertEmpty() throws Exception { 261 try { 262 store.getCertificate(null); 263 fail(); 264 } catch (NullPointerException expected) { 265 } 266 assertNull(store.getCertificate("")); 267 268 try { 269 store.getCreationDate(null); 270 fail(); 271 } catch (NullPointerException expected) { 272 } 273 assertNull(store.getCreationDate("")); 274 275 Set<String> s = store.aliases(); 276 assertNotNull(s); 277 assertTrue(s.isEmpty()); 278 assertAliases(); 279 280 Set<String> u = store.userAliases(); 281 assertNotNull(u); 282 assertTrue(u.isEmpty()); 283 284 try { 285 store.containsAlias(null); 286 fail(); 287 } catch (NullPointerException expected) { 288 } 289 assertFalse(store.containsAlias("")); 290 291 assertNull(store.getCertificateAlias(null)); 292 assertNull(store.getCertificateAlias(getCa1())); 293 294 try { 295 store.isTrustAnchor(null); 296 fail(); 297 } catch (NullPointerException expected) { 298 } 299 assertFalse(store.isTrustAnchor(getCa1())); 300 301 try { 302 store.findIssuer(null); 303 fail(); 304 } catch (NullPointerException expected) { 305 } 306 assertNull(store.findIssuer(getCa1())); 307 308 try { 309 store.installCertificate(null); 310 fail(); 311 } catch (NullPointerException expected) { 312 } 313 314 store.deleteCertificateEntry(null); 315 store.deleteCertificateEntry(""); 316 317 String[] userFiles = DIR_ADDED.list(); 318 assertTrue(userFiles == null || userFiles.length == 0); 319 } 320 321 public void testTwoSystem() throws Exception { 322 testTwo(getCa1(), getAliasSystemCa1(), 323 getCa2(), getAliasSystemCa2()); 324 } 325 326 public void testTwoUser() throws Exception { 327 testTwo(getCa1(), getAliasUserCa1(), 328 getCa2(), getAliasUserCa2()); 329 } 330 331 public void testOneSystemOneUser() throws Exception { 332 testTwo(getCa1(), getAliasSystemCa1(), 333 getCa2(), getAliasUserCa2()); 334 } 335 336 public void testTwoSystemSameSubject() throws Exception { 337 testTwo(getCa1(), getAliasSystemCa1(), 338 getCa3WithCa1Subject(), getAliasSystemCa3Collision()); 339 } 340 341 public void testTwoUserSameSubject() throws Exception { 342 testTwo(getCa1(), getAliasUserCa1(), 343 getCa3WithCa1Subject(), getAliasUserCa3Collision()); 344 345 store.deleteCertificateEntry(getAliasUserCa1()); 346 assertDeleted(getCa1(), getAliasUserCa1()); 347 assertTombstone(getAliasUserCa1()); 348 assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3Collision()); 349 assertAliases(getAliasUserCa3Collision()); 350 351 store.deleteCertificateEntry(getAliasUserCa3Collision()); 352 assertDeleted(getCa3WithCa1Subject(), getAliasUserCa3Collision()); 353 assertNoTombstone(getAliasUserCa3Collision()); 354 assertNoTombstone(getAliasUserCa1()); 355 assertEmpty(); 356 } 357 358 public void testOneSystemOneUserSameSubject() throws Exception { 359 testTwo(getCa1(), getAliasSystemCa1(), 360 getCa3WithCa1Subject(), getAliasUserCa3()); 361 testTwo(getCa1(), getAliasUserCa1(), 362 getCa3WithCa1Subject(), getAliasSystemCa3()); 363 } 364 365 private void testTwo(X509Certificate x1, String alias1, 366 X509Certificate x2, String alias2) { 367 install(x1, alias1); 368 install(x2, alias2); 369 assertRootCa(x1, alias1); 370 assertRootCa(x2, alias2); 371 assertAliases(alias1, alias2); 372 } 373 374 375 public void testOneSystemOneUserOneDeleted() throws Exception { 376 install(getCa1(), getAliasSystemCa1()); 377 store.installCertificate(getCa2()); 378 store.deleteCertificateEntry(getAliasSystemCa1()); 379 assertDeleted(getCa1(), getAliasSystemCa1()); 380 assertRootCa(getCa2(), getAliasUserCa2()); 381 assertAliases(getAliasUserCa2()); 382 } 383 384 public void testOneSystemOneUserOneDeletedSameSubject() throws Exception { 385 install(getCa1(), getAliasSystemCa1()); 386 store.installCertificate(getCa3WithCa1Subject()); 387 store.deleteCertificateEntry(getAliasSystemCa1()); 388 assertDeleted(getCa1(), getAliasSystemCa1()); 389 assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3()); 390 assertAliases(getAliasUserCa3()); 391 } 392 393 public void testUserMaskingSystem() throws Exception { 394 install(getCa1(), getAliasSystemCa1()); 395 install(getCa1(), getAliasUserCa1()); 396 assertMasked(getCa1(), getAliasSystemCa1()); 397 assertRootCa(getCa1(), getAliasUserCa1()); 398 assertAliases(getAliasSystemCa1(), getAliasUserCa1()); 399 } 400 401 public void testChain() throws Exception { 402 testChain(getAliasSystemChain1(), getAliasSystemChain2()); 403 testChain(getAliasSystemChain1(), getAliasUserChain2()); 404 testChain(getAliasUserChain1(), getAliasSystemCa1()); 405 testChain(getAliasUserChain1(), getAliasUserChain2()); 406 } 407 408 private void testChain(String alias1, String alias2) throws Exception { 409 install(getChain()[1], alias1); 410 install(getChain()[2], alias2); 411 assertIntermediateCa(getChain()[1], alias1); 412 assertRootCa(getChain()[2], alias2); 413 assertAliases(alias1, alias2); 414 assertEquals(getChain()[2], store.findIssuer(getChain()[1])); 415 assertEquals(getChain()[1], store.findIssuer(getChain()[0])); 416 417 X509Certificate[] expected = getChain(); 418 List<X509Certificate> actualList = store.getCertificateChain(expected[0]); 419 420 assertEquals("Generated CA list should be same length", expected.length, actualList.size()); 421 for (int i = 0; i < expected.length; i++) { 422 assertEquals("Chain value should be the same for position " + i, expected[i], 423 actualList.get(i)); 424 } 425 resetStore(); 426 } 427 428 public void testMissingSystemDirectory() throws Exception { 429 cleanStore(); 430 createStore(); 431 assertEmpty(); 432 } 433 434 public void testWithExistingUserDirectories() throws Exception { 435 DIR_ADDED.mkdirs(); 436 DIR_DELETED.mkdirs(); 437 install(getCa1(), getAliasSystemCa1()); 438 assertRootCa(getCa1(), getAliasSystemCa1()); 439 assertAliases(getAliasSystemCa1()); 440 } 441 442 public void testIsTrustAnchorWithReissuedgetCa() throws Exception { 443 PublicKey publicKey = getPrivate().getCertificate().getPublicKey(); 444 PrivateKey privateKey = getPrivate().getPrivateKey(); 445 String name = "CN=CA4"; 446 X509Certificate ca1 = TestKeyStore.createCa(publicKey, privateKey, name); 447 Thread.sleep(1 * 1000); // wait to ensure CAs vary by expiration 448 X509Certificate ca2 = TestKeyStore.createCa(publicKey, privateKey, name); 449 assertFalse(ca1.equals(ca2)); 450 451 String systemAlias = alias(false, ca1, 0); 452 install(ca1, systemAlias); 453 assertRootCa(ca1, systemAlias); 454 assertTrue(store.isTrustAnchor(ca2)); 455 assertEquals(ca1, store.findIssuer(ca2)); 456 resetStore(); 457 458 String userAlias = alias(true, ca1, 0); 459 store.installCertificate(ca1); 460 assertRootCa(ca1, userAlias); 461 assertTrue(store.isTrustAnchor(ca2)); 462 assertEquals(ca1, store.findIssuer(ca2)); 463 resetStore(); 464 } 465 466 public void testInstallEmpty() throws Exception { 467 store.installCertificate(getCa1()); 468 assertRootCa(getCa1(), getAliasUserCa1()); 469 assertAliases(getAliasUserCa1()); 470 471 // reinstalling should not change anything 472 store.installCertificate(getCa1()); 473 assertRootCa(getCa1(), getAliasUserCa1()); 474 assertAliases(getAliasUserCa1()); 475 } 476 477 public void testInstallEmptySystemExists() throws Exception { 478 install(getCa1(), getAliasSystemCa1()); 479 assertRootCa(getCa1(), getAliasSystemCa1()); 480 assertAliases(getAliasSystemCa1()); 481 482 // reinstalling should not affect system CA 483 store.installCertificate(getCa1()); 484 assertRootCa(getCa1(), getAliasSystemCa1()); 485 assertAliases(getAliasSystemCa1()); 486 487 } 488 489 public void testInstallEmptyDeletedSystemExists() throws Exception { 490 install(getCa1(), getAliasSystemCa1()); 491 store.deleteCertificateEntry(getAliasSystemCa1()); 492 assertEmpty(); 493 assertDeleted(getCa1(), getAliasSystemCa1()); 494 495 // installing should restore deleted system CA 496 store.installCertificate(getCa1()); 497 assertRootCa(getCa1(), getAliasSystemCa1()); 498 assertAliases(getAliasSystemCa1()); 499 } 500 501 public void testDeleteEmpty() throws Exception { 502 store.deleteCertificateEntry(getAliasSystemCa1()); 503 assertEmpty(); 504 assertDeleted(getCa1(), getAliasSystemCa1()); 505 } 506 507 public void testDeleteUser() throws Exception { 508 store.installCertificate(getCa1()); 509 assertRootCa(getCa1(), getAliasUserCa1()); 510 assertAliases(getAliasUserCa1()); 511 512 store.deleteCertificateEntry(getAliasUserCa1()); 513 assertEmpty(); 514 assertDeleted(getCa1(), getAliasUserCa1()); 515 assertNoTombstone(getAliasUserCa1()); 516 } 517 518 public void testDeleteSystem() throws Exception { 519 install(getCa1(), getAliasSystemCa1()); 520 assertRootCa(getCa1(), getAliasSystemCa1()); 521 assertAliases(getAliasSystemCa1()); 522 523 store.deleteCertificateEntry(getAliasSystemCa1()); 524 assertEmpty(); 525 assertDeleted(getCa1(), getAliasSystemCa1()); 526 527 // deleting again should not change anything 528 store.deleteCertificateEntry(getAliasSystemCa1()); 529 assertEmpty(); 530 assertDeleted(getCa1(), getAliasSystemCa1()); 531 } 532 533 public void testIsUserAddedCertificate() throws Exception { 534 assertFalse(store.isUserAddedCertificate(getCa1())); 535 assertFalse(store.isUserAddedCertificate(getCa2())); 536 install(getCa1(), getAliasSystemCa1()); 537 assertFalse(store.isUserAddedCertificate(getCa1())); 538 assertFalse(store.isUserAddedCertificate(getCa2())); 539 install(getCa1(), getAliasUserCa1()); 540 assertTrue(store.isUserAddedCertificate(getCa1())); 541 assertFalse(store.isUserAddedCertificate(getCa2())); 542 install(getCa2(), getAliasUserCa2()); 543 assertTrue(store.isUserAddedCertificate(getCa1())); 544 assertTrue(store.isUserAddedCertificate(getCa2())); 545 store.deleteCertificateEntry(getAliasUserCa1()); 546 assertFalse(store.isUserAddedCertificate(getCa1())); 547 assertTrue(store.isUserAddedCertificate(getCa2())); 548 store.deleteCertificateEntry(getAliasUserCa2()); 549 assertFalse(store.isUserAddedCertificate(getCa1())); 550 assertFalse(store.isUserAddedCertificate(getCa2())); 551 } 552 553 private void assertRootCa(X509Certificate x, String alias) { 554 assertIntermediateCa(x, alias); 555 assertEquals(x, store.findIssuer(x)); 556 } 557 558 private void assertTrusted(X509Certificate x, String alias) { 559 assertEquals(x, store.getCertificate(alias)); 560 assertEquals(file(alias).lastModified(), store.getCreationDate(alias).getTime()); 561 assertTrue(store.containsAlias(alias)); 562 assertTrue(store.isTrustAnchor(x)); 563 } 564 565 private void assertIntermediateCa(X509Certificate x, String alias) { 566 assertTrusted(x, alias); 567 assertEquals(alias, store.getCertificateAlias(x)); 568 } 569 570 private void assertMasked(X509Certificate x, String alias) { 571 assertTrusted(x, alias); 572 assertFalse(alias.equals(store.getCertificateAlias(x))); 573 } 574 575 private void assertDeleted(X509Certificate x, String alias) { 576 assertNull(store.getCertificate(alias)); 577 assertFalse(store.containsAlias(alias)); 578 assertNull(store.getCertificateAlias(x)); 579 assertFalse(store.isTrustAnchor(x)); 580 assertEquals(store.allSystemAliases().contains(alias), 581 store.getCertificate(alias, true) != null); 582 } 583 584 private void assertTombstone(String alias) { 585 assertTrue(TrustedCertificateStore.isUser(alias)); 586 File file = file(alias); 587 assertTrue(file.exists()); 588 assertEquals(0, file.length()); 589 } 590 591 private void assertNoTombstone(String alias) { 592 assertTrue(TrustedCertificateStore.isUser(alias)); 593 assertFalse(file(alias).exists()); 594 } 595 596 private void assertAliases(String... aliases) { 597 Set<String> expected = new HashSet<String>(Arrays.asList(aliases)); 598 Set<String> actual = new HashSet<String>(); 599 for (String alias : store.aliases()) { 600 boolean system = TrustedCertificateStore.isSystem(alias); 601 boolean user = TrustedCertificateStore.isUser(alias); 602 if (system || user) { 603 assertEquals(system, store.allSystemAliases().contains(alias)); 604 assertEquals(user, store.userAliases().contains(alias)); 605 actual.add(alias); 606 } else { 607 throw new AssertionError(alias); 608 } 609 } 610 assertEquals(expected, actual); 611 } 612 613 /** 614 * format a certificate alias 615 */ 616 private static String alias(boolean user, X509Certificate x, int index) { 617 String prefix = user ? "user:" : "system:"; 618 619 X500Principal subject = x.getSubjectX500Principal(); 620 int intHash = NativeCrypto.X509_NAME_hash_old(subject); 621 String strHash = IntegralToString.intToHexString(intHash, false, 8); 622 623 return prefix + strHash + '.' + index; 624 } 625 626 /** 627 * Install certificate under specified alias 628 */ 629 private static void install(X509Certificate x, String alias) { 630 try { 631 File file = file(alias); 632 file.getParentFile().mkdirs(); 633 OutputStream out = new FileOutputStream(file); 634 out.write(x.getEncoded()); 635 out.close(); 636 } catch (Exception e) { 637 throw new RuntimeException(e); 638 } 639 } 640 641 /** 642 * Compute file for an alias 643 */ 644 private static File file(String alias) { 645 File dir; 646 if (TrustedCertificateStore.isSystem(alias)) { 647 dir = DIR_SYSTEM; 648 } else if (TrustedCertificateStore.isUser(alias)) { 649 dir = DIR_ADDED; 650 } else { 651 throw new IllegalArgumentException(alias); 652 } 653 654 int index = alias.lastIndexOf(":"); 655 if (index == -1) { 656 throw new IllegalArgumentException(alias); 657 } 658 String filename = alias.substring(index+1); 659 660 return new File(dir, filename); 661 } 662 } 663