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