1 /*
   2  * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.mscapi;
  27 
  28 import java.io.ByteArrayInputStream;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.OutputStream;
  32 import java.security.AccessController;
  33 import java.security.InvalidKeyException;
  34 import java.security.KeyStoreSpi;
  35 import java.security.KeyStoreException;
  36 import java.security.PrivilegedAction;
  37 import java.security.UnrecoverableKeyException;
  38 import java.security.NoSuchAlgorithmException;
  39 import java.security.SecurityPermission;
  40 import java.security.cert.X509Certificate;
  41 import java.security.cert.Certificate;
  42 import java.security.cert.CertificateException;
  43 import java.security.cert.CertificateFactory;
  44 import java.security.interfaces.RSAPrivateCrtKey;
  45 import java.util.*;
  46 
  47 /**
  48  * Implementation of key store for Windows using the Microsoft Crypto API.
  49  *
  50  * @since 1.6
  51  */
  52 abstract class KeyStore extends KeyStoreSpi {
  53 
  54     public static final class MY extends KeyStore {
  55         public MY() {
  56             super("MY");
  57         }
  58     }
  59 
  60     public static final class ROOT extends KeyStore {
  61         public ROOT() {
  62             super("ROOT");
  63         }
  64     }
  65 
  66     class KeyEntry
  67     {
  68         private Key privateKey;
  69         private X509Certificate[] certChain;
  70         private String alias;
  71 
  72         KeyEntry(Key key, X509Certificate[] chain) {
  73             this(null, key, chain);
  74         }
  75 
  76         KeyEntry(String alias, Key key, X509Certificate[] chain) {
  77             this.privateKey = key;
  78             this.certChain = chain;
  79             /*
  80              * The default alias for both entry types is derived from a
  81              * hash value intrinsic to the first certificate in the chain.
  82              */
  83              if (alias == null) {
  84                  this.alias = Integer.toString(chain[0].hashCode());
  85              } else {
  86                  this.alias = alias;
  87              }
  88         }
  89 
  90         /**
  91          * Gets the alias for the keystore entry.
  92          */
  93         String getAlias()
  94         {
  95             return alias;
  96         }
  97 
  98         /**
  99          * Sets the alias for the keystore entry.
 100          */
 101         void setAlias(String alias)
 102         {
 103             // TODO - set friendly name prop in cert store
 104             this.alias = alias;
 105         }
 106 
 107         /**
 108          * Gets the private key for the keystore entry.
 109          */
 110         Key getPrivateKey()
 111         {
 112             return privateKey;
 113         }
 114 
 115         /**
 116          * Sets the private key for the keystore entry.
 117          */
 118         void setPrivateKey(RSAPrivateCrtKey key)
 119             throws InvalidKeyException, KeyStoreException
 120         {
 121             byte[] modulusBytes = key.getModulus().toByteArray();
 122 
 123             // Adjust key length due to sign bit
 124             int keyBitLength = (modulusBytes[0] == 0)
 125                 ? (modulusBytes.length - 1) * 8
 126                 : modulusBytes.length * 8;
 127 
 128             byte[] keyBlob = generatePrivateKeyBlob(
 129                 keyBitLength,
 130                 modulusBytes,
 131                 key.getPublicExponent().toByteArray(),
 132                 key.getPrivateExponent().toByteArray(),
 133                 key.getPrimeP().toByteArray(),
 134                 key.getPrimeQ().toByteArray(),
 135                 key.getPrimeExponentP().toByteArray(),
 136                 key.getPrimeExponentQ().toByteArray(),
 137                 key.getCrtCoefficient().toByteArray());
 138 
 139             privateKey = storePrivateKey(Objects.requireNonNull(keyBlob),
 140                 "{" + UUID.randomUUID().toString() + "}", keyBitLength);
 141         }
 142 
 143         /**
 144          * Gets the certificate chain for the keystore entry.
 145          */
 146         X509Certificate[] getCertificateChain()
 147         {
 148             return certChain;
 149         }
 150 
 151         /**
 152          * Sets the certificate chain for the keystore entry.
 153          */
 154         void setCertificateChain(X509Certificate[] chain)
 155             throws CertificateException, KeyStoreException
 156         {
 157             for (int i = 0; i < chain.length; i++) {
 158                 byte[] encoding = chain[i].getEncoded();
 159                 if (i == 0 && privateKey != null) {
 160                     storeCertificate(getName(), alias, encoding,
 161                         encoding.length, privateKey.getHCryptProvider(),
 162                         privateKey.getHCryptKey());
 163 
 164                 } else {
 165                     storeCertificate(getName(), alias, encoding,
 166                         encoding.length, 0L, 0L); // no private key to attach
 167                 }
 168             }
 169             certChain = chain;
 170         }
 171     };
 172 
 173     /*
 174      * An X.509 certificate factory.
 175      * Used to create an X.509 certificate from its DER-encoding.
 176      */
 177     private CertificateFactory certificateFactory = null;
 178 
 179     /*
 180      * Compatibility mode: for applications that assume keystores are
 181      * stream-based this mode tolerates (but ignores) a non-null stream
 182      * or password parameter when passed to the load or store methods.
 183      * The mode is enabled by default.
 184      */
 185     private static final String KEYSTORE_COMPATIBILITY_MODE_PROP =
 186         "sun.security.mscapi.keyStoreCompatibilityMode";
 187     private final boolean keyStoreCompatibilityMode;
 188 
 189     /*
 190      * The keystore entries.
 191      */
 192     private Collection<KeyEntry> entries = new ArrayList<KeyEntry>();
 193 
 194     /*
 195      * The keystore name.
 196      * Case is not significant.
 197      */
 198     private final String storeName;
 199 
 200     KeyStore(String storeName) {
 201         // Get the compatibility mode
 202         String prop = AccessController.doPrivileged(
 203             (PrivilegedAction<String>) () -> System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP));
 204 
 205         if ("false".equalsIgnoreCase(prop)) {
 206             keyStoreCompatibilityMode = false;
 207         } else {
 208             keyStoreCompatibilityMode = true;
 209         }
 210 
 211         this.storeName = storeName;
 212     }
 213 
 214     /**
 215      * Returns the key associated with the given alias.
 216      * <p>
 217      * A compatibility mode is supported for applications that assume
 218      * a password must be supplied. It permits (but ignores) a non-null
 219      * <code>password</code>.  The mode is enabled by default.
 220      * Set the
 221      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
 222      * system property to <code>false</code> to disable compatibility mode
 223      * and reject a non-null <code>password</code>.
 224      *
 225      * @param alias the alias name
 226      * @param password the password, which should be <code>null</code>
 227      *
 228      * @return the requested key, or null if the given alias does not exist
 229      * or does not identify a <i>key entry</i>.
 230      *
 231      * @exception NoSuchAlgorithmException if the algorithm for recovering the
 232      * key cannot be found,
 233      * or if compatibility mode is disabled and <code>password</code> is
 234      * non-null.
 235      * @exception UnrecoverableKeyException if the key cannot be recovered.
 236      */
 237     public java.security.Key engineGetKey(String alias, char[] password)
 238         throws NoSuchAlgorithmException, UnrecoverableKeyException
 239     {
 240         if (alias == null) {
 241             return null;
 242         }
 243 
 244         if (password != null && !keyStoreCompatibilityMode) {
 245             throw new UnrecoverableKeyException("Password must be null");
 246         }
 247 
 248         if (engineIsKeyEntry(alias) == false)
 249             return null;
 250 
 251         for (KeyEntry entry : entries) {
 252             if (alias.equals(entry.getAlias())) {
 253                 return entry.getPrivateKey();
 254             }
 255         }
 256 
 257         return null;
 258     }
 259 
 260     /**
 261      * Returns the certificate chain associated with the given alias.
 262      *
 263      * @param alias the alias name
 264      *
 265      * @return the certificate chain (ordered with the user's certificate first
 266      * and the root certificate authority last), or null if the given alias
 267      * does not exist or does not contain a certificate chain (i.e., the given
 268      * alias identifies either a <i>trusted certificate entry</i> or a
 269      * <i>key entry</i> without a certificate chain).
 270      */
 271     public Certificate[] engineGetCertificateChain(String alias)
 272     {
 273         if (alias == null) {
 274             return null;
 275         }
 276 
 277         for (KeyEntry entry : entries) {
 278             if (alias.equals(entry.getAlias())) {
 279                 X509Certificate[] certChain = entry.getCertificateChain();
 280 
 281                 return certChain.clone();
 282             }
 283         }
 284 
 285         return null;
 286     }
 287 
 288     /**
 289      * Returns the certificate associated with the given alias.
 290      *
 291      * <p>If the given alias name identifies a
 292      * <i>trusted certificate entry</i>, the certificate associated with that
 293      * entry is returned. If the given alias name identifies a
 294      * <i>key entry</i>, the first element of the certificate chain of that
 295      * entry is returned, or null if that entry does not have a certificate
 296      * chain.
 297      *
 298      * @param alias the alias name
 299      *
 300      * @return the certificate, or null if the given alias does not exist or
 301      * does not contain a certificate.
 302      */
 303     public Certificate engineGetCertificate(String alias)
 304     {
 305         if (alias == null) {
 306             return null;
 307         }
 308 
 309         for (KeyEntry entry : entries) {
 310             if (alias.equals(entry.getAlias()))
 311             {
 312                 X509Certificate[] certChain = entry.getCertificateChain();
 313                 return certChain.length == 0 ? null : certChain[0];
 314             }
 315         }
 316 
 317         return null;
 318     }
 319 
 320     /**
 321      * Returns the creation date of the entry identified by the given alias.
 322      *
 323      * @param alias the alias name
 324      *
 325      * @return the creation date of this entry, or null if the given alias does
 326      * not exist
 327      */
 328     public Date engineGetCreationDate(String alias) {
 329         if (alias == null) {
 330             return null;
 331         }
 332         return new Date();
 333     }
 334 
 335     /**
 336      * Stores the given private key and associated certificate chain in the
 337      * keystore.
 338      *
 339      * <p>The given java.security.PrivateKey <code>key</code> must
 340      * be accompanied by a certificate chain certifying the
 341      * corresponding public key.
 342      *
 343      * <p>If the given alias already exists, the keystore information
 344      * associated with it is overridden by the given key and certificate
 345      * chain. Otherwise, a new entry is created.
 346      *
 347      * <p>
 348      * A compatibility mode is supported for applications that assume
 349      * a password must be supplied. It permits (but ignores) a non-null
 350      * <code>password</code>.  The mode is enabled by default.
 351      * Set the
 352      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
 353      * system property to <code>false</code> to disable compatibility mode
 354      * and reject a non-null <code>password</code>.
 355      *
 356      * @param alias the alias name
 357      * @param key the private key to be associated with the alias
 358      * @param password the password, which should be <code>null</code>
 359      * @param chain the certificate chain for the corresponding public
 360      *        key (only required if the given key is of type
 361      *        <code>java.security.PrivateKey</code>).
 362      *
 363      * @exception KeyStoreException if the given key is not a private key,
 364      * cannot be protected, or if compatibility mode is disabled and
 365      * <code>password</code> is non-null, or if this operation fails for
 366      * some other reason.
 367      */
 368     public void engineSetKeyEntry(String alias, java.security.Key key,
 369         char[] password, Certificate[] chain) throws KeyStoreException
 370     {
 371         if (alias == null) {
 372             throw new KeyStoreException("alias must not be null");
 373         }
 374 
 375         if (password != null && !keyStoreCompatibilityMode) {
 376             throw new KeyStoreException("Password must be null");
 377         }
 378 
 379         if (key instanceof RSAPrivateCrtKey) {
 380 
 381             KeyEntry entry = null;
 382             boolean found = false;
 383 
 384             for (KeyEntry e : entries) {
 385                 if (alias.equals(e.getAlias())) {
 386                     found = true;
 387                     entry = e;
 388                     break;
 389                 }
 390             }
 391 
 392             X509Certificate[] xchain;
 393             if (chain != null) {
 394                 if (chain instanceof X509Certificate[]) {
 395                     xchain = (X509Certificate[]) chain;
 396                 } else {
 397                     xchain = new X509Certificate[chain.length];
 398                     System.arraycopy(chain, 0, xchain, 0, chain.length);
 399                 }
 400             } else {
 401                 xchain = null;
 402             }
 403 
 404             if (! found) {
 405                 entry =
 406                     //TODO new KeyEntry(alias, key, (X509Certificate[]) chain);
 407                     new KeyEntry(alias, null, xchain);
 408                 entries.add(entry);
 409             }
 410 
 411             entry.setAlias(alias);
 412 
 413             try {
 414                 entry.setPrivateKey((RSAPrivateCrtKey) key);
 415                 entry.setCertificateChain(xchain);
 416 
 417             } catch (CertificateException ce) {
 418                 throw new KeyStoreException(ce);
 419 
 420             } catch (InvalidKeyException ike) {
 421                 throw new KeyStoreException(ike);
 422             }
 423 
 424         } else {
 425             throw new UnsupportedOperationException(
 426                 "Cannot assign the key to the given alias.");
 427         }
 428     }
 429 
 430     /**
 431      * Assigns the given key (that has already been protected) to the given
 432      * alias.
 433      *
 434      * <p>If the protected key is of type
 435      * <code>java.security.PrivateKey</code>, it must be accompanied by a
 436      * certificate chain certifying the corresponding public key. If the
 437      * underlying keystore implementation is of type <code>jks</code>,
 438      * <code>key</code> must be encoded as an
 439      * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
 440      *
 441      * <p>If the given alias already exists, the keystore information
 442      * associated with it is overridden by the given key (and possibly
 443      * certificate chain).
 444      *
 445      * @param alias the alias name
 446      * @param key the key (in protected format) to be associated with the alias
 447      * @param chain the certificate chain for the corresponding public
 448      * key (only useful if the protected key is of type
 449      * <code>java.security.PrivateKey</code>).
 450      *
 451      * @exception KeyStoreException if this operation fails.
 452      */
 453     public void engineSetKeyEntry(String alias, byte[] key,
 454                                   Certificate[] chain)
 455         throws KeyStoreException
 456     {
 457         throw new UnsupportedOperationException(
 458             "Cannot assign the encoded key to the given alias.");
 459     }
 460 
 461     /**
 462      * Assigns the given certificate to the given alias.
 463      *
 464      * <p>If the given alias already exists in this keystore and identifies a
 465      * <i>trusted certificate entry</i>, the certificate associated with it is
 466      * overridden by the given certificate.
 467      *
 468      * @param alias the alias name
 469      * @param cert the certificate
 470      *
 471      * @exception KeyStoreException if the given alias already exists and does
 472      * not identify a <i>trusted certificate entry</i>, or this operation
 473      * fails for some other reason.
 474      */
 475     public void engineSetCertificateEntry(String alias, Certificate cert)
 476         throws KeyStoreException
 477     {
 478         if (alias == null) {
 479             throw new KeyStoreException("alias must not be null");
 480         }
 481 
 482         if (cert instanceof X509Certificate) {
 483 
 484             // TODO - build CryptoAPI chain?
 485             X509Certificate[] chain =
 486                 new X509Certificate[]{ (X509Certificate) cert };
 487             KeyEntry entry = null;
 488             boolean found = false;
 489 
 490             for (KeyEntry e : entries) {
 491                 if (alias.equals(e.getAlias())) {
 492                     found = true;
 493                     entry = e;
 494                     break;
 495                 }
 496             }
 497 
 498             if (! found) {
 499                 entry =
 500                     new KeyEntry(alias, null, chain);
 501                 entries.add(entry);
 502 
 503             }
 504             if (entry.getPrivateKey() == null) { // trusted-cert entry
 505                 entry.setAlias(alias);
 506 
 507                 try {
 508                     entry.setCertificateChain(chain);
 509 
 510                 } catch (CertificateException ce) {
 511                     throw new KeyStoreException(ce);
 512                 }
 513             }
 514 
 515         } else {
 516             throw new UnsupportedOperationException(
 517                 "Cannot assign the certificate to the given alias.");
 518         }
 519     }
 520 
 521     /**
 522      * Deletes the entry identified by the given alias from this keystore.
 523      *
 524      * @param alias the alias name
 525      *
 526      * @exception KeyStoreException if the entry cannot be removed.
 527      */
 528     public void engineDeleteEntry(String alias)
 529         throws KeyStoreException
 530     {
 531         if (alias == null) {
 532             throw new KeyStoreException("alias must not be null");
 533         }
 534 
 535         for (KeyEntry entry : entries) {
 536             if (alias.equals(entry.getAlias())) {
 537 
 538                 // Get end-entity certificate and remove from system cert store
 539                 X509Certificate[] certChain = entry.getCertificateChain();
 540                 if (certChain != null) {
 541 
 542                     try {
 543 
 544                         byte[] encoding = certChain[0].getEncoded();
 545                         removeCertificate(getName(), alias, encoding,
 546                             encoding.length);
 547 
 548                     } catch (CertificateException e) {
 549                         throw new KeyStoreException("Cannot remove entry: " +
 550                             e);
 551                     }
 552                 }
 553                 Key privateKey = entry.getPrivateKey();
 554                 if (privateKey != null) {
 555                     destroyKeyContainer(
 556                         Key.getContainerName(privateKey.getHCryptProvider()));
 557                 }
 558 
 559                 entries.remove(entry);
 560                 break;
 561             }
 562         }
 563     }
 564 
 565     /**
 566      * Lists all the alias names of this keystore.
 567      *
 568      * @return enumeration of the alias names
 569      */
 570     public Enumeration<String> engineAliases() {
 571 
 572         final Iterator<KeyEntry> iter = entries.iterator();
 573 
 574         return new Enumeration<String>()
 575         {
 576             public boolean hasMoreElements()
 577             {
 578                 return iter.hasNext();
 579             }
 580 
 581             public String nextElement()
 582             {
 583                 KeyEntry entry = iter.next();
 584                 return entry.getAlias();
 585             }
 586         };
 587     }
 588 
 589     /**
 590      * Checks if the given alias exists in this keystore.
 591      *
 592      * @param alias the alias name
 593      *
 594      * @return true if the alias exists, false otherwise
 595      */
 596     public boolean engineContainsAlias(String alias) {
 597         for (Enumeration<String> enumerator = engineAliases();
 598             enumerator.hasMoreElements();)
 599         {
 600             String a = enumerator.nextElement();
 601 
 602             if (a.equals(alias))
 603                 return true;
 604         }
 605         return false;
 606     }
 607 
 608     /**
 609      * Retrieves the number of entries in this keystore.
 610      *
 611      * @return the number of entries in this keystore
 612      */
 613     public int engineSize() {
 614         return entries.size();
 615     }
 616 
 617     /**
 618      * Returns true if the entry identified by the given alias is a
 619      * <i>key entry</i>, and false otherwise.
 620      *
 621      * @return true if the entry identified by the given alias is a
 622      * <i>key entry</i>, false otherwise.
 623      */
 624     public boolean engineIsKeyEntry(String alias) {
 625 
 626         if (alias == null) {
 627             return false;
 628         }
 629 
 630         for (KeyEntry entry : entries) {
 631             if (alias.equals(entry.getAlias())) {
 632                 return entry.getPrivateKey() != null;
 633             }
 634         }
 635 
 636         return false;
 637     }
 638 
 639     /**
 640      * Returns true if the entry identified by the given alias is a
 641      * <i>trusted certificate entry</i>, and false otherwise.
 642      *
 643      * @return true if the entry identified by the given alias is a
 644      * <i>trusted certificate entry</i>, false otherwise.
 645      */
 646     public boolean engineIsCertificateEntry(String alias)
 647     {
 648         for (KeyEntry entry : entries) {
 649             if (alias.equals(entry.getAlias())) {
 650                 return entry.getPrivateKey() == null;
 651             }
 652         }
 653 
 654         return false;
 655     }
 656 
 657     /**
 658      * Returns the (alias) name of the first keystore entry whose certificate
 659      * matches the given certificate.
 660      *
 661      * <p>This method attempts to match the given certificate with each
 662      * keystore entry. If the entry being considered
 663      * is a <i>trusted certificate entry</i>, the given certificate is
 664      * compared to that entry's certificate. If the entry being considered is
 665      * a <i>key entry</i>, the given certificate is compared to the first
 666      * element of that entry's certificate chain (if a chain exists).
 667      *
 668      * @param cert the certificate to match with.
 669      *
 670      * @return the (alias) name of the first entry with matching certificate,
 671      * or null if no such entry exists in this keystore.
 672      */
 673     public String engineGetCertificateAlias(Certificate cert)
 674     {
 675         for (KeyEntry entry : entries) {
 676             if (entry.certChain != null && entry.certChain[0].equals(cert)) {
 677                 return entry.getAlias();
 678             }
 679         }
 680 
 681         return null;
 682     }
 683 
 684     /**
 685      * engineStore is currently a no-op.
 686      * Entries are stored during engineSetEntry.
 687      *
 688      * A compatibility mode is supported for applications that assume
 689      * keystores are stream-based. It permits (but ignores) a non-null
 690      * <code>stream</code> or <code>password</code>.
 691      * The mode is enabled by default.
 692      * Set the
 693      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
 694      * system property to <code>false</code> to disable compatibility mode
 695      * and reject a non-null <code>stream</code> or <code>password</code>.
 696      *
 697      * @param stream the output stream, which should be <code>null</code>
 698      * @param password the password, which should be <code>null</code>
 699      *
 700      * @exception IOException if compatibility mode is disabled and either
 701      * parameter is non-null.
 702      */
 703     public void engineStore(OutputStream stream, char[] password)
 704         throws IOException, NoSuchAlgorithmException, CertificateException
 705     {
 706         if (stream != null && !keyStoreCompatibilityMode) {
 707             throw new IOException("Keystore output stream must be null");
 708         }
 709 
 710         if (password != null && !keyStoreCompatibilityMode) {
 711             throw new IOException("Keystore password must be null");
 712         }
 713     }
 714 
 715     /**
 716      * Loads the keystore.
 717      *
 718      * A compatibility mode is supported for applications that assume
 719      * keystores are stream-based. It permits (but ignores) a non-null
 720      * <code>stream</code> or <code>password</code>.
 721      * The mode is enabled by default.
 722      * Set the
 723      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
 724      * system property to <code>false</code> to disable compatibility mode
 725      * and reject a non-null <code>stream</code> or <code>password</code>.
 726      *
 727      * @param stream the input stream, which should be <code>null</code>.
 728      * @param password the password, which should be <code>null</code>.
 729      *
 730      * @exception IOException if there is an I/O or format problem with the
 731      * keystore data. Or if compatibility mode is disabled and either
 732      * parameter is non-null.
 733      * @exception NoSuchAlgorithmException if the algorithm used to check
 734      * the integrity of the keystore cannot be found
 735      * @exception CertificateException if any of the certificates in the
 736      * keystore could not be loaded
 737      * @exception SecurityException if the security check for
 738      *  <code>SecurityPermission("authProvider.<i>name</i>")</code> does not
 739      *  pass, where <i>name</i> is the value returned by
 740      *  this provider's <code>getName</code> method.
 741      */
 742     public void engineLoad(InputStream stream, char[] password)
 743         throws IOException, NoSuchAlgorithmException, CertificateException
 744     {
 745         if (stream != null && !keyStoreCompatibilityMode) {
 746             throw new IOException("Keystore input stream must be null");
 747         }
 748 
 749         if (password != null && !keyStoreCompatibilityMode) {
 750             throw new IOException("Keystore password must be null");
 751         }
 752 
 753         /*
 754          * Use the same security check as AuthProvider.login
 755          */
 756         SecurityManager sm = System.getSecurityManager();
 757         if (sm != null) {
 758             sm.checkPermission(new SecurityPermission(
 759                 "authProvider.SunMSCAPI"));
 760         }
 761 
 762         // Clear all key entries
 763         entries.clear();
 764 
 765         try {
 766 
 767             // Load keys and/or certificate chains
 768             loadKeysOrCertificateChains(getName(), entries);
 769 
 770         } catch (KeyStoreException e) {
 771             throw new IOException(e);
 772         }
 773     }
 774 
 775     /**
 776      * Generates a certificate chain from the collection of
 777      * certificates and stores the result into a key entry.
 778      */
 779     private void generateCertificateChain(String alias,
 780         Collection<? extends Certificate> certCollection,
 781         Collection<KeyEntry> entries)
 782     {
 783         try
 784         {
 785             X509Certificate[] certChain =
 786                 new X509Certificate[certCollection.size()];
 787 
 788             int i = 0;
 789             for (Iterator<? extends Certificate> iter =
 790                     certCollection.iterator(); iter.hasNext(); i++)
 791             {
 792                 certChain[i] = (X509Certificate) iter.next();
 793             }
 794 
 795             KeyEntry entry = new KeyEntry(alias, null, certChain);
 796 
 797             // Add cert chain
 798             entries.add(entry);
 799         }
 800         catch (Throwable e)
 801         {
 802             // Ignore the exception and skip this entry
 803             // TODO - throw CertificateException?
 804         }
 805     }
 806 
 807     /**
 808      * Generates RSA key and certificate chain from the private key handle,
 809      * collection of certificates and stores the result into key entries.
 810      */
 811     private void generateRSAKeyAndCertificateChain(String alias,
 812         long hCryptProv, long hCryptKey, int keyLength,
 813         Collection<? extends Certificate> certCollection,
 814         Collection<KeyEntry> entries)
 815     {
 816         try
 817         {
 818             X509Certificate[] certChain =
 819                 new X509Certificate[certCollection.size()];
 820 
 821             int i = 0;
 822             for (Iterator<? extends Certificate> iter =
 823                     certCollection.iterator(); iter.hasNext(); i++)
 824             {
 825                 certChain[i] = (X509Certificate) iter.next();
 826             }
 827 
 828             KeyEntry entry = new KeyEntry(alias, new RSAPrivateKey(hCryptProv,
 829                 hCryptKey, keyLength), certChain);
 830 
 831             // Add cert chain
 832             entries.add(entry);
 833         }
 834         catch (Throwable e)
 835         {
 836             // Ignore the exception and skip this entry
 837             // TODO - throw CertificateException?
 838         }
 839     }
 840 
 841     /**
 842      * Generates certificates from byte data and stores into cert collection.
 843      *
 844      * @param data Byte data.
 845      * @param certCollection Collection of certificates.
 846      */
 847     private void generateCertificate(byte[] data,
 848         Collection<Certificate> certCollection) {
 849         try
 850         {
 851             ByteArrayInputStream bis = new ByteArrayInputStream(data);
 852 
 853             // Obtain certificate factory
 854             if (certificateFactory == null) {
 855                 certificateFactory = CertificateFactory.getInstance("X.509", "SUN");
 856             }
 857 
 858             // Generate certificate
 859             Collection<? extends Certificate> c =
 860                     certificateFactory.generateCertificates(bis);
 861             certCollection.addAll(c);
 862         }
 863         catch (CertificateException e)
 864         {
 865             // Ignore the exception and skip this certificate
 866             // TODO - throw CertificateException?
 867         }
 868         catch (Throwable te)
 869         {
 870             // Ignore the exception and skip this certificate
 871             // TODO - throw CertificateException?
 872         }
 873     }
 874 
 875     /**
 876      * Returns the name of the keystore.
 877      */
 878     private String getName()
 879     {
 880         return storeName;
 881     }
 882 
 883     /**
 884      * Load keys and/or certificates from keystore into Collection.
 885      *
 886      * @param name Name of keystore.
 887      * @param entries Collection of key/certificate.
 888      */
 889     private native void loadKeysOrCertificateChains(String name,
 890         Collection<KeyEntry> entries) throws KeyStoreException;
 891 
 892     /**
 893      * Stores a DER-encoded certificate into the certificate store
 894      *
 895      * @param name Name of the keystore.
 896      * @param alias Name of the certificate.
 897      * @param encoding DER-encoded certificate.
 898      */
 899     private native void storeCertificate(String name, String alias,
 900         byte[] encoding, int encodingLength, long hCryptProvider,
 901         long hCryptKey) throws CertificateException, KeyStoreException;
 902 
 903     /**
 904      * Removes the certificate from the certificate store
 905      *
 906      * @param name Name of the keystore.
 907      * @param alias Name of the certificate.
 908      * @param encoding DER-encoded certificate.
 909      */
 910     private native void removeCertificate(String name, String alias,
 911         byte[] encoding, int encodingLength)
 912             throws CertificateException, KeyStoreException;
 913 
 914     /**
 915      * Destroys the key container.
 916      *
 917      * @param keyContainerName The name of the key container.
 918      */
 919     private native void destroyKeyContainer(String keyContainerName)
 920         throws KeyStoreException;
 921 
 922     /**
 923      * Generates a private-key BLOB from a key's components.
 924      */
 925     private native byte[] generatePrivateKeyBlob(
 926         int keyBitLength,
 927         byte[] modulus,
 928         byte[] publicExponent,
 929         byte[] privateExponent,
 930         byte[] primeP,
 931         byte[] primeQ,
 932         byte[] exponentP,
 933         byte[] exponentQ,
 934         byte[] crtCoefficient) throws InvalidKeyException;
 935 
 936     private native RSAPrivateKey storePrivateKey(byte[] keyBlob,
 937         String keyContainerName, int keySize) throws KeyStoreException;
 938 }