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