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