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