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