KeyStore.java revision 6cdb6b7e6939270ccd21790ec95e42197cefc0c3
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.security; 19 20import java.io.File; 21import java.io.FileInputStream; 22import java.io.IOException; 23import java.io.InputStream; 24import java.io.OutputStream; 25import java.security.cert.Certificate; 26import java.security.cert.CertificateException; 27import java.security.cert.X509Certificate; 28import java.util.Arrays; 29import java.util.Date; 30import java.util.Enumeration; 31import javax.crypto.SecretKey; 32import javax.security.auth.DestroyFailedException; 33import javax.security.auth.Destroyable; 34import javax.security.auth.callback.CallbackHandler; 35import org.apache.harmony.security.fortress.Engine; 36 37/** 38 * {@code KeyStore} is responsible for maintaining cryptographic keys and their 39 * owners. 40 * <p> 41 * The type of the system key store can be changed by setting the {@code 42 * 'keystore.type'} property in the file named {@code 43 * JAVA_HOME/lib/security/java.security}. 44 * 45 * @see Certificate 46 * @see PrivateKey 47 */ 48public class KeyStore { 49 50 // Store KeyStore SERVICE name 51 private static final String SERVICE = "KeyStore"; 52 53 // Used to access common engine functionality 54 private static final Engine ENGINE = new Engine(SERVICE); 55 56 // Store KeyStore property name 57 private static final String PROPERTYNAME = "keystore.type"; 58 59 // Store default KeyStore type 60 private static final String DEFAULT_KEYSTORE_TYPE = "jks"; 61 62 // Store KeyStore state (initialized or not) 63 private boolean isInit; 64 65 // Store used KeyStoreSpi 66 private final KeyStoreSpi implSpi; 67 68 // Store used provider 69 private final Provider provider; 70 71 // Store used type 72 private final String type; 73 74 /** 75 * Constructs a new instance of {@code KeyStore} with the given arguments. 76 * 77 * @param keyStoreSpi 78 * the concrete key store. 79 * @param provider 80 * the provider. 81 * @param type 82 * the type of the {@code KeyStore} to be constructed. 83 */ 84 protected KeyStore(KeyStoreSpi keyStoreSpi, Provider provider, String type) { 85 this.type = type; 86 this.provider = provider; 87 this.implSpi = keyStoreSpi; 88 isInit = false; 89 } 90 91 /** 92 * Throws the standard "keystore not initialized" exception. 93 */ 94 private static void throwNotInitialized() throws KeyStoreException { 95 throw new KeyStoreException("KeyStore was not initialized"); 96 } 97 98 /** 99 * Returns a new instance of {@code KeyStore} with the specified type. 100 * 101 * @param type 102 * the type of the returned {@code KeyStore}. 103 * @return a new instance of {@code KeyStore} with the specified type. 104 * @throws KeyStoreException 105 * if an error occurred during the creation of the new {@code 106 * KeyStore}. 107 * @throws NullPointerException if {@code type == null} 108 * @see #getDefaultType 109 */ 110 public static KeyStore getInstance(String type) throws KeyStoreException { 111 if (type == null) { 112 throw new NullPointerException(); 113 } 114 try { 115 Engine.SpiAndProvider sap = ENGINE.getInstance(type, null); 116 return new KeyStore((KeyStoreSpi) sap.spi, sap.provider, type); 117 } catch (NoSuchAlgorithmException e) { 118 throw new KeyStoreException(e.getMessage()); 119 } 120 } 121 122 /** 123 * Returns a new instance of {@code KeyStore} from the specified provider 124 * with the given type. 125 * 126 * @param type 127 * the type of the returned {@code KeyStore}. 128 * @param provider 129 * name of the provider of the {@code KeyStore}. 130 * @return a new instance of {@code KeyStore} from the specified provider 131 * with the given type. 132 * @throws KeyStoreException 133 * if an error occurred during the creation of the new {@code 134 * KeyStore}. 135 * @throws NoSuchProviderException 136 * if the specified provider is not available. 137 * @throws IllegalArgumentException if {@code provider == null || provider.isEmpty()} 138 * @throws NullPointerException 139 * if {@code type} is {@code null} (instead of 140 * NoSuchAlgorithmException) as in 1.4 release 141 * @see #getDefaultType 142 */ 143 public static KeyStore getInstance(String type, String provider) 144 throws KeyStoreException, NoSuchProviderException { 145 if (provider == null || provider.isEmpty()) { 146 throw new IllegalArgumentException(); 147 } 148 Provider impProvider = Security.getProvider(provider); 149 if (impProvider == null) { 150 throw new NoSuchProviderException(provider); 151 } 152 try { 153 return getInstance(type, impProvider); 154 } catch (Exception e) { 155 throw new KeyStoreException(e.getMessage(), e); 156 } 157 } 158 159 /** 160 * Returns a new instance of {@code KeyStore} from the specified provider 161 * with the given type. 162 * 163 * @param type 164 * the type of the returned {@code KeyStore}. 165 * @param provider 166 * the provider of the {@code KeyStore}. 167 * @return a new instance of {@code KeyStore} from the specified provider 168 * with the given type. 169 * @throws KeyStoreException 170 * if an error occurred during the creation of the new {@code 171 * KeyStore}. 172 * @throws IllegalArgumentException 173 * if {@code provider} is {@code null} or the empty string. 174 * @throws NullPointerException if {@code type == null} (instead of 175 * NoSuchAlgorithmException) as in 1.4 release 176 * @see #getDefaultType 177 */ 178 public static KeyStore getInstance(String type, Provider provider) 179 throws KeyStoreException { 180 // check parameters 181 if (provider == null) { 182 throw new IllegalArgumentException(); 183 } 184 if (type == null) { 185 throw new NullPointerException(); 186 } 187 // return KeyStore instance 188 try { 189 Object spi = ENGINE.getInstance(type, provider, null); 190 return new KeyStore((KeyStoreSpi) spi, provider, type); 191 } catch (Exception e) { 192 // override exception 193 throw new KeyStoreException(e.getMessage()); 194 } 195 } 196 197 /** 198 * Returns the default type for {@code KeyStore} instances. 199 * <p> 200 * The default is specified in the {@code 'keystore.type'} property in the 201 * file named {@code JAVA_HOME/lib/security/java.security}. If this property 202 * is not set, {@code "jks"} will be used. 203 * 204 * @return the default type for {@code KeyStore} instances 205 */ 206 public static final String getDefaultType() { 207 String dt = AccessController.doPrivileged( 208 new PrivilegedAction<String>() { 209 public String run() { 210 return Security.getProperty(PROPERTYNAME); 211 } 212 } 213 ); 214 return (dt == null ? DEFAULT_KEYSTORE_TYPE : dt); 215 } 216 217 /** 218 * Returns the provider associated with this {@code KeyStore}. 219 * 220 * @return the provider associated with this {@code KeyStore}. 221 */ 222 public final Provider getProvider() { 223 return provider; 224 } 225 226 /** 227 * Returns the type of this {@code KeyStore}. 228 * 229 * @return the type of this {@code KeyStore}. 230 */ 231 public final String getType() { 232 return type; 233 } 234 235 /** 236 * Returns the key with the given alias, using the password to recover the 237 * key from the store. 238 * 239 * @param alias 240 * the alias for the entry. 241 * @param password 242 * the password used to recover the key. 243 * @return the key with the specified alias, or {@code null} if the 244 * specified alias is not bound to an entry. 245 * @throws KeyStoreException 246 * if this {@code KeyStore} is not initialized. 247 * @throws NoSuchAlgorithmException 248 * if the algorithm for recovering the key is not available. 249 * @throws UnrecoverableKeyException 250 * if the key can not be recovered. 251 */ 252 public final Key getKey(String alias, char[] password) 253 throws KeyStoreException, NoSuchAlgorithmException, 254 UnrecoverableKeyException { 255 if (!isInit) { 256 // BEGIN android-changed 257 throwNotInitialized(); 258 // END android-changed 259 } 260 return implSpi.engineGetKey(alias, password); 261 } 262 263 /** 264 * Returns the certificate chain for the entry with the given alias. 265 * 266 * @param alias 267 * the alias for the entry. 268 * @return the certificate chain for the entry with the given alias, or 269 * {@code null} if the specified alias is not bound to an entry. 270 * @throws KeyStoreException 271 * if this {@code KeyStore} is not initialized. 272 */ 273 public final Certificate[] getCertificateChain(String alias) 274 throws KeyStoreException { 275 if (!isInit) { 276 // BEGIN android-changed 277 throwNotInitialized(); 278 // END android-changed 279 } 280 return implSpi.engineGetCertificateChain(alias); 281 } 282 283 /** 284 * Returns the trusted certificate for the entry with the given alias. 285 * 286 * @param alias 287 * the alias for the entry. 288 * @return the trusted certificate for the entry with the given alias, or 289 * {@code null} if the specified alias is not bound to an entry. 290 * @throws KeyStoreException 291 * if this {@code KeyStore} is not initialized. 292 */ 293 public final Certificate getCertificate(String alias) 294 throws KeyStoreException { 295 if (!isInit) { 296 // BEGIN android-changed 297 throwNotInitialized(); 298 // END android-changed 299 } 300 return implSpi.engineGetCertificate(alias); 301 } 302 303 /** 304 * Returns the creation date of the entry with the given alias. 305 * 306 * @param alias 307 * the alias for the entry. 308 * @return the creation date, or {@code null} if the specified alias is not 309 * bound to an entry. 310 * @throws KeyStoreException 311 * if this {@code KeyStore} is not initialized. 312 */ 313 public final Date getCreationDate(String alias) throws KeyStoreException { 314 if (!isInit) { 315 // BEGIN android-changed 316 throwNotInitialized(); 317 // END android-changed 318 } 319 return implSpi.engineGetCreationDate(alias); 320 } 321 322 /** 323 * Associates the given alias with the key, password and certificate chain. 324 * <p> 325 * If the specified alias already exists, it will be reassigned. 326 * 327 * @param alias 328 * the alias for the key. 329 * @param key 330 * the key. 331 * @param password 332 * the password. 333 * @param chain 334 * the certificate chain. 335 * @throws KeyStoreException 336 * if this {@code KeyStore} is not initialized. 337 * @throws IllegalArgumentException 338 * if {@code key} is a {@code PrivateKey} and {@code chain} does 339 * not contain any certificates. 340 * @throws NullPointerException 341 * if {@code alias} is {@code null}. 342 */ 343 public final void setKeyEntry(String alias, Key key, char[] password, 344 Certificate[] chain) throws KeyStoreException { 345 if (!isInit) { 346 // BEGIN android-changed 347 throwNotInitialized(); 348 // END android-changed 349 } 350 351 // Certificate chain is required for PrivateKey 352 if (null != key && key instanceof PrivateKey && (chain == null || chain.length == 0)) { 353 throw new IllegalArgumentException("Certificate chain is not defined for Private key"); 354 } 355 implSpi.engineSetKeyEntry(alias, key, password, chain); 356 } 357 358 /** 359 * Associates the given alias with a key and a certificate chain. 360 * <p> 361 * If the specified alias already exists, it will be reassigned. 362 * <p> 363 * If this {@code KeyStore} is of type {@code "jks"}, {@code key} must be 364 * encoded conform to the PKS#8 standard as an 365 * {@link javax.crypto.EncryptedPrivateKeyInfo}. 366 * 367 * @param alias 368 * the alias for the key. 369 * @param key 370 * the key in an encoded format. 371 * @param chain 372 * the certificate chain. 373 * @throws KeyStoreException 374 * if this {@code KeyStore} is not initialized or if {@code key} 375 * is null. 376 * @throws IllegalArgumentException 377 * if {@code key} is a {@code PrivateKey} and {@code chain} 378 * does. 379 * @throws NullPointerException 380 * if {@code alias} is {@code null}. 381 */ 382 public final void setKeyEntry(String alias, byte[] key, Certificate[] chain) 383 throws KeyStoreException { 384 if (!isInit) { 385 // BEGIN android-changed 386 throwNotInitialized(); 387 // END android-changed 388 } 389 implSpi.engineSetKeyEntry(alias, key, chain); 390 } 391 392 /** 393 * Associates the given alias with a certificate. 394 * <p> 395 * If the specified alias already exists, it will be reassigned. 396 * 397 * @param alias 398 * the alias for the certificate. 399 * @param cert 400 * the certificate. 401 * @throws KeyStoreException 402 * if this {@code KeyStore} is not initialized, or an existing 403 * alias is not associated to an entry containing a trusted 404 * certificate, or this method fails for any other reason. 405 * @throws NullPointerException 406 * if {@code alias} is {@code null}. 407 */ 408 public final void setCertificateEntry(String alias, Certificate cert) 409 throws KeyStoreException { 410 if (!isInit) { 411 // BEGIN android-changed 412 throwNotInitialized(); 413 // END android-changed 414 } 415 implSpi.engineSetCertificateEntry(alias, cert); 416 } 417 418 /** 419 * Deletes the entry identified with the given alias from this {@code 420 * KeyStore}. 421 * 422 * @param alias 423 * the alias for the entry. 424 * @throws KeyStoreException 425 * if this {@code KeyStore} is not initialized, or if the entry 426 * can not be deleted. 427 */ 428 public final void deleteEntry(String alias) throws KeyStoreException { 429 if (!isInit) { 430 // BEGIN android-changed 431 throwNotInitialized(); 432 // END android-changed 433 } 434 implSpi.engineDeleteEntry(alias); 435 } 436 437 /** 438 * Returns an {@code Enumeration} over all alias names stored in this 439 * {@code KeyStore}. 440 * 441 * @return an {@code Enumeration} over all alias names stored in this 442 * {@code KeyStore}. 443 * @throws KeyStoreException 444 * if this {@code KeyStore} is not initialized. 445 */ 446 public final Enumeration<String> aliases() throws KeyStoreException { 447 if (!isInit) { 448 // BEGIN android-changed 449 throwNotInitialized(); 450 // END android-changed 451 } 452 return implSpi.engineAliases(); 453 } 454 455 /** 456 * Indicates whether the given alias is present in this {@code KeyStore}. 457 * 458 * @param alias 459 * the alias of an entry. 460 * @return {@code true} if the alias exists, {@code false} otherwise. 461 * @throws KeyStoreException 462 * if this {@code KeyStore} is not initialized. 463 */ 464 public final boolean containsAlias(String alias) throws KeyStoreException { 465 if (!isInit) { 466 // BEGIN android-changed 467 throwNotInitialized(); 468 // END android-changed 469 } 470 return implSpi.engineContainsAlias(alias); 471 } 472 473 /** 474 * Returns the number of entries stored in this {@code KeyStore}. 475 * 476 * @return the number of entries stored in this {@code KeyStore}. 477 * @throws KeyStoreException 478 * if this {@code KeyStore} is not initialized. 479 */ 480 public final int size() throws KeyStoreException { 481 if (!isInit) { 482 // BEGIN android-changed 483 throwNotInitialized(); 484 // END android-changed 485 } 486 return implSpi.engineSize(); 487 } 488 489 /** 490 * Indicates whether the specified alias is associated with either a 491 * {@link PrivateKeyEntry} or a {@link SecretKeyEntry}. 492 * 493 * @param alias 494 * the alias of an entry. 495 * @return {@code true} if the given alias is associated with a key entry. 496 * @throws KeyStoreException 497 * if this {@code KeyStore} is not initialized. 498 */ 499 public final boolean isKeyEntry(String alias) throws KeyStoreException { 500 if (!isInit) { 501 // BEGIN android-changed 502 throwNotInitialized(); 503 // END android-changed 504 } 505 return implSpi.engineIsKeyEntry(alias); 506 } 507 508 /** 509 * Indicates whether the specified alias is associated with a 510 * {@link TrustedCertificateEntry}. 511 * 512 * @param alias 513 * the alias of an entry. 514 * @return {@code true} if the given alias is associated with a certificate 515 * entry. 516 * @throws KeyStoreException 517 * if this {@code KeyStore} is not initialized. 518 */ 519 public final boolean isCertificateEntry(String alias) 520 throws KeyStoreException { 521 if (!isInit) { 522 // BEGIN android-changed 523 throwNotInitialized(); 524 // END android-changed 525 } 526 return implSpi.engineIsCertificateEntry(alias); 527 } 528 529 /** 530 * Returns the alias associated with the first entry whose certificate 531 * matches the specified certificate. 532 * 533 * @param cert 534 * the certificate to find the associated entry's alias for. 535 * @return the alias or {@code null} if no entry with the specified 536 * certificate can be found. 537 * @throws KeyStoreException 538 * if this {@code KeyStore} is not initialized. 539 */ 540 public final String getCertificateAlias(Certificate cert) 541 throws KeyStoreException { 542 if (!isInit) { 543 // BEGIN android-changed 544 throwNotInitialized(); 545 // END android-changed 546 } 547 return implSpi.engineGetCertificateAlias(cert); 548 } 549 550 /** 551 * Writes this {@code KeyStore} to the specified {@code OutputStream}. The 552 * data written to the {@code OutputStream} is protected by the specified 553 * password. 554 * 555 * @param stream 556 * the {@code OutputStream} to write the store's data to. 557 * @param password 558 * the password to protect the data. 559 * @throws KeyStoreException 560 * if this {@code KeyStore} is not initialized. 561 * @throws IOException 562 * if a problem occurred while writing to the stream. 563 * @throws NoSuchAlgorithmException 564 * if the required algorithm is not available. 565 * @throws CertificateException 566 * if an exception occurred while storing the certificates of 567 * this {@code KeyStore}. 568 */ 569 public final void store(OutputStream stream, char[] password) 570 throws KeyStoreException, IOException, NoSuchAlgorithmException, 571 CertificateException { 572 if (!isInit) { 573 // BEGIN android-changed 574 throwNotInitialized(); 575 // END android-changed 576 } 577 578 //Just delegate stream and password to implSpi 579 implSpi.engineStore(stream, password); 580 } 581 582 /** 583 * Stores this {@code KeyStore} using the specified {@code 584 * LoadStoreParameter}. 585 * 586 * @param param 587 * the {@code LoadStoreParameter} that specifies how to store 588 * this {@code KeyStore}, maybe {@code null}. 589 * @throws KeyStoreException 590 * if this {@code KeyStore} is not initialized. 591 * @throws IOException 592 * if a problem occurred while writing to the stream. 593 * @throws NoSuchAlgorithmException 594 * if the required algorithm is not available. 595 * @throws CertificateException 596 * if an exception occurred while storing the certificates of 597 * this {@code KeyStore}. 598 * @throws IllegalArgumentException 599 * if the given {@link LoadStoreParameter} is not recognized. 600 */ 601 public final void store(LoadStoreParameter param) throws KeyStoreException, 602 IOException, NoSuchAlgorithmException, CertificateException { 603 if (!isInit) { 604 // BEGIN android-changed 605 throwNotInitialized(); 606 // END android-changed 607 } 608 implSpi.engineStore(param); 609 } 610 611 /** 612 * Initializes this {@code KeyStore} from the provided {@code InputStream}. 613 * Pass {@code null} as the {@code stream} argument to initialize an empty 614 * {@code KeyStore} or to initialize a {@code KeyStore} which does not rely 615 * on an {@code InputStream}. This {@code KeyStore} utilizes the given 616 * password to verify the stored data. 617 * 618 * @param stream 619 * the {@code InputStream} to load this {@code KeyStore}'s data 620 * from or {@code null}. 621 * @param password 622 * the password to verify the stored data, maybe {@code null}. 623 * @throws IOException 624 * if a problem occurred while reading from the stream. 625 * @throws NoSuchAlgorithmException 626 * if the required algorithm is not available. 627 * @throws CertificateException 628 * if an exception occurred while loading the certificates of 629 * this {@code KeyStore}. 630 */ 631 public final void load(InputStream stream, char[] password) 632 throws IOException, NoSuchAlgorithmException, CertificateException { 633 implSpi.engineLoad(stream, password); 634 isInit = true; 635 } 636 637 /** 638 * Loads this {@code KeyStore} using the specified {@code 639 * LoadStoreParameter}. 640 * 641 * @param param 642 * the {@code LoadStoreParameter} that specifies how to load this 643 * {@code KeyStore}, maybe {@code null}. 644 * @throws IOException 645 * if a problem occurred while reading from the stream. 646 * @throws NoSuchAlgorithmException 647 * if the required algorithm is not available. 648 * @throws CertificateException 649 * if an exception occurred while loading the certificates of 650 * this {@code KeyStore}. 651 * @throws IllegalArgumentException 652 * if the given {@link LoadStoreParameter} is not recognized. 653 */ 654 public final void load(LoadStoreParameter param) throws IOException, 655 NoSuchAlgorithmException, CertificateException { 656 implSpi.engineLoad(param); 657 isInit = true; 658 } 659 660 /** 661 * Returns the {@code Entry} with the given alias, using the specified 662 * {@code ProtectionParameter}. 663 * 664 * @param alias 665 * the alias of the requested entry. 666 * @param param 667 * the {@code ProtectionParameter} used to protect the requested 668 * entry, maybe {@code null}. 669 * @return he {@code Entry} with the given alias, using the specified 670 * {@code ProtectionParameter}. 671 * @throws NoSuchAlgorithmException 672 * if the required algorithm is not available. 673 * @throws UnrecoverableEntryException 674 * if the entry can not be recovered. 675 * @throws KeyStoreException 676 * if this {@code KeyStore} is not initialized. 677 * @throws NullPointerException 678 * if {@code alias} is {@code null}. 679 */ 680 public final Entry getEntry(String alias, ProtectionParameter param) 681 throws NoSuchAlgorithmException, UnrecoverableEntryException, 682 KeyStoreException { 683 if (alias == null) { 684 throw new NullPointerException("alias == null"); 685 } 686 if (!isInit) { 687 // BEGIN android-changed 688 throwNotInitialized(); 689 // END android-changed 690 } 691 return implSpi.engineGetEntry(alias, param); 692 } 693 694 /** 695 * Stores the given {@code Entry} in this {@code KeyStore} and associates 696 * the entry with the given {@code alias}. The entry is protected by the 697 * specified {@code ProtectionParameter}. 698 * <p> 699 * If the specified alias already exists, it will be reassigned. 700 * 701 * @param alias 702 * the alias for the entry. 703 * @param entry 704 * the entry to store. 705 * @param param 706 * the {@code ProtectionParameter} to protect the entry. 707 * @throws KeyStoreException 708 * if this {@code KeyStore} is not initialized. 709 * @throws NullPointerException 710 * if {@code alias} is {@code null} or {@code entry} is {@code 711 * null}. 712 */ 713 public final void setEntry(String alias, Entry entry, 714 ProtectionParameter param) throws KeyStoreException { 715 if (!isInit) { 716 // BEGIN android-changed 717 throwNotInitialized(); 718 // END android-changed 719 } 720 if (alias == null) { 721 throw new NullPointerException("alias == null"); 722 } 723 if (entry == null) { 724 throw new NullPointerException("entry == null"); 725 } 726 implSpi.engineSetEntry(alias, entry, param); 727 } 728 729 /** 730 * Indicates whether the entry for the given alias is assignable to the 731 * provided {@code Class}. 732 * 733 * @param alias 734 * the alias for the entry. 735 * @param entryClass 736 * the type of the entry. 737 * @return {@code true} if the {@code Entry} for the alias is assignable to 738 * the specified {@code entryClass}. 739 * @throws KeyStoreException 740 * if this {@code KeyStore} is not initialized. 741 */ 742 public final boolean entryInstanceOf(String alias, 743 Class<? extends KeyStore.Entry> entryClass) 744 throws KeyStoreException { 745 if (alias == null) { 746 throw new NullPointerException("alias == null"); 747 } 748 if (entryClass == null) { 749 throw new NullPointerException("entryClass == null"); 750 } 751 752 if (!isInit) { 753 // BEGIN android-changed 754 throwNotInitialized(); 755 // END android-changed 756 } 757 return implSpi.engineEntryInstanceOf(alias, entryClass); 758 } 759 760 /** 761 * {@code Builder} is used to construct new instances of {@code KeyStore}. 762 */ 763 public abstract static class Builder { 764 /** 765 * Constructs a new instance of {@code Builder}. 766 */ 767 protected Builder() { 768 } 769 770 /** 771 * Returns the {@code KeyStore} created by this {@code Builder}. 772 * 773 * @return the {@code KeyStore} created by this {@code Builder}. 774 * @throws KeyStoreException 775 * if an error occurred during construction. 776 */ 777 public abstract KeyStore getKeyStore() throws KeyStoreException; 778 779 /** 780 * Returns the {@code ProtectionParameter} to be used when a {@code 781 * Entry} with the specified alias is requested. Before this method is 782 * invoked, {@link #getKeyStore()} must be called. 783 * 784 * @param alias 785 * the alias for the entry. 786 * @return the {@code ProtectionParameter} to be used when a {@code 787 * Entry} with the specified alias is requested. 788 * @throws KeyStoreException 789 * if an error occurred during the lookup for the protection 790 * parameter. 791 * @throws IllegalStateException 792 * if {@link #getKeyStore()} is not called prior the 793 * invocation of this method. 794 * @throws NullPointerException 795 * if {@code alias} is {@code null}. 796 */ 797 public abstract ProtectionParameter getProtectionParameter(String alias) 798 throws KeyStoreException; 799 800 /** 801 * Returns a new {@code Builder} that holds the given {@code KeyStore} 802 * and the given {@code ProtectionParameter}. 803 * 804 * @param keyStore 805 * the {@code KeyStore} to be held. 806 * @param protectionParameter 807 * the {@code ProtectionParameter} to be held. 808 * @return a new instance of {@code Builder} that holds the specified 809 * {@code KeyStore} and the specified {@code 810 * ProtectionParameter}. 811 * @throws NullPointerException 812 * if {@code keyStore} or {@code protectionParameter} is 813 * {@code null}. 814 * @throws IllegalArgumentException 815 * if the given {@code KeyStore} is not initialized. 816 */ 817 public static Builder newInstance(KeyStore keyStore, 818 ProtectionParameter protectionParameter) { 819 if (keyStore == null) { 820 throw new NullPointerException("keyStore == null"); 821 } 822 if (protectionParameter == null) { 823 throw new NullPointerException("protectionParameter == null"); 824 } 825 if (!keyStore.isInit) { 826 throw new IllegalArgumentException("KeyStore was not initialized"); 827 } 828 return new BuilderImpl(keyStore, protectionParameter, 829 null, null, null, null); 830 } 831 832 /** 833 * Returns a new {@code Builder} that creates a new {@code KeyStore} 834 * based on the provided arguments. 835 * <p> 836 * If {@code provider} is {@code null}, all installed providers are 837 * searched, otherwise the key store from the specified provider is 838 * used. 839 * 840 * @param type 841 * the type of the {@code KeyStore} to be constructed. 842 * @param provider 843 * the provider of the {@code KeyStore} to be constructed, 844 * maybe {@code null}. 845 * @param file 846 * the {@code File} that contains the data for the {@code 847 * KeyStore}. 848 * @param protectionParameter 849 * the {@code ProtectionParameter} used to protect the stored 850 * keys. 851 * @return a new {@code Builder} that creates a new {@code KeyStore} 852 * based on the provided arguments. 853 * @throws NullPointerException 854 * if {@code type, protectionParameter} or {@code file} is 855 * {@code null}. 856 * @throws IllegalArgumentException 857 * {@code protectionParameter} not an instance of either 858 * {@code PasswordProtection} or {@code 859 * CallbackHandlerProtection}, {@code file} is not a file or 860 * does not exist at all. 861 */ 862 public static Builder newInstance(String type, Provider provider, 863 File file, ProtectionParameter protectionParameter) { 864 // check null parameters 865 if (type == null) { 866 throw new NullPointerException("type == null"); 867 } 868 if (protectionParameter == null) { 869 throw new NullPointerException("protectionParameter == null"); 870 } 871 if (file == null) { 872 throw new NullPointerException("file == null"); 873 } 874 // protection parameter should be PasswordProtection or 875 // CallbackHandlerProtection 876 if (!(protectionParameter instanceof PasswordProtection) 877 && !(protectionParameter instanceof CallbackHandlerProtection)) { 878 throw new IllegalArgumentException("protectionParameter is neither " 879 + "PasswordProtection nor CallbackHandlerProtection instance"); 880 } 881 // check file parameter 882 if (!file.exists()) { 883 throw new IllegalArgumentException("File does not exist: " + file.getName()); 884 } 885 if (!file.isFile()) { 886 throw new IllegalArgumentException("Not a regular file: " + file.getName()); 887 } 888 // create new instance 889 return new BuilderImpl(null, protectionParameter, file, 890 type, provider, AccessController.getContext()); 891 } 892 893 /** 894 * Returns a new {@code Builder} that creates a new {@code KeyStore} 895 * based on the provided arguments. 896 * <p> 897 * If {@code provider} is {@code null}, all installed providers are 898 * searched, otherwise the key store from the specified provider is 899 * used. 900 * 901 * @param type 902 * the type of the {@code KeyStore} to be constructed. 903 * @param provider 904 * the provider of the {@code KeyStore} to be constructed, 905 * maybe {@code null}. 906 * @param protectionParameter 907 * the {@code ProtectionParameter} used to protect the stored 908 * keys. 909 * @return a new {@code Builder} that creates a new {@code KeyStore} 910 * based on the provided arguments. 911 * @throws NullPointerException 912 * if {@code type} or {@code protectionParameter} is {@code 913 * null}. 914 * @throws IllegalArgumentException 915 * {@code protectionParameter} not an instance of either 916 * {@code PasswordProtection} or {@code 917 * CallbackHandlerProtection}, {@code file} is not a file or 918 * does not exist at all. 919 */ 920 public static Builder newInstance(String type, Provider provider, 921 ProtectionParameter protectionParameter) { 922 if (type == null) { 923 throw new NullPointerException("type == null"); 924 } 925 if (protectionParameter == null) { 926 throw new NullPointerException("protectionParameter == null"); 927 } 928 return new BuilderImpl(null, protectionParameter, null, 929 type, provider, AccessController.getContext()); 930 } 931 932 /* 933 * This class is implementation of abstract class KeyStore.Builder 934 * 935 * @author Vera Petrashkova 936 * 937 */ 938 private static class BuilderImpl extends Builder { 939 // Store used KeyStore 940 private KeyStore keyStore; 941 942 // Store used ProtectionParameter 943 private ProtectionParameter protParameter; 944 945 // Store used KeyStore type 946 private final String typeForKeyStore; 947 948 // Store used KeyStore provider 949 private final Provider providerForKeyStore; 950 951 // Store used file for KeyStore loading 952 private final File fileForLoad; 953 954 // Store getKeyStore method was invoked or not for KeyStoreBuilder 955 private boolean isGetKeyStore = false; 956 957 // Store last Exception in getKeyStore() 958 private KeyStoreException lastException; 959 960 // Store AccessControlContext which is used in getKeyStore() method 961 private final AccessControlContext accControlContext; 962 963 /** 964 * Constructor BuilderImpl initializes private fields: keyStore, 965 * protParameter, typeForKeyStore providerForKeyStore fileForLoad, 966 * isGetKeyStore 967 */ 968 BuilderImpl(KeyStore ks, ProtectionParameter pp, File file, 969 String type, Provider provider, AccessControlContext context) { 970 super(); 971 keyStore = ks; 972 protParameter = pp; 973 fileForLoad = file; 974 typeForKeyStore = type; 975 providerForKeyStore = provider; 976 isGetKeyStore = false; 977 lastException = null; 978 accControlContext = context; 979 } 980 981 /** 982 * Implementation of abstract getKeyStore() method If 983 * KeyStoreBuilder encapsulates KeyStore object then this object is 984 * returned 985 * 986 * If KeyStoreBuilder encapsulates KeyStore type and provider then 987 * KeyStore is created using these parameters. If KeyStoreBuilder 988 * encapsulates file and ProtectionParameter then KeyStore data are 989 * loaded from FileInputStream that is created on file. If file is 990 * not defined then KeyStore object is initialized with null 991 * InputStream and null password. 992 * 993 * Result KeyStore object is returned. 994 */ 995 @Override 996 public synchronized KeyStore getKeyStore() throws KeyStoreException { 997 // If KeyStore was created but in final block some exception was 998 // thrown 999 // then it was stored in lastException variable and will be 1000 // thrown 1001 // all subsequent calls of this method. 1002 if (lastException != null) { 1003 throw lastException; 1004 } 1005 if (keyStore != null) { 1006 isGetKeyStore = true; 1007 return keyStore; 1008 } 1009 1010 try { 1011 final KeyStore ks; 1012 final char[] passwd; 1013 1014 // get KeyStore instance using type or type and provider 1015 ks = (providerForKeyStore == null ? KeyStore 1016 .getInstance(typeForKeyStore) : KeyStore 1017 .getInstance(typeForKeyStore, providerForKeyStore)); 1018 // protection parameter should be PasswordProtection 1019 // or CallbackHandlerProtection 1020 if (protParameter instanceof PasswordProtection) { 1021 passwd = ((PasswordProtection) protParameter) 1022 .getPassword(); 1023 } else if (protParameter instanceof CallbackHandlerProtection) { 1024 passwd = KeyStoreSpi 1025 .getPasswordFromCallBack(protParameter); 1026 } else { 1027 throw new KeyStoreException("protectionParameter is neither " 1028 + "PasswordProtection nor CallbackHandlerProtection instance"); 1029 } 1030 1031 // load KeyStore from file 1032 AccessController.doPrivileged( 1033 new PrivilegedExceptionAction<Object>() { 1034 public Object run() throws Exception { 1035 if (fileForLoad != null) { 1036 FileInputStream fis = null; 1037 try { 1038 fis = new FileInputStream(fileForLoad); 1039 ks.load(fis, passwd); 1040 } finally { 1041 // close file input stream 1042 if( fis != null ) { 1043 fis.close(); 1044 } 1045 } 1046 } else { 1047 ks.load(new TmpLSParameter( 1048 protParameter)); 1049 } 1050 return null; 1051 } 1052 }, accControlContext); 1053 1054 1055 isGetKeyStore = true; 1056 return ks; 1057 } catch (KeyStoreException e) { 1058 // Store exception 1059 throw lastException = e; 1060 } catch (Exception e) { 1061 // Override exception 1062 throw lastException = new KeyStoreException(e); 1063 } 1064 } 1065 1066 /** 1067 * This is implementation of abstract method 1068 * getProtectionParameter(String alias) 1069 * 1070 * Return: ProtectionParameter to get Entry which was saved in 1071 * KeyStore with defined alias 1072 */ 1073 @Override 1074 public synchronized ProtectionParameter getProtectionParameter( 1075 String alias) throws KeyStoreException { 1076 if (alias == null) { 1077 throw new NullPointerException("alias == null"); 1078 } 1079 if (!isGetKeyStore) { 1080 throw new IllegalStateException("getKeyStore() was not invoked"); 1081 } 1082 return protParameter; 1083 } 1084 } 1085 1086 /* 1087 * Implementation of LoadStoreParameter interface 1088 */ 1089 private static class TmpLSParameter implements LoadStoreParameter { 1090 1091 // Store used protection parameter 1092 private final ProtectionParameter protPar; 1093 1094 /** 1095 * Creates TmpLoadStoreParameter object 1096 * @param protPar protection parameter 1097 */ 1098 public TmpLSParameter(ProtectionParameter protPar) { 1099 this.protPar = protPar; 1100 } 1101 1102 /** 1103 * This method returns protection parameter 1104 */ 1105 public ProtectionParameter getProtectionParameter() { 1106 return protPar; 1107 } 1108 } 1109 } 1110 1111 /** 1112 * {@code CallbackHandlerProtection} is a {@code ProtectionParameter} that 1113 * encapsulates a {@link CallbackHandler}. 1114 */ 1115 public static class CallbackHandlerProtection implements 1116 ProtectionParameter { 1117 // Store CallbackHandler 1118 private final CallbackHandler callbackHandler; 1119 1120 /** 1121 * Constructs a new instance of {@code CallbackHandlerProtection} with 1122 * the {@code CallbackHandler}. 1123 * 1124 * @param handler 1125 * the {@code CallbackHandler}. 1126 * @throws NullPointerException 1127 * if {@code handler} is {@code null}. 1128 */ 1129 public CallbackHandlerProtection(CallbackHandler handler) { 1130 if (handler == null) { 1131 throw new NullPointerException("handler == null"); 1132 } 1133 this.callbackHandler = handler; 1134 } 1135 1136 /** 1137 * Returns the {@code CallbackHandler}. 1138 * 1139 * @return the {@code CallbackHandler}. 1140 */ 1141 public CallbackHandler getCallbackHandler() { 1142 return callbackHandler; 1143 } 1144 } 1145 1146 /** 1147 * {@code Entry} is the common marker interface for a {@code KeyStore} 1148 * entry. 1149 */ 1150 public static interface Entry { 1151 } 1152 1153 /** 1154 * {@code LoadStoreParameter} represents a parameter that specifies how a 1155 * {@code KeyStore} can be loaded and stored. 1156 * 1157 * @see KeyStore#load(LoadStoreParameter) 1158 * @see KeyStore#store(LoadStoreParameter) 1159 */ 1160 public static interface LoadStoreParameter { 1161 /** 1162 * Returns the {@code ProtectionParameter} which is used to protect data 1163 * in the {@code KeyStore}. 1164 * 1165 * @return the {@code ProtectionParameter} which is used to protect data 1166 * in the {@code KeyStore}, maybe {@code null}. 1167 */ 1168 public ProtectionParameter getProtectionParameter(); 1169 } 1170 1171 /** 1172 * {@code PasswordProtection} is a {@code ProtectionParameter} that protects 1173 * a {@code KeyStore} using a password. 1174 */ 1175 public static class PasswordProtection implements ProtectionParameter, 1176 Destroyable { 1177 1178 // Store password 1179 private char[] password; 1180 1181 private boolean isDestroyed = false; 1182 1183 /** 1184 * Constructs a new instance of {@code PasswordProtection} with a 1185 * password. A copy of the password is stored in the new {@code 1186 * PasswordProtection} object. 1187 * 1188 * @param password 1189 * the password, maybe {@code null}. 1190 */ 1191 public PasswordProtection(char[] password) { 1192 if (password != null) { 1193 this.password = password.clone(); 1194 } 1195 } 1196 1197 /** 1198 * Returns the password. 1199 * 1200 * @return the password. 1201 * @throws IllegalStateException 1202 * if the password has been destroyed. 1203 */ 1204 public synchronized char[] getPassword() { 1205 if (isDestroyed) { 1206 throw new IllegalStateException("Password was destroyed"); 1207 } 1208 return password; 1209 } 1210 1211 /** 1212 * Destroys / invalidates the password. 1213 * 1214 * @throws DestroyFailedException 1215 * if the password could not be invalidated. 1216 */ 1217 public synchronized void destroy() throws DestroyFailedException { 1218 isDestroyed = true; 1219 if (password != null) { 1220 Arrays.fill(password, '\u0000'); 1221 password = null; 1222 } 1223 } 1224 1225 /** 1226 * Indicates whether the password is invalidated. 1227 * 1228 * @return {@code true} if the password is invalidated, {@code false} 1229 * otherwise. 1230 */ 1231 public synchronized boolean isDestroyed() { 1232 return isDestroyed; 1233 } 1234 } 1235 1236 /** 1237 * {@code ProtectionParameter} is a marker interface for protection 1238 * parameters. A protection parameter is used to protect the content of a 1239 * {@code KeyStore}. 1240 */ 1241 public static interface ProtectionParameter { 1242 } 1243 1244 /** 1245 * {@code PrivateKeyEntry} represents a {@code KeyStore} entry that 1246 * holds a private key. 1247 */ 1248 public static final class PrivateKeyEntry implements Entry { 1249 // Store Certificate chain 1250 private Certificate[] chain; 1251 1252 // Store PrivateKey 1253 private PrivateKey privateKey; 1254 1255 /** 1256 * Constructs a new instance of {@code PrivateKeyEntry} with the given 1257 * {@code PrivateKey} and the provided certificate chain. 1258 * 1259 * @param privateKey 1260 * the private key. 1261 * @param chain 1262 * the ordered certificate chain with the certificate 1263 * corresponding to the private key at index 0. 1264 * @throws NullPointerException 1265 * if {@code privateKey} or {@code chain} is {@code null}. 1266 * @throws IllegalArgumentException 1267 * if {@code chain.length == 0}, the algorithm of the 1268 * private key does not match the algorithm of the public 1269 * key of the first certificate or the certificates are not 1270 * all of the same type. 1271 */ 1272 public PrivateKeyEntry(PrivateKey privateKey, Certificate[] chain) { 1273 if (privateKey == null) { 1274 throw new NullPointerException("privateKey == null"); 1275 } 1276 if (chain == null) { 1277 throw new NullPointerException("chain == null"); 1278 } 1279 1280 if (chain.length == 0) { 1281 throw new IllegalArgumentException("chain.length == 0"); 1282 } 1283 // Match algorithm of private key and algorithm of public key from 1284 // the end certificate 1285 String s = chain[0].getType(); 1286 if (!(chain[0].getPublicKey().getAlgorithm()).equals(privateKey.getAlgorithm())) { 1287 throw new IllegalArgumentException("Algorithm of private key does not match " 1288 + "algorithm of public key in end certificate of entry " 1289 + "(with index number: 0)"); 1290 } 1291 // Match certificate types 1292 for (int i = 1; i < chain.length; i++) { 1293 if (!s.equals(chain[i].getType())) { 1294 throw new IllegalArgumentException("Certificates from the given chain have " 1295 + "different types"); 1296 } 1297 } 1298 // clone chain - this.chain = (Certificate[])chain.clone(); 1299 boolean isAllX509Certificates = true; 1300 // assert chain length > 0 1301 for(Certificate cert: chain){ 1302 if(!(cert instanceof X509Certificate)){ 1303 isAllX509Certificates = false; 1304 break; 1305 } 1306 } 1307 1308 if(isAllX509Certificates){ 1309 this.chain = new X509Certificate[chain.length]; 1310 } else { 1311 this.chain = new Certificate[chain.length]; 1312 } 1313 System.arraycopy(chain, 0, this.chain, 0, chain.length); 1314 this.privateKey = privateKey; 1315 } 1316 1317 /** 1318 * Returns the private key. 1319 * 1320 * @return the private key. 1321 */ 1322 public PrivateKey getPrivateKey() { 1323 return privateKey; 1324 } 1325 1326 /** 1327 * Returns the certificate chain. 1328 * 1329 * @return the certificate chain. 1330 */ 1331 public Certificate[] getCertificateChain() { 1332 return chain.clone(); 1333 } 1334 1335 /** 1336 * Returns the certificate corresponding to the private key. 1337 * 1338 * @return the certificate corresponding to the private key. 1339 */ 1340 public Certificate getCertificate() { 1341 return chain[0]; 1342 } 1343 1344 /** 1345 * Returns a string containing a concise, human-readable description of 1346 * this {@code PrivateKeyEntry}. 1347 * 1348 * @return a printable representation for this {@code PrivateKeyEntry}. 1349 */ 1350 @Override 1351 public String toString() { 1352 StringBuilder sb = new StringBuilder( 1353 "PrivateKeyEntry: number of elements in certificate chain is "); 1354 sb.append(Integer.toString(chain.length)); 1355 sb.append("\n"); 1356 for (int i = 0; i < chain.length; i++) { 1357 sb.append(chain[i].toString()); 1358 sb.append("\n"); 1359 } 1360 return sb.toString(); 1361 } 1362 } 1363 1364 /** 1365 * {@code SecretKeyEntry} represents a {@code KeyStore} entry that 1366 * holds a secret key. 1367 */ 1368 public static final class SecretKeyEntry implements Entry { 1369 1370 // Store SecretKey 1371 private final SecretKey secretKey; 1372 1373 /** 1374 * Constructs a new instance of {@code SecretKeyEntry} with the given 1375 * {@code SecretKey}. 1376 * 1377 * @param secretKey 1378 * the secret key. 1379 * @throws NullPointerException 1380 * if {@code secretKey} is {@code null}. 1381 */ 1382 public SecretKeyEntry(SecretKey secretKey) { 1383 if (secretKey == null) { 1384 throw new NullPointerException("secretKey == null"); 1385 } 1386 this.secretKey = secretKey; 1387 } 1388 1389 /** 1390 * Returns the secret key. 1391 * 1392 * @return the secret key. 1393 */ 1394 public SecretKey getSecretKey() { 1395 return secretKey; 1396 } 1397 1398 /** 1399 * Returns a string containing a concise, human-readable description of 1400 * this {@code SecretKeyEntry}. 1401 * 1402 * @return a printable representation for this {@code 1403 * SecretKeyEntry}. 1404 */ 1405 @Override 1406 public String toString() { 1407 StringBuilder sb = new StringBuilder("SecretKeyEntry: algorithm - "); 1408 sb.append(secretKey.getAlgorithm()); 1409 return sb.toString(); 1410 } 1411 } 1412 1413 /** 1414 * {@code TrustedCertificateEntry} represents a {@code KeyStore} entry that 1415 * holds a trusted certificate. 1416 */ 1417 public static final class TrustedCertificateEntry implements Entry { 1418 1419 // Store trusted Certificate 1420 private final Certificate trustCertificate; 1421 1422 /** 1423 * Constructs a new instance of {@code TrustedCertificateEntry} with the 1424 * given {@code Certificate}. 1425 * 1426 * @param trustCertificate 1427 * the trusted certificate. 1428 * @throws NullPointerException 1429 * if {@code trustCertificate} is {@code null}. 1430 */ 1431 public TrustedCertificateEntry(Certificate trustCertificate) { 1432 if (trustCertificate == null) { 1433 throw new NullPointerException("trustCertificate == null"); 1434 } 1435 this.trustCertificate = trustCertificate; 1436 } 1437 1438 /** 1439 * Returns the trusted certificate. 1440 * 1441 * @return the trusted certificate. 1442 */ 1443 public Certificate getTrustedCertificate() { 1444 return trustCertificate; 1445 } 1446 1447 /** 1448 * Returns a string containing a concise, human-readable description of 1449 * this {@code TrustedCertificateEntry}. 1450 * 1451 * @return a printable representation for this {@code 1452 * TrustedCertificateEntry}. 1453 */ 1454 @Override 1455 public String toString() { 1456 return "Trusted certificate entry:\n" + trustCertificate; 1457 } 1458 } 1459} 1460