1/* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.security.provider; 27 28import java.io.*; 29import java.net.*; 30import java.security.*; 31import java.security.cert.Certificate; 32import java.security.cert.CertificateFactory; 33import java.security.cert.CertificateException; 34import java.util.*; 35 36import sun.security.pkcs.EncryptedPrivateKeyInfo; 37import sun.security.util.PolicyUtil; 38 39/** 40 * This class provides the domain keystore type identified as "DKS". 41 * DKS presents a collection of separate keystores as a single logical keystore. 42 * The collection of keystores is specified in a domain configuration file which 43 * is passed to DKS in a {@link DomainLoadStoreParameter}. 44 * <p> 45 * The following properties are supported: 46 * <dl> 47 * <dt> {@code keystoreType="<type>"} </dt> 48 * <dd> The keystore type. </dd> 49 * <dt> {@code keystoreURI="<url>"} </dt> 50 * <dd> The keystore location. </dd> 51 * <dt> {@code keystoreProviderName="<name>"} </dt> 52 * <dd> The name of the keystore's JCE provider. </dd> 53 * <dt> {@code keystorePasswordEnv="<environment-variable>"} </dt> 54 * <dd> The environment variable that stores a keystore password. 55 * <dt> {@code entryNameSeparator="<separator>"} </dt> 56 * <dd> The separator between a keystore name prefix and an entry name. 57 * When specified, it applies to all the entries in a domain. 58 * Its default value is a space. </dd> 59 * </dl> 60 * 61 * @since 1.8 62 */ 63 64abstract class DomainKeyStore extends KeyStoreSpi { 65 66 // regular DKS 67 public static final class DKS extends DomainKeyStore { 68 String convertAlias(String alias) { 69 return alias.toLowerCase(Locale.ENGLISH); 70 } 71 } 72 73 // DKS property names 74 private static final String ENTRY_NAME_SEPARATOR = "entrynameseparator"; 75 private static final String KEYSTORE_PROVIDER_NAME = "keystoreprovidername"; 76 private static final String KEYSTORE_TYPE = "keystoretype"; 77 private static final String KEYSTORE_URI = "keystoreuri"; 78 private static final String KEYSTORE_PASSWORD_ENV = "keystorepasswordenv"; 79 80 // RegEx meta characters 81 private static final String REGEX_META = ".$|()[{^?*+\\"; 82 83 // Default prefix for keystores loaded-by-stream 84 private static final String DEFAULT_STREAM_PREFIX = "iostream"; 85 private int streamCounter = 1; 86 private String entryNameSeparator = " "; 87 private String entryNameSeparatorRegEx = " "; 88 89 // Default keystore type 90 private static final String DEFAULT_KEYSTORE_TYPE = 91 KeyStore.getDefaultType(); 92 93 // Domain keystores 94 private final Map<String, KeyStore> keystores = new HashMap<>(); 95 96 DomainKeyStore() { 97 } 98 99 // convert an alias to internal form, overridden in subclasses: 100 // lower case for regular DKS 101 abstract String convertAlias(String alias); 102 103 /** 104 * Returns the key associated with the given alias, using the given 105 * password to recover it. 106 * 107 * @param alias the alias name 108 * @param password the password for recovering the key 109 * 110 * @return the requested key, or null if the given alias does not exist 111 * or does not identify a <i>key entry</i>. 112 * 113 * @exception NoSuchAlgorithmException if the algorithm for recovering the 114 * key cannot be found 115 * @exception UnrecoverableKeyException if the key cannot be recovered 116 * (e.g., the given password is wrong). 117 */ 118 public Key engineGetKey(String alias, char[] password) 119 throws NoSuchAlgorithmException, UnrecoverableKeyException 120 { 121 AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair = 122 getKeystoresForReading(alias); 123 Key key = null; 124 125 try { 126 String entryAlias = pair.getKey(); 127 for (KeyStore keystore : pair.getValue()) { 128 key = keystore.getKey(entryAlias, password); 129 if (key != null) { 130 break; 131 } 132 } 133 } catch (KeyStoreException e) { 134 throw new IllegalStateException(e); 135 } 136 137 return key; 138 } 139 140 /** 141 * Returns the certificate chain associated with the given alias. 142 * 143 * @param alias the alias name 144 * 145 * @return the certificate chain (ordered with the user's certificate first 146 * and the root certificate authority last), or null if the given alias 147 * does not exist or does not contain a certificate chain (i.e., the given 148 * alias identifies either a <i>trusted certificate entry</i> or a 149 * <i>key entry</i> without a certificate chain). 150 */ 151 public Certificate[] engineGetCertificateChain(String alias) { 152 153 AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair = 154 getKeystoresForReading(alias); 155 Certificate[] chain = null; 156 157 try { 158 String entryAlias = pair.getKey(); 159 for (KeyStore keystore : pair.getValue()) { 160 chain = keystore.getCertificateChain(entryAlias); 161 if (chain != null) { 162 break; 163 } 164 } 165 } catch (KeyStoreException e) { 166 throw new IllegalStateException(e); 167 } 168 169 return chain; 170 } 171 172 /** 173 * Returns the certificate associated with the given alias. 174 * 175 * <p>If the given alias name identifies a 176 * <i>trusted certificate entry</i>, the certificate associated with that 177 * entry is returned. If the given alias name identifies a 178 * <i>key entry</i>, the first element of the certificate chain of that 179 * entry is returned, or null if that entry does not have a certificate 180 * chain. 181 * 182 * @param alias the alias name 183 * 184 * @return the certificate, or null if the given alias does not exist or 185 * does not contain a certificate. 186 */ 187 public Certificate engineGetCertificate(String alias) { 188 189 AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair = 190 getKeystoresForReading(alias); 191 Certificate cert = null; 192 193 try { 194 String entryAlias = pair.getKey(); 195 for (KeyStore keystore : pair.getValue()) { 196 cert = keystore.getCertificate(entryAlias); 197 if (cert != null) { 198 break; 199 } 200 } 201 } catch (KeyStoreException e) { 202 throw new IllegalStateException(e); 203 } 204 205 return cert; 206 } 207 208 /** 209 * Returns the creation date of the entry identified by the given alias. 210 * 211 * @param alias the alias name 212 * 213 * @return the creation date of this entry, or null if the given alias does 214 * not exist 215 */ 216 public Date engineGetCreationDate(String alias) { 217 218 AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair = 219 getKeystoresForReading(alias); 220 Date date = null; 221 222 try { 223 String entryAlias = pair.getKey(); 224 for (KeyStore keystore : pair.getValue()) { 225 date = keystore.getCreationDate(entryAlias); 226 if (date != null) { 227 break; 228 } 229 } 230 } catch (KeyStoreException e) { 231 throw new IllegalStateException(e); 232 } 233 234 return date; 235 } 236 237 /** 238 * Assigns the given private key to the given alias, protecting 239 * it with the given password as defined in PKCS8. 240 * 241 * <p>The given java.security.PrivateKey <code>key</code> must 242 * be accompanied by a certificate chain certifying the 243 * corresponding public key. 244 * 245 * <p>If the given alias already exists, the keystore information 246 * associated with it is overridden by the given key and certificate 247 * chain. 248 * 249 * @param alias the alias name 250 * @param key the private key to be associated with the alias 251 * @param password the password to protect the key 252 * @param chain the certificate chain for the corresponding public 253 * key (only required if the given key is of type 254 * <code>java.security.PrivateKey</code>). 255 * 256 * @exception KeyStoreException if the given key is not a private key, 257 * cannot be protected, or this operation fails for some other reason 258 */ 259 public void engineSetKeyEntry(String alias, Key key, char[] password, 260 Certificate[] chain) 261 throws KeyStoreException 262 { 263 AbstractMap.SimpleEntry<String, 264 AbstractMap.SimpleEntry<String, KeyStore>> pair = 265 getKeystoreForWriting(alias); 266 267 if (pair == null) { 268 throw new KeyStoreException("Error setting key entry for '" + 269 alias + "'"); 270 } 271 String entryAlias = pair.getKey(); 272 Map.Entry<String, KeyStore> keystore = pair.getValue(); 273 keystore.getValue().setKeyEntry(entryAlias, key, password, chain); 274 } 275 276 /** 277 * Assigns the given key (that has already been protected) to the given 278 * alias. 279 * 280 * <p>If the protected key is of type 281 * <code>java.security.PrivateKey</code>, it must be accompanied by a 282 * certificate chain certifying the corresponding public key. If the 283 * underlying keystore implementation is of type <code>jks</code>, 284 * <code>key</code> must be encoded as an 285 * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard. 286 * 287 * <p>If the given alias already exists, the keystore information 288 * associated with it is overridden by the given key (and possibly 289 * certificate chain). 290 * 291 * @param alias the alias name 292 * @param key the key (in protected format) to be associated with the alias 293 * @param chain the certificate chain for the corresponding public 294 * key (only useful if the protected key is of type 295 * <code>java.security.PrivateKey</code>). 296 * 297 * @exception KeyStoreException if this operation fails. 298 */ 299 public void engineSetKeyEntry(String alias, byte[] key, 300 Certificate[] chain) 301 throws KeyStoreException 302 { 303 AbstractMap.SimpleEntry<String, 304 AbstractMap.SimpleEntry<String, KeyStore>> pair = 305 getKeystoreForWriting(alias); 306 307 if (pair == null) { 308 throw new KeyStoreException( 309 "Error setting protected key entry for '" + alias + "'"); 310 } 311 String entryAlias = pair.getKey(); 312 Map.Entry<String, KeyStore> keystore = pair.getValue(); 313 keystore.getValue().setKeyEntry(entryAlias, key, chain); 314 } 315 316 /** 317 * Assigns the given certificate to the given alias. 318 * 319 * <p>If the given alias already exists in this keystore and identifies a 320 * <i>trusted certificate entry</i>, the certificate associated with it is 321 * overridden by the given certificate. 322 * 323 * @param alias the alias name 324 * @param cert the certificate 325 * 326 * @exception KeyStoreException if the given alias already exists and does 327 * not identify a <i>trusted certificate entry</i>, or this operation 328 * fails for some other reason. 329 */ 330 public void engineSetCertificateEntry(String alias, Certificate cert) 331 throws KeyStoreException 332 { 333 AbstractMap.SimpleEntry<String, 334 AbstractMap.SimpleEntry<String, KeyStore>> pair = 335 getKeystoreForWriting(alias); 336 337 if (pair == null) { 338 throw new KeyStoreException("Error setting certificate entry for '" 339 + alias + "'"); 340 } 341 String entryAlias = pair.getKey(); 342 Map.Entry<String, KeyStore> keystore = pair.getValue(); 343 keystore.getValue().setCertificateEntry(entryAlias, cert); 344 } 345 346 /** 347 * Deletes the entry identified by the given alias from this keystore. 348 * 349 * @param alias the alias name 350 * 351 * @exception KeyStoreException if the entry cannot be removed. 352 */ 353 public void engineDeleteEntry(String alias) throws KeyStoreException 354 { 355 AbstractMap.SimpleEntry<String, 356 AbstractMap.SimpleEntry<String, KeyStore>> pair = 357 getKeystoreForWriting(alias); 358 359 if (pair == null) { 360 throw new KeyStoreException("Error deleting entry for '" + alias + 361 "'"); 362 } 363 String entryAlias = pair.getKey(); 364 Map.Entry<String, KeyStore> keystore = pair.getValue(); 365 keystore.getValue().deleteEntry(entryAlias); 366 } 367 368 /** 369 * Lists all the alias names of this keystore. 370 * 371 * @return enumeration of the alias names 372 */ 373 public Enumeration<String> engineAliases() { 374 final Iterator<Map.Entry<String, KeyStore>> iterator = 375 keystores.entrySet().iterator(); 376 377 return new Enumeration<String>() { 378 private int index = 0; 379 private Map.Entry<String, KeyStore> keystoresEntry = null; 380 private String prefix = null; 381 private Enumeration<String> aliases = null; 382 383 public boolean hasMoreElements() { 384 try { 385 if (aliases == null) { 386 if (iterator.hasNext()) { 387 keystoresEntry = iterator.next(); 388 prefix = keystoresEntry.getKey() + 389 entryNameSeparator; 390 aliases = keystoresEntry.getValue().aliases(); 391 } else { 392 return false; 393 } 394 } 395 if (aliases.hasMoreElements()) { 396 return true; 397 } else { 398 if (iterator.hasNext()) { 399 keystoresEntry = iterator.next(); 400 prefix = keystoresEntry.getKey() + 401 entryNameSeparator; 402 aliases = keystoresEntry.getValue().aliases(); 403 } else { 404 return false; 405 } 406 } 407 } catch (KeyStoreException e) { 408 return false; 409 } 410 411 return aliases.hasMoreElements(); 412 } 413 414 public String nextElement() { 415 if (hasMoreElements()) { 416 return prefix + aliases.nextElement(); 417 } 418 throw new NoSuchElementException(); 419 } 420 }; 421 } 422 423 /** 424 * Checks if the given alias exists in this keystore. 425 * 426 * @param alias the alias name 427 * 428 * @return true if the alias exists, false otherwise 429 */ 430 public boolean engineContainsAlias(String alias) { 431 432 AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair = 433 getKeystoresForReading(alias); 434 435 try { 436 String entryAlias = pair.getKey(); 437 for (KeyStore keystore : pair.getValue()) { 438 if (keystore.containsAlias(entryAlias)) { 439 return true; 440 } 441 } 442 } catch (KeyStoreException e) { 443 throw new IllegalStateException(e); 444 } 445 446 return false; 447 } 448 449 /** 450 * Retrieves the number of entries in this keystore. 451 * 452 * @return the number of entries in this keystore 453 */ 454 public int engineSize() { 455 456 int size = 0; 457 try { 458 for (KeyStore keystore : keystores.values()) { 459 size += keystore.size(); 460 } 461 } catch (KeyStoreException e) { 462 throw new IllegalStateException(e); 463 } 464 465 return size; 466 } 467 468 /** 469 * Returns true if the entry identified by the given alias is a 470 * <i>key entry</i>, and false otherwise. 471 * 472 * @return true if the entry identified by the given alias is a 473 * <i>key entry</i>, false otherwise. 474 */ 475 public boolean engineIsKeyEntry(String alias) { 476 477 AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair = 478 getKeystoresForReading(alias); 479 480 try { 481 String entryAlias = pair.getKey(); 482 for (KeyStore keystore : pair.getValue()) { 483 if (keystore.isKeyEntry(entryAlias)) { 484 return true; 485 } 486 } 487 } catch (KeyStoreException e) { 488 throw new IllegalStateException(e); 489 } 490 491 return false; 492 } 493 494 /** 495 * Returns true if the entry identified by the given alias is a 496 * <i>trusted certificate entry</i>, and false otherwise. 497 * 498 * @return true if the entry identified by the given alias is a 499 * <i>trusted certificate entry</i>, false otherwise. 500 */ 501 public boolean engineIsCertificateEntry(String alias) { 502 503 AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair = 504 getKeystoresForReading(alias); 505 506 try { 507 String entryAlias = pair.getKey(); 508 for (KeyStore keystore : pair.getValue()) { 509 if (keystore.isCertificateEntry(entryAlias)) { 510 return true; 511 } 512 } 513 } catch (KeyStoreException e) { 514 throw new IllegalStateException(e); 515 } 516 517 return false; 518 } 519 520 /* 521 * Returns a keystore entry alias and a list of target keystores. 522 * When the supplied alias prefix identifies a keystore then that single 523 * keystore is returned. When no alias prefix is supplied then all the 524 * keystores are returned. 525 */ 526 private AbstractMap.SimpleEntry<String, Collection<KeyStore>> 527 getKeystoresForReading(String alias) { 528 529 String[] splits = alias.split(this.entryNameSeparatorRegEx, 2); 530 if (splits.length == 2) { // prefixed alias 531 KeyStore keystore = keystores.get(splits[0]); 532 if (keystore != null) { 533 return new AbstractMap.SimpleEntry<>(splits[1], 534 (Collection<KeyStore>) Collections.singleton(keystore)); 535 } 536 } else if (splits.length == 1) { // unprefixed alias 537 // Check all keystores for the first occurrence of the alias 538 return new AbstractMap.SimpleEntry<>(alias, keystores.values()); 539 } 540 return new AbstractMap.SimpleEntry<>("", 541 (Collection<KeyStore>) Collections.<KeyStore>emptyList()); 542 } 543 544 /* 545 * Returns a keystore entry alias and a single target keystore. 546 * An alias prefix must be supplied. 547 */ 548 private 549 AbstractMap.SimpleEntry<String, AbstractMap.SimpleEntry<String, KeyStore>> 550 getKeystoreForWriting(String alias) { 551 552 String[] splits = alias.split(this.entryNameSeparator, 2); 553 if (splits.length == 2) { // prefixed alias 554 KeyStore keystore = keystores.get(splits[0]); 555 if (keystore != null) { 556 return new AbstractMap.SimpleEntry<>(splits[1], 557 new AbstractMap.SimpleEntry<>(splits[0], keystore)); 558 } 559 } 560 return null; 561 } 562 563 /** 564 * Returns the (alias) name of the first keystore entry whose certificate 565 * matches the given certificate. 566 * 567 * <p>This method attempts to match the given certificate with each 568 * keystore entry. If the entry being considered 569 * is a <i>trusted certificate entry</i>, the given certificate is 570 * compared to that entry's certificate. If the entry being considered is 571 * a <i>key entry</i>, the given certificate is compared to the first 572 * element of that entry's certificate chain (if a chain exists). 573 * 574 * @param cert the certificate to match with. 575 * 576 * @return the (alias) name of the first entry with matching certificate, 577 * or null if no such entry exists in this keystore. 578 */ 579 public String engineGetCertificateAlias(Certificate cert) { 580 581 try { 582 583 String alias = null; 584 for (KeyStore keystore : keystores.values()) { 585 if ((alias = keystore.getCertificateAlias(cert)) != null) { 586 break; 587 } 588 } 589 return alias; 590 591 } catch (KeyStoreException e) { 592 throw new IllegalStateException(e); 593 } 594 } 595 596 /** 597 * Stores this keystore to the given output stream, and protects its 598 * integrity with the given password. 599 * 600 * @param stream the output stream to which this keystore is written. 601 * @param password the password to generate the keystore integrity check 602 * 603 * @exception IOException if there was an I/O problem with data 604 * @exception NoSuchAlgorithmException if the appropriate data integrity 605 * algorithm could not be found 606 * @exception CertificateException if any of the certificates included in 607 * the keystore data could not be stored 608 */ 609 public void engineStore(OutputStream stream, char[] password) 610 throws IOException, NoSuchAlgorithmException, CertificateException 611 { 612 // Support storing to a stream only when a single keystore has been 613 // configured 614 try { 615 if (keystores.size() == 1) { 616 keystores.values().iterator().next().store(stream, password); 617 return; 618 } 619 } catch (KeyStoreException e) { 620 throw new IllegalStateException(e); 621 } 622 623 throw new UnsupportedOperationException( 624 "This keystore must be stored using a DomainLoadStoreParameter"); 625 } 626 627 @Override 628 public void engineStore(KeyStore.LoadStoreParameter param) 629 throws IOException, NoSuchAlgorithmException, CertificateException 630 { 631 if (param instanceof DomainLoadStoreParameter) { 632 DomainLoadStoreParameter domainParameter = 633 (DomainLoadStoreParameter) param; 634 List<KeyStoreBuilderComponents> builders = getBuilders( 635 domainParameter.getConfiguration(), 636 domainParameter.getProtectionParams()); 637 638 for (KeyStoreBuilderComponents builder : builders) { 639 640 try { 641 642 KeyStore.ProtectionParameter pp = builder.protection; 643 if (!(pp instanceof KeyStore.PasswordProtection)) { 644 throw new KeyStoreException( 645 new IllegalArgumentException("ProtectionParameter" + 646 " must be a KeyStore.PasswordProtection")); 647 } 648 char[] password = 649 ((KeyStore.PasswordProtection) builder.protection) 650 .getPassword(); 651 652 // Store the keystores 653 KeyStore keystore = keystores.get(builder.name); 654 655 try (FileOutputStream stream = 656 new FileOutputStream(builder.file)) { 657 658 keystore.store(stream, password); 659 } 660 } catch (KeyStoreException e) { 661 throw new IOException(e); 662 } 663 } 664 } else { 665 throw new UnsupportedOperationException( 666 "This keystore must be stored using a " + 667 "DomainLoadStoreParameter"); 668 } 669 } 670 671 /** 672 * Loads the keystore from the given input stream. 673 * 674 * <p>If a password is given, it is used to check the integrity of the 675 * keystore data. Otherwise, the integrity of the keystore is not checked. 676 * 677 * @param stream the input stream from which the keystore is loaded 678 * @param password the (optional) password used to check the integrity of 679 * the keystore. 680 * 681 * @exception IOException if there is an I/O or format problem with the 682 * keystore data 683 * @exception NoSuchAlgorithmException if the algorithm used to check 684 * the integrity of the keystore cannot be found 685 * @exception CertificateException if any of the certificates in the 686 * keystore could not be loaded 687 */ 688 public void engineLoad(InputStream stream, char[] password) 689 throws IOException, NoSuchAlgorithmException, CertificateException 690 { 691 // Support loading from a stream only for a JKS or default type keystore 692 try { 693 KeyStore keystore = null; 694 695 try { 696 keystore = KeyStore.getInstance("JKS"); 697 keystore.load(stream, password); 698 699 } catch (Exception e) { 700 // Retry 701 if (!"JKS".equalsIgnoreCase(DEFAULT_KEYSTORE_TYPE)) { 702 keystore = KeyStore.getInstance(DEFAULT_KEYSTORE_TYPE); 703 keystore.load(stream, password); 704 } else { 705 throw e; 706 } 707 } 708 String keystoreName = DEFAULT_STREAM_PREFIX + streamCounter++; 709 keystores.put(keystoreName, keystore); 710 711 } catch (Exception e) { 712 throw new UnsupportedOperationException( 713 "This keystore must be loaded using a " + 714 "DomainLoadStoreParameter"); 715 } 716 } 717 718 @Override 719 public void engineLoad(KeyStore.LoadStoreParameter param) 720 throws IOException, NoSuchAlgorithmException, CertificateException 721 { 722 if (param instanceof DomainLoadStoreParameter) { 723 DomainLoadStoreParameter domainParameter = 724 (DomainLoadStoreParameter) param; 725 List<KeyStoreBuilderComponents> builders = getBuilders( 726 domainParameter.getConfiguration(), 727 domainParameter.getProtectionParams()); 728 729 for (KeyStoreBuilderComponents builder : builders) { 730 731 try { 732 // Load the keystores (file-based and non-file-based) 733 if (builder.file != null) { 734 keystores.put(builder.name, 735 KeyStore.Builder.newInstance(builder.type, 736 builder.provider, builder.file, 737 builder.protection) 738 .getKeyStore()); 739 } else { 740 keystores.put(builder.name, 741 KeyStore.Builder.newInstance(builder.type, 742 builder.provider, builder.protection) 743 .getKeyStore()); 744 } 745 } catch (KeyStoreException e) { 746 throw new IOException(e); 747 } 748 } 749 } else { 750 throw new UnsupportedOperationException( 751 "This keystore must be loaded using a " + 752 "DomainLoadStoreParameter"); 753 } 754 } 755 756 /* 757 * Parse a keystore domain configuration file and associated collection 758 * of keystore passwords to create a collection of KeyStore.Builder. 759 */ 760 private List<KeyStoreBuilderComponents> getBuilders(URI configuration, 761 Map<String, KeyStore.ProtectionParameter> passwords) 762 throws IOException { 763 764 PolicyParser parser = new PolicyParser(true); // expand properties 765 Collection<PolicyParser.DomainEntry> domains = null; 766 List<KeyStoreBuilderComponents> builders = new ArrayList<>(); 767 String uriDomain = configuration.getFragment(); 768 769 try (InputStreamReader configurationReader = 770 new InputStreamReader( 771 PolicyUtil.getInputStream(configuration.toURL()), "UTF-8")) { 772 parser.read(configurationReader); 773 domains = parser.getDomainEntries(); 774 775 } catch (MalformedURLException mue) { 776 throw new IOException(mue); 777 778 } catch (PolicyParser.ParsingException pe) { 779 throw new IOException(pe); 780 } 781 782 for (PolicyParser.DomainEntry domain : domains) { 783 Map<String, String> domainProperties = domain.getProperties(); 784 785 if (uriDomain != null && 786 (!uriDomain.equalsIgnoreCase(domain.getName()))) { 787 continue; // skip this domain 788 } 789 790 if (domainProperties.containsKey(ENTRY_NAME_SEPARATOR)) { 791 this.entryNameSeparator = 792 domainProperties.get(ENTRY_NAME_SEPARATOR); 793 // escape any regex meta characters 794 char ch = 0; 795 StringBuilder s = new StringBuilder(); 796 for (int i = 0; i < this.entryNameSeparator.length(); i++) { 797 ch = this.entryNameSeparator.charAt(i); 798 if (REGEX_META.indexOf(ch) != -1) { 799 s.append('\\'); 800 } 801 s.append(ch); 802 } 803 this.entryNameSeparatorRegEx = s.toString(); 804 } 805 806 Collection<PolicyParser.KeyStoreEntry> keystores = 807 domain.getEntries(); 808 for (PolicyParser.KeyStoreEntry keystore : keystores) { 809 String keystoreName = keystore.getName(); 810 Map<String, String> properties = 811 new HashMap<>(domainProperties); 812 properties.putAll(keystore.getProperties()); 813 814 String keystoreType = DEFAULT_KEYSTORE_TYPE; 815 if (properties.containsKey(KEYSTORE_TYPE)) { 816 keystoreType = properties.get(KEYSTORE_TYPE); 817 } 818 819 Provider keystoreProvider = null; 820 if (properties.containsKey(KEYSTORE_PROVIDER_NAME)) { 821 String keystoreProviderName = 822 properties.get(KEYSTORE_PROVIDER_NAME); 823 keystoreProvider = 824 Security.getProvider(keystoreProviderName); 825 if (keystoreProvider == null) { 826 throw new IOException("Error locating JCE provider: " + 827 keystoreProviderName); 828 } 829 } 830 831 File keystoreFile = null; 832 if (properties.containsKey(KEYSTORE_URI)) { 833 String uri = properties.get(KEYSTORE_URI); 834 835 try { 836 if (uri.startsWith("file://")) { 837 keystoreFile = new File(new URI(uri)); 838 } else { 839 keystoreFile = new File(uri); 840 } 841 842 } catch (URISyntaxException | IllegalArgumentException e) { 843 throw new IOException( 844 "Error processing keystore property: " + 845 "keystoreURI=\"" + uri + "\"", e); 846 } 847 } 848 849 KeyStore.ProtectionParameter keystoreProtection = null; 850 if (passwords.containsKey(keystoreName)) { 851 keystoreProtection = passwords.get(keystoreName); 852 853 } else if (properties.containsKey(KEYSTORE_PASSWORD_ENV)) { 854 String env = properties.get(KEYSTORE_PASSWORD_ENV); 855 String pwd = System.getenv(env); 856 if (pwd != null) { 857 keystoreProtection = 858 new KeyStore.PasswordProtection(pwd.toCharArray()); 859 } else { 860 throw new IOException( 861 "Error processing keystore property: " + 862 "keystorePasswordEnv=\"" + env + "\""); 863 } 864 } else { 865 keystoreProtection = new KeyStore.PasswordProtection(null); 866 } 867 868 builders.add(new KeyStoreBuilderComponents(keystoreName, 869 keystoreType, keystoreProvider, keystoreFile, 870 keystoreProtection)); 871 } 872 break; // skip other domains 873 } 874 if (builders.isEmpty()) { 875 throw new IOException("Error locating domain configuration data " + 876 "for: " + configuration); 877 } 878 879 return builders; 880 } 881 882/* 883 * Utility class that holds the components used to construct a KeyStore.Builder 884 */ 885class KeyStoreBuilderComponents { 886 String name; 887 String type; 888 Provider provider; 889 File file; 890 KeyStore.ProtectionParameter protection; 891 892 KeyStoreBuilderComponents(String name, String type, Provider provider, 893 File file, KeyStore.ProtectionParameter protection) { 894 this.name = name; 895 this.type = type; 896 this.provider = provider; 897 this.file = file; 898 this.protection = protection; 899 } 900} 901} 902