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