jdk/src/jdk.crypto.token/share/classes/sun/security/pkcs11/P11KeyStore.java
changeset 42693 6645de32a866
parent 32634 614f8e5859aa
equal deleted inserted replaced
42692:97247477b481 42693:6645de32a866
       
     1 /*
       
     2  * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.security.pkcs11;
       
    27 
       
    28 import java.math.BigInteger;
       
    29 
       
    30 import java.io.InputStream;
       
    31 import java.io.OutputStream;
       
    32 import java.io.IOException;
       
    33 import java.io.ByteArrayInputStream;
       
    34 import java.io.UnsupportedEncodingException;
       
    35 
       
    36 import java.util.Arrays;
       
    37 import java.util.Collections;
       
    38 import java.util.Date;
       
    39 import java.util.Enumeration;
       
    40 import java.util.ArrayList;
       
    41 import java.util.HashSet;
       
    42 import java.util.HashMap;
       
    43 import java.util.Set;
       
    44 
       
    45 import java.security.*;
       
    46 import java.security.KeyStore.*;
       
    47 
       
    48 import java.security.cert.Certificate;
       
    49 import java.security.cert.X509Certificate;
       
    50 import java.security.cert.CertificateFactory;
       
    51 import java.security.cert.CertificateException;
       
    52 
       
    53 import java.security.interfaces.*;
       
    54 import java.security.spec.*;
       
    55 
       
    56 import javax.crypto.SecretKey;
       
    57 import javax.crypto.interfaces.*;
       
    58 
       
    59 import javax.security.auth.x500.X500Principal;
       
    60 import javax.security.auth.login.LoginException;
       
    61 import javax.security.auth.callback.Callback;
       
    62 import javax.security.auth.callback.PasswordCallback;
       
    63 import javax.security.auth.callback.CallbackHandler;
       
    64 import javax.security.auth.callback.UnsupportedCallbackException;
       
    65 
       
    66 import sun.security.util.Debug;
       
    67 import sun.security.util.DerValue;
       
    68 import sun.security.util.ECUtil;
       
    69 
       
    70 import sun.security.pkcs11.Secmod.*;
       
    71 import static sun.security.pkcs11.P11Util.*;
       
    72 
       
    73 import sun.security.pkcs11.wrapper.*;
       
    74 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
       
    75 
       
    76 import sun.security.rsa.RSAKeyFactory;
       
    77 
       
    78 final class P11KeyStore extends KeyStoreSpi {
       
    79 
       
    80     private static final CK_ATTRIBUTE ATTR_CLASS_CERT =
       
    81                         new CK_ATTRIBUTE(CKA_CLASS, CKO_CERTIFICATE);
       
    82     private static final CK_ATTRIBUTE ATTR_CLASS_PKEY =
       
    83                         new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY);
       
    84     private static final CK_ATTRIBUTE ATTR_CLASS_SKEY =
       
    85                         new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY);
       
    86 
       
    87     private static final CK_ATTRIBUTE ATTR_X509_CERT_TYPE =
       
    88                         new CK_ATTRIBUTE(CKA_CERTIFICATE_TYPE, CKC_X_509);
       
    89 
       
    90     private static final CK_ATTRIBUTE ATTR_TOKEN_TRUE =
       
    91                         new CK_ATTRIBUTE(CKA_TOKEN, true);
       
    92 
       
    93     // XXX for testing purposes only
       
    94     //  - NSS doesn't support persistent secret keys
       
    95     //    (key type gets mangled if secret key is a token key)
       
    96     //  - if debug is turned on, then this is set to false
       
    97     private static CK_ATTRIBUTE ATTR_SKEY_TOKEN_TRUE = ATTR_TOKEN_TRUE;
       
    98 
       
    99     private static final CK_ATTRIBUTE ATTR_TRUSTED_TRUE =
       
   100                         new CK_ATTRIBUTE(CKA_TRUSTED, true);
       
   101     private static final CK_ATTRIBUTE ATTR_PRIVATE_TRUE =
       
   102                         new CK_ATTRIBUTE(CKA_PRIVATE, true);
       
   103 
       
   104     private static final long NO_HANDLE = -1;
       
   105     private static final long FINDOBJECTS_MAX = 100;
       
   106     private static final String ALIAS_SEP = "/";
       
   107 
       
   108     private static final boolean NSS_TEST = false;
       
   109     private static final Debug debug =
       
   110                         Debug.getInstance("pkcs11keystore");
       
   111     private static boolean CKA_TRUSTED_SUPPORTED = true;
       
   112 
       
   113     private final Token token;
       
   114 
       
   115     // If multiple certs are found to share the same CKA_LABEL
       
   116     // at load time (NSS-style keystore), then the keystore is read
       
   117     // and the unique keystore aliases are mapped to the entries.
       
   118     // However, write capabilities are disabled.
       
   119     private boolean writeDisabled = false;
       
   120 
       
   121     // Map of unique keystore aliases to entries in the token
       
   122     private HashMap<String, AliasInfo> aliasMap;
       
   123 
       
   124     // whether to use NSS Secmod info for trust attributes
       
   125     private final boolean useSecmodTrust;
       
   126 
       
   127     // if useSecmodTrust == true, which type of trust we are interested in
       
   128     private Secmod.TrustType nssTrustType;
       
   129 
       
   130     /**
       
   131      * The underlying token may contain multiple certs belonging to the
       
   132      * same "personality" (for example, a signing cert and encryption cert),
       
   133      * all sharing the same CKA_LABEL.  These must be resolved
       
   134      * into unique keystore aliases.
       
   135      *
       
   136      * In addition, private keys and certs may not have a CKA_LABEL.
       
   137      * It is assumed that a private key and corresponding certificate
       
   138      * share the same CKA_ID, and that the CKA_ID is unique across the token.
       
   139      * The CKA_ID may not be human-readable.
       
   140      * These pairs must be resolved into unique keystore aliases.
       
   141      *
       
   142      * Furthermore, secret keys are assumed to have a CKA_LABEL
       
   143      * unique across the entire token.
       
   144      *
       
   145      * When the KeyStore is loaded, instances of this class are
       
   146      * created to represent the private keys/secret keys/certs
       
   147      * that reside on the token.
       
   148      */
       
   149     private static class AliasInfo {
       
   150 
       
   151         // CKA_CLASS - entry type
       
   152         private CK_ATTRIBUTE type = null;
       
   153 
       
   154         // CKA_LABEL of cert and secret key
       
   155         private String label = null;
       
   156 
       
   157         // CKA_ID of the private key/cert pair
       
   158         private byte[] id = null;
       
   159 
       
   160         // CKA_TRUSTED - true if cert is trusted
       
   161         private boolean trusted = false;
       
   162 
       
   163         // either end-entity cert or trusted cert depending on 'type'
       
   164         private X509Certificate cert = null;
       
   165 
       
   166         // chain
       
   167         private X509Certificate[] chain = null;
       
   168 
       
   169         // true if CKA_ID for private key and cert match up
       
   170         private boolean matched = false;
       
   171 
       
   172         // SecretKeyEntry
       
   173         public AliasInfo(String label) {
       
   174             this.type = ATTR_CLASS_SKEY;
       
   175             this.label = label;
       
   176         }
       
   177 
       
   178         // PrivateKeyEntry
       
   179         public AliasInfo(String label,
       
   180                         byte[] id,
       
   181                         boolean trusted,
       
   182                         X509Certificate cert) {
       
   183             this.type = ATTR_CLASS_PKEY;
       
   184             this.label = label;
       
   185             this.id = id;
       
   186             this.trusted = trusted;
       
   187             this.cert = cert;
       
   188         }
       
   189 
       
   190         public String toString() {
       
   191             StringBuilder sb = new StringBuilder();
       
   192             if (type == ATTR_CLASS_PKEY) {
       
   193                 sb.append("\ttype=[private key]\n");
       
   194             } else if (type == ATTR_CLASS_SKEY) {
       
   195                 sb.append("\ttype=[secret key]\n");
       
   196             } else if (type == ATTR_CLASS_CERT) {
       
   197                 sb.append("\ttype=[trusted cert]\n");
       
   198             }
       
   199             sb.append("\tlabel=[" + label + "]\n");
       
   200             if (id == null) {
       
   201                 sb.append("\tid=[null]\n");
       
   202             } else {
       
   203                 sb.append("\tid=" + P11KeyStore.getID(id) + "\n");
       
   204             }
       
   205             sb.append("\ttrusted=[" + trusted + "]\n");
       
   206             sb.append("\tmatched=[" + matched + "]\n");
       
   207             if (cert == null) {
       
   208                 sb.append("\tcert=[null]\n");
       
   209             } else {
       
   210                 sb.append("\tcert=[\tsubject: " +
       
   211                         cert.getSubjectX500Principal() +
       
   212                         "\n\t\tissuer: " +
       
   213                         cert.getIssuerX500Principal() +
       
   214                         "\n\t\tserialNum: " +
       
   215                         cert.getSerialNumber().toString() +
       
   216                         "]");
       
   217             }
       
   218             return sb.toString();
       
   219         }
       
   220     }
       
   221 
       
   222     /**
       
   223      * callback handler for passing password to Provider.login method
       
   224      */
       
   225     private static class PasswordCallbackHandler implements CallbackHandler {
       
   226 
       
   227         private char[] password;
       
   228 
       
   229         private PasswordCallbackHandler(char[] password) {
       
   230             if (password != null) {
       
   231                 this.password = password.clone();
       
   232             }
       
   233         }
       
   234 
       
   235         public void handle(Callback[] callbacks)
       
   236                 throws IOException, UnsupportedCallbackException {
       
   237             if (!(callbacks[0] instanceof PasswordCallback)) {
       
   238                 throw new UnsupportedCallbackException(callbacks[0]);
       
   239             }
       
   240             PasswordCallback pc = (PasswordCallback)callbacks[0];
       
   241             pc.setPassword(password);  // this clones the password if not null
       
   242         }
       
   243 
       
   244         protected void finalize() throws Throwable {
       
   245             if (password != null) {
       
   246                 Arrays.fill(password, ' ');
       
   247             }
       
   248             super.finalize();
       
   249         }
       
   250     }
       
   251 
       
   252     /**
       
   253      * getTokenObject return value.
       
   254      *
       
   255      * if object is not found, type is set to null.
       
   256      * otherwise, type is set to the requested type.
       
   257      */
       
   258     private static class THandle {
       
   259         private final long handle;              // token object handle
       
   260         private final CK_ATTRIBUTE type;        // CKA_CLASS
       
   261 
       
   262         private THandle(long handle, CK_ATTRIBUTE type) {
       
   263             this.handle = handle;
       
   264             this.type = type;
       
   265         }
       
   266     }
       
   267 
       
   268     P11KeyStore(Token token) {
       
   269         this.token = token;
       
   270         this.useSecmodTrust = token.provider.nssUseSecmodTrust;
       
   271     }
       
   272 
       
   273     /**
       
   274      * Returns the key associated with the given alias.
       
   275      * The key must have been associated with
       
   276      * the alias by a call to <code>setKeyEntry</code>,
       
   277      * or by a call to <code>setEntry</code> with a
       
   278      * <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>.
       
   279      *
       
   280      * @param alias the alias name
       
   281      * @param password the password, which must be <code>null</code>
       
   282      *
       
   283      * @return the requested key, or null if the given alias does not exist
       
   284      * or does not identify a key-related entry.
       
   285      *
       
   286      * @exception NoSuchAlgorithmException if the algorithm for recovering the
       
   287      * key cannot be found
       
   288      * @exception UnrecoverableKeyException if the key cannot be recovered
       
   289      */
       
   290     public synchronized Key engineGetKey(String alias, char[] password)
       
   291                 throws NoSuchAlgorithmException, UnrecoverableKeyException {
       
   292 
       
   293         token.ensureValid();
       
   294         if (password != null && !token.config.getKeyStoreCompatibilityMode()) {
       
   295             throw new NoSuchAlgorithmException("password must be null");
       
   296         }
       
   297 
       
   298         AliasInfo aliasInfo = aliasMap.get(alias);
       
   299         if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {
       
   300             return null;
       
   301         }
       
   302 
       
   303         Session session = null;
       
   304         try {
       
   305             session = token.getOpSession();
       
   306 
       
   307             if (aliasInfo.type == ATTR_CLASS_PKEY) {
       
   308                 THandle h = getTokenObject(session,
       
   309                                         aliasInfo.type,
       
   310                                         aliasInfo.id,
       
   311                                         null);
       
   312                 if (h.type == ATTR_CLASS_PKEY) {
       
   313                     return loadPkey(session, h.handle);
       
   314                 }
       
   315             } else {
       
   316                 THandle h = getTokenObject(session,
       
   317                                         ATTR_CLASS_SKEY,
       
   318                                         null,
       
   319                                         alias);
       
   320                 if (h.type == ATTR_CLASS_SKEY) {
       
   321                     return loadSkey(session, h.handle);
       
   322                 }
       
   323             }
       
   324 
       
   325             // did not find anything
       
   326             return null;
       
   327         } catch (PKCS11Exception | KeyStoreException e) {
       
   328             throw new ProviderException(e);
       
   329         } finally {
       
   330             token.releaseSession(session);
       
   331         }
       
   332     }
       
   333 
       
   334     /**
       
   335      * Returns the certificate chain associated with the given alias.
       
   336      * The certificate chain must have been associated with the alias
       
   337      * by a call to <code>setKeyEntry</code>,
       
   338      * or by a call to <code>setEntry</code> with a
       
   339      * <code>PrivateKeyEntry</code>.
       
   340      *
       
   341      * @param alias the alias name
       
   342      *
       
   343      * @return the certificate chain (ordered with the user's certificate first
       
   344      * and the root certificate authority last), or null if the given alias
       
   345      * does not exist or does not contain a certificate chain
       
   346      */
       
   347     public synchronized Certificate[] engineGetCertificateChain(String alias) {
       
   348 
       
   349         token.ensureValid();
       
   350 
       
   351         AliasInfo aliasInfo = aliasMap.get(alias);
       
   352         if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_PKEY) {
       
   353             return null;
       
   354         }
       
   355         return aliasInfo.chain;
       
   356     }
       
   357 
       
   358     /**
       
   359      * Returns the certificate associated with the given alias.
       
   360      *
       
   361      * <p> If the given alias name identifies an entry
       
   362      * created by a call to <code>setCertificateEntry</code>,
       
   363      * or created by a call to <code>setEntry</code> with a
       
   364      * <code>TrustedCertificateEntry</code>,
       
   365      * then the trusted certificate contained in that entry is returned.
       
   366      *
       
   367      * <p> If the given alias name identifies an entry
       
   368      * created by a call to <code>setKeyEntry</code>,
       
   369      * or created by a call to <code>setEntry</code> with a
       
   370      * <code>PrivateKeyEntry</code>,
       
   371      * then the first element of the certificate chain in that entry
       
   372      * (if a chain exists) is returned.
       
   373      *
       
   374      * @param alias the alias name
       
   375      *
       
   376      * @return the certificate, or null if the given alias does not exist or
       
   377      * does not contain a certificate.
       
   378      */
       
   379     public synchronized Certificate engineGetCertificate(String alias) {
       
   380         token.ensureValid();
       
   381 
       
   382         AliasInfo aliasInfo = aliasMap.get(alias);
       
   383         if (aliasInfo == null) {
       
   384             return null;
       
   385         }
       
   386         return aliasInfo.cert;
       
   387     }
       
   388 
       
   389     /**
       
   390      * Returns the creation date of the entry identified by the given alias.
       
   391      *
       
   392      * @param alias the alias name
       
   393      *
       
   394      * @return the creation date of this entry, or null if the given alias does
       
   395      * not exist
       
   396      */
       
   397     public Date engineGetCreationDate(String alias) {
       
   398         token.ensureValid();
       
   399         throw new ProviderException(new UnsupportedOperationException());
       
   400     }
       
   401 
       
   402     /**
       
   403      * Assigns the given key to the given alias, protecting it with the given
       
   404      * password.
       
   405      *
       
   406      * <p>If the given key is of type <code>java.security.PrivateKey</code>,
       
   407      * it must be accompanied by a certificate chain certifying the
       
   408      * corresponding public key.
       
   409      *
       
   410      * <p>If the given alias already exists, the keystore information
       
   411      * associated with it is overridden by the given key (and possibly
       
   412      * certificate chain).
       
   413      *
       
   414      * @param alias the alias name
       
   415      * @param key the key to be associated with the alias
       
   416      * @param password the password to protect the key
       
   417      * @param chain the certificate chain for the corresponding public
       
   418      * key (only required if the given key is of type
       
   419      * <code>java.security.PrivateKey</code>).
       
   420      *
       
   421      * @exception KeyStoreException if the given key cannot be protected, or
       
   422      * this operation fails for some other reason
       
   423      */
       
   424     public synchronized void engineSetKeyEntry(String alias, Key key,
       
   425                                    char[] password,
       
   426                                    Certificate[] chain)
       
   427                 throws KeyStoreException {
       
   428 
       
   429         token.ensureValid();
       
   430         checkWrite();
       
   431 
       
   432         if (!(key instanceof PrivateKey) && !(key instanceof SecretKey)) {
       
   433             throw new KeyStoreException("key must be PrivateKey or SecretKey");
       
   434         } else if (key instanceof PrivateKey && chain == null) {
       
   435             throw new KeyStoreException
       
   436                 ("PrivateKey must be accompanied by non-null chain");
       
   437         } else if (key instanceof SecretKey && chain != null) {
       
   438             throw new KeyStoreException
       
   439                 ("SecretKey must be accompanied by null chain");
       
   440         } else if (password != null &&
       
   441                     !token.config.getKeyStoreCompatibilityMode()) {
       
   442             throw new KeyStoreException("Password must be null");
       
   443         }
       
   444 
       
   445         KeyStore.Entry entry = null;
       
   446         try {
       
   447             if (key instanceof PrivateKey) {
       
   448                 entry = new KeyStore.PrivateKeyEntry((PrivateKey)key, chain);
       
   449             } else if (key instanceof SecretKey) {
       
   450                 entry = new KeyStore.SecretKeyEntry((SecretKey)key);
       
   451             }
       
   452         } catch (NullPointerException | IllegalArgumentException e) {
       
   453             throw new KeyStoreException(e);
       
   454         }
       
   455         engineSetEntry(alias, entry, new KeyStore.PasswordProtection(password));
       
   456     }
       
   457 
       
   458     /**
       
   459      * Assigns the given key (that has already been protected) to the given
       
   460      * alias.
       
   461      *
       
   462      * <p>If the protected key is of type
       
   463      * <code>java.security.PrivateKey</code>,
       
   464      * it must be accompanied by a certificate chain certifying the
       
   465      * corresponding public key.
       
   466      *
       
   467      * <p>If the given alias already exists, the keystore information
       
   468      * associated with it is overridden by the given key (and possibly
       
   469      * certificate chain).
       
   470      *
       
   471      * @param alias the alias name
       
   472      * @param key the key (in protected format) to be associated with the alias
       
   473      * @param chain the certificate chain for the corresponding public
       
   474      * key (only useful if the protected key is of type
       
   475      * <code>java.security.PrivateKey</code>).
       
   476      *
       
   477      * @exception KeyStoreException if this operation fails.
       
   478      */
       
   479     public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
       
   480                 throws KeyStoreException {
       
   481         token.ensureValid();
       
   482         throw new ProviderException(new UnsupportedOperationException());
       
   483     }
       
   484 
       
   485     /**
       
   486      * Assigns the given certificate to the given alias.
       
   487      *
       
   488      * <p> If the given alias identifies an existing entry
       
   489      * created by a call to <code>setCertificateEntry</code>,
       
   490      * or created by a call to <code>setEntry</code> with a
       
   491      * <code>TrustedCertificateEntry</code>,
       
   492      * the trusted certificate in the existing entry
       
   493      * is overridden by the given certificate.
       
   494      *
       
   495      * @param alias the alias name
       
   496      * @param cert the certificate
       
   497      *
       
   498      * @exception KeyStoreException if the given alias already exists and does
       
   499      * not identify an entry containing a trusted certificate,
       
   500      * or this operation fails for some other reason.
       
   501      */
       
   502     public synchronized void engineSetCertificateEntry
       
   503         (String alias, Certificate cert) throws KeyStoreException {
       
   504 
       
   505         token.ensureValid();
       
   506         checkWrite();
       
   507 
       
   508         if (cert == null) {
       
   509             throw new KeyStoreException("invalid null certificate");
       
   510         }
       
   511 
       
   512         KeyStore.Entry entry = null;
       
   513         entry = new KeyStore.TrustedCertificateEntry(cert);
       
   514         engineSetEntry(alias, entry, null);
       
   515     }
       
   516 
       
   517     /**
       
   518      * Deletes the entry identified by the given alias from this keystore.
       
   519      *
       
   520      * @param alias the alias name
       
   521      *
       
   522      * @exception KeyStoreException if the entry cannot be removed.
       
   523      */
       
   524     public synchronized void engineDeleteEntry(String alias)
       
   525                 throws KeyStoreException {
       
   526         token.ensureValid();
       
   527 
       
   528         if (token.isWriteProtected()) {
       
   529             throw new KeyStoreException("token write-protected");
       
   530         }
       
   531         checkWrite();
       
   532         deleteEntry(alias);
       
   533     }
       
   534 
       
   535     /**
       
   536      * XXX - not sure whether to keep this
       
   537      */
       
   538     private boolean deleteEntry(String alias) throws KeyStoreException {
       
   539         AliasInfo aliasInfo = aliasMap.get(alias);
       
   540         if (aliasInfo != null) {
       
   541 
       
   542             aliasMap.remove(alias);
       
   543 
       
   544             try {
       
   545                 if (aliasInfo.type == ATTR_CLASS_CERT) {
       
   546                     // trusted certificate entry
       
   547                     return destroyCert(aliasInfo.id);
       
   548                 } else if (aliasInfo.type == ATTR_CLASS_PKEY) {
       
   549                     // private key entry
       
   550                     return destroyPkey(aliasInfo.id) &&
       
   551                                 destroyChain(aliasInfo.id);
       
   552                 } else if (aliasInfo.type == ATTR_CLASS_SKEY) {
       
   553                     // secret key entry
       
   554                     return destroySkey(alias);
       
   555                 } else {
       
   556                     throw new KeyStoreException("unexpected entry type");
       
   557                 }
       
   558             } catch (PKCS11Exception | CertificateException e) {
       
   559                 throw new KeyStoreException(e);
       
   560             }
       
   561         }
       
   562         return false;
       
   563     }
       
   564 
       
   565     /**
       
   566      * Lists all the alias names of this keystore.
       
   567      *
       
   568      * @return enumeration of the alias names
       
   569      */
       
   570     public synchronized Enumeration<String> engineAliases() {
       
   571         token.ensureValid();
       
   572 
       
   573         // don't want returned enumeration to iterate off actual keySet -
       
   574         // otherwise applications that iterate and modify the keystore
       
   575         // may run into concurrent modification problems
       
   576         return Collections.enumeration(new HashSet<String>(aliasMap.keySet()));
       
   577     }
       
   578 
       
   579     /**
       
   580      * Checks if the given alias exists in this keystore.
       
   581      *
       
   582      * @param alias the alias name
       
   583      *
       
   584      * @return true if the alias exists, false otherwise
       
   585      */
       
   586     public synchronized boolean engineContainsAlias(String alias) {
       
   587         token.ensureValid();
       
   588         return aliasMap.containsKey(alias);
       
   589     }
       
   590 
       
   591     /**
       
   592      * Retrieves the number of entries in this keystore.
       
   593      *
       
   594      * @return the number of entries in this keystore
       
   595      */
       
   596     public synchronized int engineSize() {
       
   597         token.ensureValid();
       
   598         return aliasMap.size();
       
   599     }
       
   600 
       
   601     /**
       
   602      * Returns true if the entry identified by the given alias
       
   603      * was created by a call to <code>setKeyEntry</code>,
       
   604      * or created by a call to <code>setEntry</code> with a
       
   605      * <code>PrivateKeyEntry</code> or a <code>SecretKeyEntry</code>.
       
   606      *
       
   607      * @param alias the alias for the keystore entry to be checked
       
   608      *
       
   609      * @return true if the entry identified by the given alias is a
       
   610      * key-related, false otherwise.
       
   611      */
       
   612     public synchronized boolean engineIsKeyEntry(String alias) {
       
   613         token.ensureValid();
       
   614 
       
   615         AliasInfo aliasInfo = aliasMap.get(alias);
       
   616         if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) {
       
   617             return false;
       
   618         }
       
   619         return true;
       
   620     }
       
   621 
       
   622     /**
       
   623      * Returns true if the entry identified by the given alias
       
   624      * was created by a call to <code>setCertificateEntry</code>,
       
   625      * or created by a call to <code>setEntry</code> with a
       
   626      * <code>TrustedCertificateEntry</code>.
       
   627      *
       
   628      * @param alias the alias for the keystore entry to be checked
       
   629      *
       
   630      * @return true if the entry identified by the given alias contains a
       
   631      * trusted certificate, false otherwise.
       
   632      */
       
   633     public synchronized boolean engineIsCertificateEntry(String alias) {
       
   634         token.ensureValid();
       
   635 
       
   636         AliasInfo aliasInfo = aliasMap.get(alias);
       
   637         if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_CERT) {
       
   638             return false;
       
   639         }
       
   640         return true;
       
   641     }
       
   642 
       
   643     /**
       
   644      * Returns the (alias) name of the first keystore entry whose certificate
       
   645      * matches the given certificate.
       
   646      *
       
   647      * <p>This method attempts to match the given certificate with each
       
   648      * keystore entry. If the entry being considered was
       
   649      * created by a call to <code>setCertificateEntry</code>,
       
   650      * or created by a call to <code>setEntry</code> with a
       
   651      * <code>TrustedCertificateEntry</code>,
       
   652      * then the given certificate is compared to that entry's certificate.
       
   653      *
       
   654      * <p> If the entry being considered was
       
   655      * created by a call to <code>setKeyEntry</code>,
       
   656      * or created by a call to <code>setEntry</code> with a
       
   657      * <code>PrivateKeyEntry</code>,
       
   658      * then the given certificate is compared to the first
       
   659      * element of that entry's certificate chain.
       
   660      *
       
   661      * @param cert the certificate to match with.
       
   662      *
       
   663      * @return the alias name of the first entry with matching certificate,
       
   664      * or null if no such entry exists in this keystore.
       
   665      */
       
   666     public synchronized String engineGetCertificateAlias(Certificate cert) {
       
   667         token.ensureValid();
       
   668         Enumeration<String> e = engineAliases();
       
   669         while (e.hasMoreElements()) {
       
   670             String alias = e.nextElement();
       
   671             Certificate tokenCert = engineGetCertificate(alias);
       
   672             if (tokenCert != null && tokenCert.equals(cert)) {
       
   673                 return alias;
       
   674             }
       
   675         }
       
   676         return null;
       
   677     }
       
   678 
       
   679     /**
       
   680      * engineStore currently is a No-op.
       
   681      * Entries are stored to the token during engineSetEntry
       
   682      *
       
   683      * @param stream this must be <code>null</code>
       
   684      * @param password this must be <code>null</code>
       
   685      */
       
   686     public synchronized void engineStore(OutputStream stream, char[] password)
       
   687         throws IOException, NoSuchAlgorithmException, CertificateException {
       
   688         token.ensureValid();
       
   689         if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {
       
   690             throw new IOException("output stream must be null");
       
   691         }
       
   692 
       
   693         if (password != null && !token.config.getKeyStoreCompatibilityMode()) {
       
   694             throw new IOException("password must be null");
       
   695         }
       
   696     }
       
   697 
       
   698     /**
       
   699      * engineStore currently is a No-op.
       
   700      * Entries are stored to the token during engineSetEntry
       
   701      *
       
   702      * @param param this must be <code>null</code>
       
   703      *
       
   704      * @exception IllegalArgumentException if the given
       
   705      *          <code>KeyStore.LoadStoreParameter</code>
       
   706      *          input is not <code>null</code>
       
   707      */
       
   708     public synchronized void engineStore(KeyStore.LoadStoreParameter param)
       
   709         throws IOException, NoSuchAlgorithmException, CertificateException {
       
   710         token.ensureValid();
       
   711         if (param != null) {
       
   712             throw new IllegalArgumentException
       
   713                 ("LoadStoreParameter must be null");
       
   714         }
       
   715     }
       
   716 
       
   717     /**
       
   718      * Loads the keystore.
       
   719      *
       
   720      * @param stream the input stream, which must be <code>null</code>
       
   721      * @param password the password used to unlock the keystore,
       
   722      *          or <code>null</code> if the token supports a
       
   723      *          CKF_PROTECTED_AUTHENTICATION_PATH
       
   724      *
       
   725      * @exception IOException if the given <code>stream</code> is not
       
   726      *          <code>null</code>, if the token supports a
       
   727      *          CKF_PROTECTED_AUTHENTICATION_PATH and a non-null
       
   728      *          password is given, of if the token login operation failed
       
   729      */
       
   730     public synchronized void engineLoad(InputStream stream, char[] password)
       
   731         throws IOException, NoSuchAlgorithmException, CertificateException {
       
   732 
       
   733         token.ensureValid();
       
   734 
       
   735         if (NSS_TEST) {
       
   736             ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);
       
   737         }
       
   738 
       
   739         if (stream != null && !token.config.getKeyStoreCompatibilityMode()) {
       
   740             throw new IOException("input stream must be null");
       
   741         }
       
   742 
       
   743         if (useSecmodTrust) {
       
   744             nssTrustType = Secmod.TrustType.ALL;
       
   745         }
       
   746 
       
   747         try {
       
   748             if (password == null) {
       
   749                 login(null);
       
   750             } else {
       
   751                 login(new PasswordCallbackHandler(password));
       
   752             }
       
   753         } catch(LoginException e) {
       
   754             Throwable cause = e.getCause();
       
   755             if (cause instanceof PKCS11Exception) {
       
   756                 PKCS11Exception pe = (PKCS11Exception) cause;
       
   757                 if (pe.getErrorCode() == CKR_PIN_INCORRECT) {
       
   758                     // if password is wrong, the cause of the IOException
       
   759                     // should be an UnrecoverableKeyException
       
   760                     throw new IOException("load failed",
       
   761                             new UnrecoverableKeyException().initCause(e));
       
   762                 }
       
   763             }
       
   764             throw new IOException("load failed", e);
       
   765         }
       
   766 
       
   767         try {
       
   768             if (mapLabels() == true) {
       
   769                 // CKA_LABELs are shared by multiple certs
       
   770                 writeDisabled = true;
       
   771             }
       
   772             if (debug != null) {
       
   773                 dumpTokenMap();
       
   774             }
       
   775         } catch (KeyStoreException | PKCS11Exception e) {
       
   776             throw new IOException("load failed", e);
       
   777         }
       
   778     }
       
   779 
       
   780     /**
       
   781      * Loads the keystore using the given
       
   782      * <code>KeyStore.LoadStoreParameter</code>.
       
   783      *
       
   784      * <p> The <code>LoadStoreParameter.getProtectionParameter()</code>
       
   785      * method is expected to return a <code>KeyStore.PasswordProtection</code>
       
   786      * object.  The password is retrieved from that object and used
       
   787      * to unlock the PKCS#11 token.
       
   788      *
       
   789      * <p> If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH
       
   790      * then the provided password must be <code>null</code>.
       
   791      *
       
   792      * @param param the <code>KeyStore.LoadStoreParameter</code>
       
   793      *
       
   794      * @exception IllegalArgumentException if the given
       
   795      *          <code>KeyStore.LoadStoreParameter</code> is <code>null</code>,
       
   796      *          or if that parameter returns a <code>null</code>
       
   797      *          <code>ProtectionParameter</code> object.
       
   798      *          input is not recognized
       
   799      * @exception IOException if the token supports a
       
   800      *          CKF_PROTECTED_AUTHENTICATION_PATH and the provided password
       
   801      *          is non-null, or if the token login operation fails
       
   802      */
       
   803     public synchronized void engineLoad(KeyStore.LoadStoreParameter param)
       
   804                 throws IOException, NoSuchAlgorithmException,
       
   805                 CertificateException {
       
   806 
       
   807         token.ensureValid();
       
   808 
       
   809         if (NSS_TEST) {
       
   810             ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);
       
   811         }
       
   812 
       
   813         // if caller wants to pass a NULL password,
       
   814         // force it to pass a non-NULL PasswordProtection that returns
       
   815         // a NULL password
       
   816 
       
   817         if (param == null) {
       
   818             throw new IllegalArgumentException
       
   819                         ("invalid null LoadStoreParameter");
       
   820         }
       
   821         if (useSecmodTrust) {
       
   822             if (param instanceof Secmod.KeyStoreLoadParameter) {
       
   823                 nssTrustType = ((Secmod.KeyStoreLoadParameter)param).getTrustType();
       
   824             } else {
       
   825                 nssTrustType = Secmod.TrustType.ALL;
       
   826             }
       
   827         }
       
   828 
       
   829         CallbackHandler handler;
       
   830         KeyStore.ProtectionParameter pp = param.getProtectionParameter();
       
   831         if (pp instanceof PasswordProtection) {
       
   832             char[] password = ((PasswordProtection)pp).getPassword();
       
   833             if (password == null) {
       
   834                 handler = null;
       
   835             } else {
       
   836                 handler = new PasswordCallbackHandler(password);
       
   837             }
       
   838         } else if (pp instanceof CallbackHandlerProtection) {
       
   839             handler = ((CallbackHandlerProtection)pp).getCallbackHandler();
       
   840         } else {
       
   841             throw new IllegalArgumentException
       
   842                         ("ProtectionParameter must be either " +
       
   843                         "PasswordProtection or CallbackHandlerProtection");
       
   844         }
       
   845 
       
   846         try {
       
   847             login(handler);
       
   848             if (mapLabels() == true) {
       
   849                 // CKA_LABELs are shared by multiple certs
       
   850                 writeDisabled = true;
       
   851             }
       
   852             if (debug != null) {
       
   853                 dumpTokenMap();
       
   854             }
       
   855         } catch (LoginException | KeyStoreException | PKCS11Exception e) {
       
   856             throw new IOException("load failed", e);
       
   857         }
       
   858     }
       
   859 
       
   860     private void login(CallbackHandler handler) throws LoginException {
       
   861         if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) {
       
   862             token.provider.login(null, handler);
       
   863         } else {
       
   864             // token supports protected authentication path
       
   865             // (external pin-pad, for example)
       
   866             if (handler != null &&
       
   867                 !token.config.getKeyStoreCompatibilityMode()) {
       
   868                 throw new LoginException("can not specify password if token " +
       
   869                                 "supports protected authentication path");
       
   870             }
       
   871 
       
   872             // must rely on application-set or default handler
       
   873             // if one is necessary
       
   874             token.provider.login(null, null);
       
   875         }
       
   876     }
       
   877 
       
   878     /**
       
   879      * Get a <code>KeyStore.Entry</code> for the specified alias
       
   880      *
       
   881      * @param alias get the <code>KeyStore.Entry</code> for this alias
       
   882      * @param protParam this must be <code>null</code>
       
   883      *
       
   884      * @return the <code>KeyStore.Entry</code> for the specified alias,
       
   885      *          or <code>null</code> if there is no such entry
       
   886      *
       
   887      * @exception KeyStoreException if the operation failed
       
   888      * @exception NoSuchAlgorithmException if the algorithm for recovering the
       
   889      *          entry cannot be found
       
   890      * @exception UnrecoverableEntryException if the specified
       
   891      *          <code>protParam</code> were insufficient or invalid
       
   892      *
       
   893      * @since 1.5
       
   894      */
       
   895     public synchronized KeyStore.Entry engineGetEntry(String alias,
       
   896                         KeyStore.ProtectionParameter protParam)
       
   897                 throws KeyStoreException, NoSuchAlgorithmException,
       
   898                 UnrecoverableEntryException {
       
   899 
       
   900         token.ensureValid();
       
   901 
       
   902         if (protParam != null &&
       
   903             protParam instanceof KeyStore.PasswordProtection &&
       
   904             ((KeyStore.PasswordProtection)protParam).getPassword() != null &&
       
   905             !token.config.getKeyStoreCompatibilityMode()) {
       
   906             throw new KeyStoreException("ProtectionParameter must be null");
       
   907         }
       
   908 
       
   909         AliasInfo aliasInfo = aliasMap.get(alias);
       
   910         if (aliasInfo == null) {
       
   911             if (debug != null) {
       
   912                 debug.println("engineGetEntry did not find alias [" +
       
   913                         alias +
       
   914                         "] in map");
       
   915             }
       
   916             return null;
       
   917         }
       
   918 
       
   919         Session session = null;
       
   920         try {
       
   921             session = token.getOpSession();
       
   922 
       
   923             if (aliasInfo.type == ATTR_CLASS_CERT) {
       
   924                 // trusted certificate entry
       
   925                 if (debug != null) {
       
   926                     debug.println("engineGetEntry found trusted cert entry");
       
   927                 }
       
   928                 return new KeyStore.TrustedCertificateEntry(aliasInfo.cert);
       
   929             } else if (aliasInfo.type == ATTR_CLASS_SKEY) {
       
   930                 // secret key entry
       
   931                 if (debug != null) {
       
   932                     debug.println("engineGetEntry found secret key entry");
       
   933                 }
       
   934 
       
   935                 THandle h = getTokenObject
       
   936                         (session, ATTR_CLASS_SKEY, null, aliasInfo.label);
       
   937                 if (h.type != ATTR_CLASS_SKEY) {
       
   938                     throw new KeyStoreException
       
   939                         ("expected but could not find secret key");
       
   940                 } else {
       
   941                     SecretKey skey = loadSkey(session, h.handle);
       
   942                     return new KeyStore.SecretKeyEntry(skey);
       
   943                 }
       
   944             } else {
       
   945                 // private key entry
       
   946                 if (debug != null) {
       
   947                     debug.println("engineGetEntry found private key entry");
       
   948                 }
       
   949 
       
   950                 THandle h = getTokenObject
       
   951                         (session, ATTR_CLASS_PKEY, aliasInfo.id, null);
       
   952                 if (h.type != ATTR_CLASS_PKEY) {
       
   953                     throw new KeyStoreException
       
   954                         ("expected but could not find private key");
       
   955                 } else {
       
   956                     PrivateKey pkey = loadPkey(session, h.handle);
       
   957                     Certificate[] chain = aliasInfo.chain;
       
   958                     if ((pkey != null) && (chain != null)) {
       
   959                         return new KeyStore.PrivateKeyEntry(pkey, chain);
       
   960                     } else {
       
   961                         if (debug != null) {
       
   962                             debug.println
       
   963                                 ("engineGetEntry got null cert chain or private key");
       
   964                         }
       
   965                     }
       
   966                 }
       
   967             }
       
   968             return null;
       
   969         } catch (PKCS11Exception pe) {
       
   970             throw new KeyStoreException(pe);
       
   971         } finally {
       
   972             token.releaseSession(session);
       
   973         }
       
   974     }
       
   975 
       
   976     /**
       
   977      * Save a <code>KeyStore.Entry</code> under the specified alias.
       
   978      *
       
   979      * <p> If an entry already exists for the specified alias,
       
   980      * it is overridden.
       
   981      *
       
   982      * <p> This KeyStore implementation only supports the standard
       
   983      * entry types, and only supports X509Certificates in
       
   984      * TrustedCertificateEntries.  Also, this implementation does not support
       
   985      * protecting entries using a different password
       
   986      * from the one used for token login.
       
   987      *
       
   988      * <p> Entries are immediately stored on the token.
       
   989      *
       
   990      * @param alias save the <code>KeyStore.Entry</code> under this alias
       
   991      * @param entry the <code>Entry</code> to save
       
   992      * @param protParam this must be <code>null</code>
       
   993      *
       
   994      * @exception KeyStoreException if this operation fails
       
   995      *
       
   996      * @since 1.5
       
   997      */
       
   998     public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
       
   999                         KeyStore.ProtectionParameter protParam)
       
  1000                 throws KeyStoreException {
       
  1001 
       
  1002         token.ensureValid();
       
  1003         checkWrite();
       
  1004 
       
  1005         if (protParam != null &&
       
  1006             protParam instanceof KeyStore.PasswordProtection &&
       
  1007             ((KeyStore.PasswordProtection)protParam).getPassword() != null &&
       
  1008             !token.config.getKeyStoreCompatibilityMode()) {
       
  1009             throw new KeyStoreException(new UnsupportedOperationException
       
  1010                                 ("ProtectionParameter must be null"));
       
  1011         }
       
  1012 
       
  1013         if (token.isWriteProtected()) {
       
  1014             throw new KeyStoreException("token write-protected");
       
  1015         }
       
  1016 
       
  1017         if (entry instanceof KeyStore.TrustedCertificateEntry) {
       
  1018 
       
  1019             if (useSecmodTrust == false) {
       
  1020                 // PKCS #11 does not allow app to modify trusted certs -
       
  1021                 throw new KeyStoreException(new UnsupportedOperationException
       
  1022                                     ("trusted certificates may only be set by " +
       
  1023                                     "token initialization application"));
       
  1024             }
       
  1025             Module module = token.provider.nssModule;
       
  1026             if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) {
       
  1027                 // XXX allow TRUSTANCHOR module
       
  1028                 throw new KeyStoreException("Trusted certificates can only be "
       
  1029                     + "added to the NSS KeyStore module");
       
  1030             }
       
  1031             Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate();
       
  1032             if (cert instanceof X509Certificate == false) {
       
  1033                 throw new KeyStoreException("Certificate must be an X509Certificate");
       
  1034             }
       
  1035             X509Certificate xcert = (X509Certificate)cert;
       
  1036             AliasInfo info = aliasMap.get(alias);
       
  1037             if (info != null) {
       
  1038                 // XXX try to update
       
  1039                 deleteEntry(alias);
       
  1040             }
       
  1041             try {
       
  1042                 storeCert(alias, xcert);
       
  1043                 module.setTrust(token, xcert);
       
  1044                 mapLabels();
       
  1045             } catch (PKCS11Exception | CertificateException e) {
       
  1046                 throw new KeyStoreException(e);
       
  1047             }
       
  1048 
       
  1049         } else {
       
  1050 
       
  1051             if (entry instanceof KeyStore.PrivateKeyEntry) {
       
  1052 
       
  1053                 PrivateKey key =
       
  1054                         ((KeyStore.PrivateKeyEntry)entry).getPrivateKey();
       
  1055                 if (!(key instanceof P11Key) &&
       
  1056                     !(key instanceof RSAPrivateKey) &&
       
  1057                     !(key instanceof DSAPrivateKey) &&
       
  1058                     !(key instanceof DHPrivateKey) &&
       
  1059                     !(key instanceof ECPrivateKey)) {
       
  1060                     throw new KeyStoreException("unsupported key type: " +
       
  1061                                                 key.getClass().getName());
       
  1062                 }
       
  1063 
       
  1064                 // only support X509Certificate chains
       
  1065                 Certificate[] chain =
       
  1066                     ((KeyStore.PrivateKeyEntry)entry).getCertificateChain();
       
  1067                 if (!(chain instanceof X509Certificate[])) {
       
  1068                     throw new KeyStoreException
       
  1069                         (new UnsupportedOperationException
       
  1070                                 ("unsupported certificate array type: " +
       
  1071                                 chain.getClass().getName()));
       
  1072                 }
       
  1073 
       
  1074                 try {
       
  1075                     boolean updatedAlias = false;
       
  1076                     Set<String> aliases = aliasMap.keySet();
       
  1077                     for (String oldAlias : aliases) {
       
  1078 
       
  1079                         // see if there's an existing entry with the same info
       
  1080 
       
  1081                         AliasInfo aliasInfo = aliasMap.get(oldAlias);
       
  1082                         if (aliasInfo.type == ATTR_CLASS_PKEY &&
       
  1083                             aliasInfo.cert.getPublicKey().equals
       
  1084                                         (chain[0].getPublicKey())) {
       
  1085 
       
  1086                             // found existing entry -
       
  1087                             // caller is renaming entry or updating cert chain
       
  1088                             //
       
  1089                             // set new CKA_LABEL/CKA_ID
       
  1090                             // and update certs if necessary
       
  1091 
       
  1092                             updatePkey(alias,
       
  1093                                         aliasInfo.id,
       
  1094                                         (X509Certificate[])chain,
       
  1095                                         !aliasInfo.cert.equals(chain[0]));
       
  1096                             updatedAlias = true;
       
  1097                             break;
       
  1098                         }
       
  1099                     }
       
  1100 
       
  1101                     if (!updatedAlias) {
       
  1102                         // caller adding new entry
       
  1103                         engineDeleteEntry(alias);
       
  1104                         storePkey(alias, (KeyStore.PrivateKeyEntry)entry);
       
  1105                     }
       
  1106 
       
  1107                 } catch (PKCS11Exception | CertificateException pe) {
       
  1108                     throw new KeyStoreException(pe);
       
  1109                 }
       
  1110 
       
  1111             } else if (entry instanceof KeyStore.SecretKeyEntry) {
       
  1112 
       
  1113                 KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;
       
  1114                 SecretKey skey = ske.getSecretKey();
       
  1115 
       
  1116                 try {
       
  1117                     // first check if the key already exists
       
  1118                     AliasInfo aliasInfo = aliasMap.get(alias);
       
  1119 
       
  1120                     if (aliasInfo != null) {
       
  1121                         engineDeleteEntry(alias);
       
  1122                     }
       
  1123                     storeSkey(alias, ske);
       
  1124 
       
  1125                 } catch (PKCS11Exception pe) {
       
  1126                     throw new KeyStoreException(pe);
       
  1127                 }
       
  1128 
       
  1129             } else {
       
  1130                 throw new KeyStoreException(new UnsupportedOperationException
       
  1131                     ("unsupported entry type: " + entry.getClass().getName()));
       
  1132             }
       
  1133 
       
  1134             try {
       
  1135 
       
  1136                 // XXX  NSS does not write out the CKA_ID we pass to them
       
  1137                 //
       
  1138                 // therefore we must re-map labels
       
  1139                 // (can not simply update aliasMap)
       
  1140 
       
  1141                 mapLabels();
       
  1142                 if (debug != null) {
       
  1143                     dumpTokenMap();
       
  1144                 }
       
  1145             } catch (PKCS11Exception | CertificateException pe) {
       
  1146                 throw new KeyStoreException(pe);
       
  1147             }
       
  1148         }
       
  1149 
       
  1150         if (debug != null) {
       
  1151             debug.println
       
  1152                 ("engineSetEntry added new entry for [" +
       
  1153                 alias +
       
  1154                 "] to token");
       
  1155         }
       
  1156     }
       
  1157 
       
  1158     /**
       
  1159      * Determines if the keystore <code>Entry</code> for the specified
       
  1160      * <code>alias</code> is an instance or subclass of the specified
       
  1161      * <code>entryClass</code>.
       
  1162      *
       
  1163      * @param alias the alias name
       
  1164      * @param entryClass the entry class
       
  1165      *
       
  1166      * @return true if the keystore <code>Entry</code> for the specified
       
  1167      *          <code>alias</code> is an instance or subclass of the
       
  1168      *          specified <code>entryClass</code>, false otherwise
       
  1169      */
       
  1170     public synchronized boolean engineEntryInstanceOf
       
  1171                 (String alias, Class<? extends KeyStore.Entry> entryClass) {
       
  1172         token.ensureValid();
       
  1173         return super.engineEntryInstanceOf(alias, entryClass);
       
  1174     }
       
  1175 
       
  1176     private X509Certificate loadCert(Session session, long oHandle)
       
  1177                 throws PKCS11Exception, CertificateException {
       
  1178 
       
  1179         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[]
       
  1180                         { new CK_ATTRIBUTE(CKA_VALUE) };
       
  1181         token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1182 
       
  1183         byte[] bytes = attrs[0].getByteArray();
       
  1184         if (bytes == null) {
       
  1185             throw new CertificateException
       
  1186                         ("unexpectedly retrieved null byte array");
       
  1187         }
       
  1188         CertificateFactory cf = CertificateFactory.getInstance("X.509");
       
  1189         return (X509Certificate)cf.generateCertificate
       
  1190                         (new ByteArrayInputStream(bytes));
       
  1191     }
       
  1192 
       
  1193     private X509Certificate[] loadChain(Session session,
       
  1194                                         X509Certificate endCert)
       
  1195                 throws PKCS11Exception, CertificateException {
       
  1196 
       
  1197         ArrayList<X509Certificate> lChain = null;
       
  1198 
       
  1199         if (endCert.getSubjectX500Principal().equals
       
  1200             (endCert.getIssuerX500Principal())) {
       
  1201             // self signed
       
  1202             return new X509Certificate[] { endCert };
       
  1203         } else {
       
  1204             lChain = new ArrayList<X509Certificate>();
       
  1205             lChain.add(endCert);
       
  1206         }
       
  1207 
       
  1208         // try loading remaining certs in chain by following
       
  1209         // issuer->subject links
       
  1210 
       
  1211         X509Certificate next = endCert;
       
  1212         while (true) {
       
  1213             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1214                         ATTR_TOKEN_TRUE,
       
  1215                         ATTR_CLASS_CERT,
       
  1216                         new CK_ATTRIBUTE(CKA_SUBJECT,
       
  1217                                 next.getIssuerX500Principal().getEncoded()) };
       
  1218             long[] ch = findObjects(session, attrs);
       
  1219 
       
  1220             if (ch == null || ch.length == 0) {
       
  1221                 // done
       
  1222                 break;
       
  1223             } else {
       
  1224                 // if more than one found, use first
       
  1225                 if (debug != null && ch.length > 1) {
       
  1226                     debug.println("engineGetEntry found " +
       
  1227                                 ch.length +
       
  1228                                 " certificate entries for subject [" +
       
  1229                                 next.getIssuerX500Principal().toString() +
       
  1230                                 "] in token - using first entry");
       
  1231                 }
       
  1232 
       
  1233                 next = loadCert(session, ch[0]);
       
  1234                 lChain.add(next);
       
  1235                 if (next.getSubjectX500Principal().equals
       
  1236                     (next.getIssuerX500Principal())) {
       
  1237                     // self signed
       
  1238                     break;
       
  1239                 }
       
  1240             }
       
  1241         }
       
  1242 
       
  1243         return lChain.toArray(new X509Certificate[lChain.size()]);
       
  1244     }
       
  1245 
       
  1246     private SecretKey loadSkey(Session session, long oHandle)
       
  1247                 throws PKCS11Exception {
       
  1248 
       
  1249         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1250                         new CK_ATTRIBUTE(CKA_KEY_TYPE) };
       
  1251         token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1252         long kType = attrs[0].getLong();
       
  1253 
       
  1254         String keyType = null;
       
  1255         int keyLength = -1;
       
  1256 
       
  1257         // XXX NSS mangles the stored key type for secret key token objects
       
  1258 
       
  1259         if (kType == CKK_DES || kType == CKK_DES3) {
       
  1260             if (kType == CKK_DES) {
       
  1261                 keyType = "DES";
       
  1262                 keyLength = 64;
       
  1263             } else if (kType == CKK_DES3) {
       
  1264                 keyType = "DESede";
       
  1265                 keyLength = 192;
       
  1266             }
       
  1267         } else {
       
  1268             if (kType == CKK_AES) {
       
  1269                 keyType = "AES";
       
  1270             } else if (kType == CKK_BLOWFISH) {
       
  1271                 keyType = "Blowfish";
       
  1272             } else if (kType == CKK_RC4) {
       
  1273                 keyType = "ARCFOUR";
       
  1274             } else {
       
  1275                 if (debug != null) {
       
  1276                     debug.println("unknown key type [" +
       
  1277                                 kType +
       
  1278                                 "] - using 'Generic Secret'");
       
  1279                 }
       
  1280                 keyType = "Generic Secret";
       
  1281             }
       
  1282 
       
  1283             // XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID?
       
  1284             if (NSS_TEST) {
       
  1285                 keyLength = 128;
       
  1286             } else {
       
  1287                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) };
       
  1288                 token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1289                 keyLength = (int)attrs[0].getLong();
       
  1290             }
       
  1291         }
       
  1292 
       
  1293         return P11Key.secretKey(session, oHandle, keyType, keyLength, null);
       
  1294     }
       
  1295 
       
  1296     private PrivateKey loadPkey(Session session, long oHandle)
       
  1297         throws PKCS11Exception, KeyStoreException {
       
  1298 
       
  1299         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1300                         new CK_ATTRIBUTE(CKA_KEY_TYPE) };
       
  1301         token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1302         long kType = attrs[0].getLong();
       
  1303         String keyType = null;
       
  1304         int keyLength = 0;
       
  1305 
       
  1306         if (kType == CKK_RSA) {
       
  1307 
       
  1308             keyType = "RSA";
       
  1309 
       
  1310             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) };
       
  1311             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1312             BigInteger modulus = attrs[0].getBigInteger();
       
  1313             keyLength = modulus.bitLength();
       
  1314 
       
  1315             // This check will combine our "don't care" values here
       
  1316             // with the system-wide min/max values.
       
  1317             try {
       
  1318                 RSAKeyFactory.checkKeyLengths(keyLength, null,
       
  1319                     -1, Integer.MAX_VALUE);
       
  1320             } catch (InvalidKeyException e) {
       
  1321                 throw new KeyStoreException(e.getMessage());
       
  1322             }
       
  1323 
       
  1324             return P11Key.privateKey(session,
       
  1325                                 oHandle,
       
  1326                                 keyType,
       
  1327                                 keyLength,
       
  1328                                 null);
       
  1329 
       
  1330         } else if (kType == CKK_DSA) {
       
  1331 
       
  1332             keyType = "DSA";
       
  1333 
       
  1334             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
       
  1335             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1336             BigInteger prime = attrs[0].getBigInteger();
       
  1337             keyLength = prime.bitLength();
       
  1338 
       
  1339             return P11Key.privateKey(session,
       
  1340                                 oHandle,
       
  1341                                 keyType,
       
  1342                                 keyLength,
       
  1343                                 null);
       
  1344 
       
  1345         } else if (kType == CKK_DH) {
       
  1346 
       
  1347             keyType = "DH";
       
  1348 
       
  1349             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
       
  1350             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1351             BigInteger prime = attrs[0].getBigInteger();
       
  1352             keyLength = prime.bitLength();
       
  1353 
       
  1354             return P11Key.privateKey(session,
       
  1355                                 oHandle,
       
  1356                                 keyType,
       
  1357                                 keyLength,
       
  1358                                 null);
       
  1359 
       
  1360         } else if (kType == CKK_EC) {
       
  1361 
       
  1362             attrs = new CK_ATTRIBUTE[] {
       
  1363                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
       
  1364             };
       
  1365             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
       
  1366             byte[] encodedParams = attrs[0].getByteArray();
       
  1367             try {
       
  1368                 ECParameterSpec params =
       
  1369                     ECUtil.getECParameterSpec(null, encodedParams);
       
  1370                 keyLength = params.getCurve().getField().getFieldSize();
       
  1371             } catch (IOException e) {
       
  1372                 // we do not want to accept key with unsupported parameters
       
  1373                 throw new KeyStoreException("Unsupported parameters", e);
       
  1374             }
       
  1375 
       
  1376             return P11Key.privateKey(session, oHandle, "EC", keyLength, null);
       
  1377 
       
  1378         } else {
       
  1379             if (debug != null) {
       
  1380                 debug.println("unknown key type [" + kType + "]");
       
  1381             }
       
  1382             throw new KeyStoreException("unknown key type");
       
  1383         }
       
  1384     }
       
  1385 
       
  1386 
       
  1387     /**
       
  1388      * XXX  On ibutton, when you C_SetAttribute(CKA_ID) for a private key
       
  1389      *      it not only changes the CKA_ID of the private key,
       
  1390      *      it changes the CKA_ID of the corresponding cert too.
       
  1391      *      And vice versa.
       
  1392      *
       
  1393      * XXX  On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID)
       
  1394      *      for a private key, and then try to delete the corresponding cert.
       
  1395      *      So this code reverses the order.
       
  1396      *      After the cert is first destroyed (if necessary),
       
  1397      *      then the CKA_ID of the private key can be changed successfully.
       
  1398      *
       
  1399      * @param replaceCert if true, then caller is updating alias info for
       
  1400      *                  existing cert (only update CKA_ID/CKA_LABEL).
       
  1401      *                  if false, then caller is updating cert chain
       
  1402      *                  (delete old end cert and add new chain).
       
  1403      */
       
  1404     private void updatePkey(String alias,
       
  1405                         byte[] cka_id,
       
  1406                         X509Certificate[] chain,
       
  1407                         boolean replaceCert) throws
       
  1408                 KeyStoreException, CertificateException, PKCS11Exception {
       
  1409 
       
  1410         // XXX
       
  1411         //
       
  1412         // always set replaceCert to true
       
  1413         //
       
  1414         // NSS does not allow resetting of CKA_LABEL on an existing cert
       
  1415         // (C_SetAttribute call succeeds, but is ignored)
       
  1416 
       
  1417         replaceCert = true;
       
  1418 
       
  1419         Session session = null;
       
  1420         try {
       
  1421             session = token.getOpSession();
       
  1422 
       
  1423             // first get private key object handle and hang onto it
       
  1424 
       
  1425             THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
       
  1426             long pKeyHandle;
       
  1427             if (h.type == ATTR_CLASS_PKEY) {
       
  1428                 pKeyHandle = h.handle;
       
  1429             } else {
       
  1430                 throw new KeyStoreException
       
  1431                         ("expected but could not find private key " +
       
  1432                         "with CKA_ID " +
       
  1433                         getID(cka_id));
       
  1434             }
       
  1435 
       
  1436             // next find existing end entity cert
       
  1437 
       
  1438             h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
       
  1439             if (h.type != ATTR_CLASS_CERT) {
       
  1440                 throw new KeyStoreException
       
  1441                         ("expected but could not find certificate " +
       
  1442                         "with CKA_ID " +
       
  1443                         getID(cka_id));
       
  1444             } else {
       
  1445                 if (replaceCert) {
       
  1446                     // replacing existing cert and chain
       
  1447                     destroyChain(cka_id);
       
  1448                 } else {
       
  1449                     // renaming alias for existing cert
       
  1450                     CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1451                         new CK_ATTRIBUTE(CKA_LABEL, alias),
       
  1452                         new CK_ATTRIBUTE(CKA_ID, alias) };
       
  1453                     token.p11.C_SetAttributeValue
       
  1454                         (session.id(), h.handle, attrs);
       
  1455                 }
       
  1456             }
       
  1457 
       
  1458             // add new chain
       
  1459 
       
  1460             if (replaceCert) {
       
  1461                 // add all certs in chain
       
  1462                 storeChain(alias, chain);
       
  1463             } else {
       
  1464                 // already updated alias info for existing end cert -
       
  1465                 // just update CA certs
       
  1466                 storeCaCerts(chain, 1);
       
  1467             }
       
  1468 
       
  1469             // finally update CKA_ID for private key
       
  1470             //
       
  1471             // ibutton may have already done this (that is ok)
       
  1472 
       
  1473             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1474                                 new CK_ATTRIBUTE(CKA_ID, alias) };
       
  1475             token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs);
       
  1476 
       
  1477             if (debug != null) {
       
  1478                 debug.println("updatePkey set new alias [" +
       
  1479                                 alias +
       
  1480                                 "] for private key entry");
       
  1481             }
       
  1482         } finally {
       
  1483             token.releaseSession(session);
       
  1484         }
       
  1485     }
       
  1486 
       
  1487     private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)
       
  1488                 throws PKCS11Exception {
       
  1489 
       
  1490         // if token key, update alias.
       
  1491         // if session key, convert to token key.
       
  1492 
       
  1493         Session session = null;
       
  1494         try {
       
  1495             session = token.getOpSession();
       
  1496             if (key.tokenObject == true) {
       
  1497 
       
  1498                 // token key - set new CKA_ID
       
  1499 
       
  1500                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1501                                 new CK_ATTRIBUTE(CKA_ID, alias) };
       
  1502                 token.p11.C_SetAttributeValue
       
  1503                                 (session.id(), key.keyID, attrs);
       
  1504                 if (debug != null) {
       
  1505                     debug.println("updateP11Pkey set new alias [" +
       
  1506                                 alias +
       
  1507                                 "] for key entry");
       
  1508                 }
       
  1509             } else {
       
  1510 
       
  1511                 // session key - convert to token key and set CKA_ID
       
  1512 
       
  1513                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1514                     ATTR_TOKEN_TRUE,
       
  1515                     new CK_ATTRIBUTE(CKA_ID, alias),
       
  1516                 };
       
  1517                 if (attribute != null) {
       
  1518                     attrs = addAttribute(attrs, attribute);
       
  1519                 }
       
  1520                 token.p11.C_CopyObject(session.id(), key.keyID, attrs);
       
  1521                 if (debug != null) {
       
  1522                     debug.println("updateP11Pkey copied private session key " +
       
  1523                                 "for [" +
       
  1524                                 alias +
       
  1525                                 "] to token entry");
       
  1526                 }
       
  1527             }
       
  1528         } finally {
       
  1529             token.releaseSession(session);
       
  1530         }
       
  1531     }
       
  1532 
       
  1533     private void storeCert(String alias, X509Certificate cert)
       
  1534                 throws PKCS11Exception, CertificateException {
       
  1535 
       
  1536         ArrayList<CK_ATTRIBUTE> attrList = new ArrayList<CK_ATTRIBUTE>();
       
  1537         attrList.add(ATTR_TOKEN_TRUE);
       
  1538         attrList.add(ATTR_CLASS_CERT);
       
  1539         attrList.add(ATTR_X509_CERT_TYPE);
       
  1540         attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT,
       
  1541                                 cert.getSubjectX500Principal().getEncoded()));
       
  1542         attrList.add(new CK_ATTRIBUTE(CKA_ISSUER,
       
  1543                                 cert.getIssuerX500Principal().getEncoded()));
       
  1544         attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER,
       
  1545                                 cert.getSerialNumber().toByteArray()));
       
  1546         attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded()));
       
  1547 
       
  1548         if (alias != null) {
       
  1549             attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias));
       
  1550             attrList.add(new CK_ATTRIBUTE(CKA_ID, alias));
       
  1551         } else {
       
  1552             // ibutton requires something to be set
       
  1553             // - alias must be unique
       
  1554             attrList.add(new CK_ATTRIBUTE(CKA_ID,
       
  1555                         getID(cert.getSubjectX500Principal().getName
       
  1556                                         (X500Principal.CANONICAL), cert)));
       
  1557         }
       
  1558 
       
  1559         Session session = null;
       
  1560         try {
       
  1561             session = token.getOpSession();
       
  1562             token.p11.C_CreateObject(session.id(),
       
  1563                         attrList.toArray(new CK_ATTRIBUTE[attrList.size()]));
       
  1564         } finally {
       
  1565             token.releaseSession(session);
       
  1566         }
       
  1567     }
       
  1568 
       
  1569     private void storeChain(String alias, X509Certificate[] chain)
       
  1570                 throws PKCS11Exception, CertificateException {
       
  1571 
       
  1572         // add new chain
       
  1573         //
       
  1574         // end cert has CKA_LABEL and CKA_ID set to alias.
       
  1575         // other certs in chain have neither set.
       
  1576 
       
  1577         storeCert(alias, chain[0]);
       
  1578         storeCaCerts(chain, 1);
       
  1579     }
       
  1580 
       
  1581     private void storeCaCerts(X509Certificate[] chain, int start)
       
  1582                 throws PKCS11Exception, CertificateException {
       
  1583 
       
  1584         // do not add duplicate CA cert if already in token
       
  1585         //
       
  1586         // XXX   ibutton stores duplicate CA certs, NSS does not
       
  1587 
       
  1588         Session session = null;
       
  1589         HashSet<X509Certificate> cacerts = new HashSet<X509Certificate>();
       
  1590         try {
       
  1591             session = token.getOpSession();
       
  1592             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1593                         ATTR_TOKEN_TRUE,
       
  1594                         ATTR_CLASS_CERT };
       
  1595             long[] handles = findObjects(session, attrs);
       
  1596 
       
  1597             // load certs currently on the token
       
  1598             for (long handle : handles) {
       
  1599                 cacerts.add(loadCert(session, handle));
       
  1600             }
       
  1601         } finally {
       
  1602             token.releaseSession(session);
       
  1603         }
       
  1604 
       
  1605         for (int i = start; i < chain.length; i++) {
       
  1606             if (!cacerts.contains(chain[i])) {
       
  1607                 storeCert(null, chain[i]);
       
  1608             } else if (debug != null) {
       
  1609                 debug.println("ignoring duplicate CA cert for [" +
       
  1610                         chain[i].getSubjectX500Principal() +
       
  1611                         "]");
       
  1612             }
       
  1613         }
       
  1614     }
       
  1615 
       
  1616     private void storeSkey(String alias, KeyStore.SecretKeyEntry ske)
       
  1617                 throws PKCS11Exception, KeyStoreException {
       
  1618 
       
  1619         SecretKey skey = ske.getSecretKey();
       
  1620         // No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since
       
  1621         // they are handled in P11SecretKeyFactory.createKey() method.
       
  1622         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  1623             ATTR_SKEY_TOKEN_TRUE,
       
  1624             ATTR_PRIVATE_TRUE,
       
  1625             new CK_ATTRIBUTE(CKA_LABEL, alias),
       
  1626         };
       
  1627         try {
       
  1628             P11SecretKeyFactory.convertKey(token, skey, null, attrs);
       
  1629         } catch (InvalidKeyException ike) {
       
  1630             // re-throw KeyStoreException to match javadoc
       
  1631             throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);
       
  1632         }
       
  1633 
       
  1634         // update global alias map
       
  1635         aliasMap.put(alias, new AliasInfo(alias));
       
  1636 
       
  1637         if (debug != null) {
       
  1638             debug.println("storeSkey created token secret key for [" +
       
  1639                           alias + "]");
       
  1640         }
       
  1641     }
       
  1642 
       
  1643     private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) {
       
  1644         int n = attrs.length;
       
  1645         CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1];
       
  1646         System.arraycopy(attrs, 0, newAttrs, 0, n);
       
  1647         newAttrs[n] = attr;
       
  1648         return newAttrs;
       
  1649     }
       
  1650 
       
  1651     private void storePkey(String alias, KeyStore.PrivateKeyEntry pke)
       
  1652         throws PKCS11Exception, CertificateException, KeyStoreException  {
       
  1653 
       
  1654         PrivateKey key = pke.getPrivateKey();
       
  1655         CK_ATTRIBUTE[] attrs = null;
       
  1656 
       
  1657         // If the key is a token object on this token, update it instead
       
  1658         // of creating a duplicate key object.
       
  1659         // Otherwise, treat a P11Key like any other key, if it is extractable.
       
  1660         if (key instanceof P11Key) {
       
  1661             P11Key p11Key = (P11Key)key;
       
  1662             if (p11Key.tokenObject && (p11Key.token == this.token)) {
       
  1663                 updateP11Pkey(alias, null, p11Key);
       
  1664                 storeChain(alias, (X509Certificate[])pke.getCertificateChain());
       
  1665                 return;
       
  1666             }
       
  1667         }
       
  1668 
       
  1669         boolean useNDB = token.config.getNssNetscapeDbWorkaround();
       
  1670         PublicKey publicKey = pke.getCertificate().getPublicKey();
       
  1671 
       
  1672         if (key instanceof RSAPrivateKey) {
       
  1673 
       
  1674             X509Certificate cert = (X509Certificate)pke.getCertificate();
       
  1675             attrs = getRsaPrivKeyAttrs
       
  1676                 (alias, (RSAPrivateKey)key, cert.getSubjectX500Principal());
       
  1677 
       
  1678         } else if (key instanceof DSAPrivateKey) {
       
  1679 
       
  1680             DSAPrivateKey dsaKey = (DSAPrivateKey)key;
       
  1681 
       
  1682             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
       
  1683             if (idAttrs[0] == null) {
       
  1684                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
       
  1685             }
       
  1686 
       
  1687             attrs = new CK_ATTRIBUTE[] {
       
  1688                 ATTR_TOKEN_TRUE,
       
  1689                 ATTR_CLASS_PKEY,
       
  1690                 ATTR_PRIVATE_TRUE,
       
  1691                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),
       
  1692                 idAttrs[0],
       
  1693                 new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()),
       
  1694                 new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()),
       
  1695                 new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()),
       
  1696                 new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()),
       
  1697             };
       
  1698             if (idAttrs[1] != null) {
       
  1699                 attrs = addAttribute(attrs, idAttrs[1]);
       
  1700             }
       
  1701 
       
  1702             attrs = token.getAttributes
       
  1703                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs);
       
  1704 
       
  1705             if (debug != null) {
       
  1706                 debug.println("storePkey created DSA template");
       
  1707             }
       
  1708 
       
  1709         } else if (key instanceof DHPrivateKey) {
       
  1710 
       
  1711             DHPrivateKey dhKey = (DHPrivateKey)key;
       
  1712 
       
  1713             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
       
  1714             if (idAttrs[0] == null) {
       
  1715                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
       
  1716             }
       
  1717 
       
  1718             attrs = new CK_ATTRIBUTE[] {
       
  1719                 ATTR_TOKEN_TRUE,
       
  1720                 ATTR_CLASS_PKEY,
       
  1721                 ATTR_PRIVATE_TRUE,
       
  1722                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),
       
  1723                 idAttrs[0],
       
  1724                 new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()),
       
  1725                 new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()),
       
  1726                 new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()),
       
  1727             };
       
  1728             if (idAttrs[1] != null) {
       
  1729                 attrs = addAttribute(attrs, idAttrs[1]);
       
  1730             }
       
  1731 
       
  1732             attrs = token.getAttributes
       
  1733                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs);
       
  1734 
       
  1735         } else if (key instanceof ECPrivateKey) {
       
  1736 
       
  1737             ECPrivateKey ecKey = (ECPrivateKey)key;
       
  1738 
       
  1739             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
       
  1740             if (idAttrs[0] == null) {
       
  1741                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
       
  1742             }
       
  1743 
       
  1744             byte[] encodedParams =
       
  1745                 ECUtil.encodeECParameterSpec(null, ecKey.getParams());
       
  1746             attrs = new CK_ATTRIBUTE[] {
       
  1747                 ATTR_TOKEN_TRUE,
       
  1748                 ATTR_CLASS_PKEY,
       
  1749                 ATTR_PRIVATE_TRUE,
       
  1750                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
       
  1751                 idAttrs[0],
       
  1752                 new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()),
       
  1753                 new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
       
  1754             };
       
  1755             if (idAttrs[1] != null) {
       
  1756                 attrs = addAttribute(attrs, idAttrs[1]);
       
  1757             }
       
  1758 
       
  1759             attrs = token.getAttributes
       
  1760                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs);
       
  1761 
       
  1762             if (debug != null) {
       
  1763                 debug.println("storePkey created EC template");
       
  1764             }
       
  1765 
       
  1766         } else if (key instanceof P11Key) {
       
  1767             // sensitive/non-extractable P11Key
       
  1768             P11Key p11Key = (P11Key)key;
       
  1769             if (p11Key.token != this.token) {
       
  1770                 throw new KeyStoreException
       
  1771                     ("Cannot move sensitive keys across tokens");
       
  1772             }
       
  1773             CK_ATTRIBUTE netscapeDB = null;
       
  1774             if (useNDB) {
       
  1775                 // Note that this currently fails due to an NSS bug.
       
  1776                 // They do not allow the CKA_NETSCAPE_DB attribute to be
       
  1777                 // specified during C_CopyObject() and fail with
       
  1778                 // CKR_ATTRIBUTE_READ_ONLY.
       
  1779                 // But if we did not specify it, they would fail with
       
  1780                 // CKA_TEMPLATE_INCOMPLETE, so leave this code in here.
       
  1781                 CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true);
       
  1782                 netscapeDB = idAttrs[1];
       
  1783             }
       
  1784             // Update the key object.
       
  1785             updateP11Pkey(alias, netscapeDB, p11Key);
       
  1786             storeChain(alias, (X509Certificate[])pke.getCertificateChain());
       
  1787             return;
       
  1788 
       
  1789         } else {
       
  1790             throw new KeyStoreException("unsupported key type: " + key);
       
  1791         }
       
  1792 
       
  1793         Session session = null;
       
  1794         try {
       
  1795             session = token.getOpSession();
       
  1796 
       
  1797             // create private key entry
       
  1798             token.p11.C_CreateObject(session.id(), attrs);
       
  1799             if (debug != null) {
       
  1800                 debug.println("storePkey created token key for [" +
       
  1801                                 alias +
       
  1802                                 "]");
       
  1803             }
       
  1804         } finally {
       
  1805             token.releaseSession(session);
       
  1806         }
       
  1807 
       
  1808         storeChain(alias, (X509Certificate[])pke.getCertificateChain());
       
  1809     }
       
  1810 
       
  1811     private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias,
       
  1812                                 RSAPrivateKey key,
       
  1813                                 X500Principal subject) throws PKCS11Exception {
       
  1814 
       
  1815         // subject is currently ignored - could be used to set CKA_SUBJECT
       
  1816 
       
  1817         CK_ATTRIBUTE[] attrs = null;
       
  1818         if (key instanceof RSAPrivateCrtKey) {
       
  1819 
       
  1820             if (debug != null) {
       
  1821                 debug.println("creating RSAPrivateCrtKey attrs");
       
  1822             }
       
  1823 
       
  1824             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key;
       
  1825 
       
  1826             attrs = new CK_ATTRIBUTE[] {
       
  1827                 ATTR_TOKEN_TRUE,
       
  1828                 ATTR_CLASS_PKEY,
       
  1829                 ATTR_PRIVATE_TRUE,
       
  1830                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
       
  1831                 new CK_ATTRIBUTE(CKA_ID, alias),
       
  1832                 new CK_ATTRIBUTE(CKA_MODULUS,
       
  1833                                 rsaKey.getModulus()),
       
  1834                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
       
  1835                                 rsaKey.getPrivateExponent()),
       
  1836                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT,
       
  1837                                 rsaKey.getPublicExponent()),
       
  1838                 new CK_ATTRIBUTE(CKA_PRIME_1,
       
  1839                                 rsaKey.getPrimeP()),
       
  1840                 new CK_ATTRIBUTE(CKA_PRIME_2,
       
  1841                                 rsaKey.getPrimeQ()),
       
  1842                 new CK_ATTRIBUTE(CKA_EXPONENT_1,
       
  1843                                 rsaKey.getPrimeExponentP()),
       
  1844                 new CK_ATTRIBUTE(CKA_EXPONENT_2,
       
  1845                                 rsaKey.getPrimeExponentQ()),
       
  1846                 new CK_ATTRIBUTE(CKA_COEFFICIENT,
       
  1847                                 rsaKey.getCrtCoefficient()) };
       
  1848             attrs = token.getAttributes
       
  1849                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
       
  1850 
       
  1851         } else {
       
  1852 
       
  1853             if (debug != null) {
       
  1854                 debug.println("creating RSAPrivateKey attrs");
       
  1855             }
       
  1856 
       
  1857             RSAPrivateKey rsaKey = key;
       
  1858 
       
  1859             attrs = new CK_ATTRIBUTE[] {
       
  1860                 ATTR_TOKEN_TRUE,
       
  1861                 ATTR_CLASS_PKEY,
       
  1862                 ATTR_PRIVATE_TRUE,
       
  1863                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
       
  1864                 new CK_ATTRIBUTE(CKA_ID, alias),
       
  1865                 new CK_ATTRIBUTE(CKA_MODULUS,
       
  1866                                 rsaKey.getModulus()),
       
  1867                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
       
  1868                                 rsaKey.getPrivateExponent()) };
       
  1869             attrs = token.getAttributes
       
  1870                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
       
  1871         }
       
  1872 
       
  1873         return attrs;
       
  1874     }
       
  1875 
       
  1876     /**
       
  1877      * Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be
       
  1878      * used for this private key. It uses the same algorithm to calculate the
       
  1879      * values as NSS. The public and private keys MUST match for the result to
       
  1880      * be correct.
       
  1881      *
       
  1882      * It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB
       
  1883      * at index 1. The boolean flags determine what is to be calculated.
       
  1884      * If false or if we could not calculate the value, that element is null.
       
  1885      *
       
  1886      * NOTE that we currently do not use the CKA_ID value calculated by this
       
  1887      * method.
       
  1888      */
       
  1889     private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey,
       
  1890             PublicKey publicKey, boolean id, boolean netscapeDb) {
       
  1891         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2];
       
  1892         if ((id || netscapeDb) == false) {
       
  1893             return attrs;
       
  1894         }
       
  1895         String alg = privateKey.getAlgorithm();
       
  1896         if (id && alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {
       
  1897             // CKA_NETSCAPE_DB not needed for RSA public keys
       
  1898             BigInteger n = ((RSAPublicKey)publicKey).getModulus();
       
  1899             attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));
       
  1900         } else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) {
       
  1901             BigInteger y = ((DSAPublicKey)publicKey).getY();
       
  1902             if (id) {
       
  1903                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
       
  1904             }
       
  1905             if (netscapeDb) {
       
  1906                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
       
  1907             }
       
  1908         } else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) {
       
  1909             BigInteger y = ((DHPublicKey)publicKey).getY();
       
  1910             if (id) {
       
  1911                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
       
  1912             }
       
  1913             if (netscapeDb) {
       
  1914                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
       
  1915             }
       
  1916         } else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) {
       
  1917             ECPublicKey ecPub = (ECPublicKey)publicKey;
       
  1918             ECPoint point = ecPub.getW();
       
  1919             ECParameterSpec params = ecPub.getParams();
       
  1920             byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve());
       
  1921             if (id) {
       
  1922                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint));
       
  1923             }
       
  1924             if (netscapeDb) {
       
  1925                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint);
       
  1926             }
       
  1927         } else {
       
  1928             throw new RuntimeException("Unknown key algorithm " + alg);
       
  1929         }
       
  1930         return attrs;
       
  1931     }
       
  1932 
       
  1933     /**
       
  1934      * return true if cert destroyed
       
  1935      */
       
  1936     private boolean destroyCert(byte[] cka_id)
       
  1937                 throws PKCS11Exception, KeyStoreException {
       
  1938         Session session = null;
       
  1939         try {
       
  1940             session = token.getOpSession();
       
  1941             THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
       
  1942             if (h.type != ATTR_CLASS_CERT) {
       
  1943                 return false;
       
  1944             }
       
  1945 
       
  1946             token.p11.C_DestroyObject(session.id(), h.handle);
       
  1947             if (debug != null) {
       
  1948                 debug.println("destroyCert destroyed cert with CKA_ID [" +
       
  1949                                                 getID(cka_id) +
       
  1950                                                 "]");
       
  1951             }
       
  1952             return true;
       
  1953         } finally {
       
  1954             token.releaseSession(session);
       
  1955         }
       
  1956     }
       
  1957 
       
  1958     /**
       
  1959      * return true if chain destroyed
       
  1960      */
       
  1961     private boolean destroyChain(byte[] cka_id)
       
  1962         throws PKCS11Exception, CertificateException, KeyStoreException {
       
  1963 
       
  1964         Session session = null;
       
  1965         try {
       
  1966             session = token.getOpSession();
       
  1967 
       
  1968             THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
       
  1969             if (h.type != ATTR_CLASS_CERT) {
       
  1970                 if (debug != null) {
       
  1971                     debug.println("destroyChain could not find " +
       
  1972                         "end entity cert with CKA_ID [0x" +
       
  1973                         Functions.toHexString(cka_id) +
       
  1974                         "]");
       
  1975                 }
       
  1976                 return false;
       
  1977             }
       
  1978 
       
  1979             X509Certificate endCert = loadCert(session, h.handle);
       
  1980             token.p11.C_DestroyObject(session.id(), h.handle);
       
  1981             if (debug != null) {
       
  1982                 debug.println("destroyChain destroyed end entity cert " +
       
  1983                         "with CKA_ID [" +
       
  1984                         getID(cka_id) +
       
  1985                         "]");
       
  1986             }
       
  1987 
       
  1988             // build chain following issuer->subject links
       
  1989 
       
  1990             X509Certificate next = endCert;
       
  1991             while (true) {
       
  1992 
       
  1993                 if (next.getSubjectX500Principal().equals
       
  1994                     (next.getIssuerX500Principal())) {
       
  1995                     // self signed - done
       
  1996                     break;
       
  1997                 }
       
  1998 
       
  1999                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  2000                         ATTR_TOKEN_TRUE,
       
  2001                         ATTR_CLASS_CERT,
       
  2002                         new CK_ATTRIBUTE(CKA_SUBJECT,
       
  2003                                   next.getIssuerX500Principal().getEncoded()) };
       
  2004                 long[] ch = findObjects(session, attrs);
       
  2005 
       
  2006                 if (ch == null || ch.length == 0) {
       
  2007                     // done
       
  2008                     break;
       
  2009                 } else {
       
  2010                     // if more than one found, use first
       
  2011                     if (debug != null && ch.length > 1) {
       
  2012                         debug.println("destroyChain found " +
       
  2013                                 ch.length +
       
  2014                                 " certificate entries for subject [" +
       
  2015                                 next.getIssuerX500Principal() +
       
  2016                                 "] in token - using first entry");
       
  2017                     }
       
  2018 
       
  2019                     next = loadCert(session, ch[0]);
       
  2020 
       
  2021                     // only delete if not part of any other chain
       
  2022 
       
  2023                     attrs = new CK_ATTRIBUTE[] {
       
  2024                         ATTR_TOKEN_TRUE,
       
  2025                         ATTR_CLASS_CERT,
       
  2026                         new CK_ATTRIBUTE(CKA_ISSUER,
       
  2027                                 next.getSubjectX500Principal().getEncoded()) };
       
  2028                     long[] issuers = findObjects(session, attrs);
       
  2029 
       
  2030                     boolean destroyIt = false;
       
  2031                     if (issuers == null || issuers.length == 0) {
       
  2032                         // no other certs with this issuer -
       
  2033                         // destroy it
       
  2034                         destroyIt = true;
       
  2035                     } else if (issuers.length == 1) {
       
  2036                         X509Certificate iCert = loadCert(session, issuers[0]);
       
  2037                         if (next.equals(iCert)) {
       
  2038                             // only cert with issuer is itself (self-signed) -
       
  2039                             // destroy it
       
  2040                             destroyIt = true;
       
  2041                         }
       
  2042                     }
       
  2043 
       
  2044                     if (destroyIt) {
       
  2045                         token.p11.C_DestroyObject(session.id(), ch[0]);
       
  2046                         if (debug != null) {
       
  2047                             debug.println
       
  2048                                 ("destroyChain destroyed cert in chain " +
       
  2049                                 "with subject [" +
       
  2050                                 next.getSubjectX500Principal() + "]");
       
  2051                         }
       
  2052                     } else {
       
  2053                         if (debug != null) {
       
  2054                             debug.println("destroyChain did not destroy " +
       
  2055                                 "shared cert in chain with subject [" +
       
  2056                                 next.getSubjectX500Principal() + "]");
       
  2057                         }
       
  2058                     }
       
  2059                 }
       
  2060             }
       
  2061 
       
  2062             return true;
       
  2063 
       
  2064         } finally {
       
  2065             token.releaseSession(session);
       
  2066         }
       
  2067     }
       
  2068 
       
  2069     /**
       
  2070      * return true if secret key destroyed
       
  2071      */
       
  2072     private boolean destroySkey(String alias)
       
  2073                 throws PKCS11Exception, KeyStoreException {
       
  2074         Session session = null;
       
  2075         try {
       
  2076             session = token.getOpSession();
       
  2077 
       
  2078             THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias);
       
  2079             if (h.type != ATTR_CLASS_SKEY) {
       
  2080                 if (debug != null) {
       
  2081                     debug.println("destroySkey did not find secret key " +
       
  2082                         "with CKA_LABEL [" +
       
  2083                         alias +
       
  2084                         "]");
       
  2085                 }
       
  2086                 return false;
       
  2087             }
       
  2088             token.p11.C_DestroyObject(session.id(), h.handle);
       
  2089             return true;
       
  2090         } finally {
       
  2091             token.releaseSession(session);
       
  2092         }
       
  2093     }
       
  2094 
       
  2095     /**
       
  2096      * return true if private key destroyed
       
  2097      */
       
  2098     private boolean destroyPkey(byte[] cka_id)
       
  2099                 throws PKCS11Exception, KeyStoreException {
       
  2100         Session session = null;
       
  2101         try {
       
  2102             session = token.getOpSession();
       
  2103 
       
  2104             THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
       
  2105             if (h.type != ATTR_CLASS_PKEY) {
       
  2106                 if (debug != null) {
       
  2107                     debug.println
       
  2108                         ("destroyPkey did not find private key with CKA_ID [" +
       
  2109                         getID(cka_id) +
       
  2110                         "]");
       
  2111                 }
       
  2112                 return false;
       
  2113             }
       
  2114             token.p11.C_DestroyObject(session.id(), h.handle);
       
  2115             return true;
       
  2116         } finally {
       
  2117             token.releaseSession(session);
       
  2118         }
       
  2119     }
       
  2120 
       
  2121     /**
       
  2122      * build [alias + issuer + serialNumber] string from a cert
       
  2123      */
       
  2124     private String getID(String alias, X509Certificate cert) {
       
  2125         X500Principal issuer = cert.getIssuerX500Principal();
       
  2126         BigInteger serialNum = cert.getSerialNumber();
       
  2127 
       
  2128         return alias +
       
  2129                 ALIAS_SEP +
       
  2130                 issuer.getName(X500Principal.CANONICAL) +
       
  2131                 ALIAS_SEP +
       
  2132                 serialNum.toString();
       
  2133     }
       
  2134 
       
  2135     /**
       
  2136      * build CKA_ID string from bytes
       
  2137      */
       
  2138     private static String getID(byte[] bytes) {
       
  2139         boolean printable = true;
       
  2140         for (int i = 0; i < bytes.length; i++) {
       
  2141             if (!DerValue.isPrintableStringChar((char)bytes[i])) {
       
  2142                 printable = false;
       
  2143                 break;
       
  2144             }
       
  2145         }
       
  2146 
       
  2147         if (!printable) {
       
  2148             return "0x" + Functions.toHexString(bytes);
       
  2149         } else {
       
  2150             try {
       
  2151                 return new String(bytes, "UTF-8");
       
  2152             } catch (UnsupportedEncodingException uee) {
       
  2153                 return "0x" + Functions.toHexString(bytes);
       
  2154             }
       
  2155         }
       
  2156     }
       
  2157 
       
  2158     /**
       
  2159      * find an object on the token
       
  2160      *
       
  2161      * @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY
       
  2162      * @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY
       
  2163      * @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY
       
  2164      */
       
  2165     private THandle getTokenObject(Session session,
       
  2166                                 CK_ATTRIBUTE type,
       
  2167                                 byte[] cka_id,
       
  2168                                 String cka_label)
       
  2169                 throws PKCS11Exception, KeyStoreException {
       
  2170 
       
  2171         CK_ATTRIBUTE[] attrs;
       
  2172         if (type == ATTR_CLASS_SKEY) {
       
  2173             attrs = new CK_ATTRIBUTE[] {
       
  2174                         ATTR_SKEY_TOKEN_TRUE,
       
  2175                         new CK_ATTRIBUTE(CKA_LABEL, cka_label),
       
  2176                         type };
       
  2177         } else {
       
  2178             attrs = new CK_ATTRIBUTE[] {
       
  2179                         ATTR_TOKEN_TRUE,
       
  2180                         new CK_ATTRIBUTE(CKA_ID, cka_id),
       
  2181                         type };
       
  2182         }
       
  2183         long[] h = findObjects(session, attrs);
       
  2184         if (h.length == 0) {
       
  2185             if (debug != null) {
       
  2186                 if (type == ATTR_CLASS_SKEY) {
       
  2187                     debug.println("getTokenObject did not find secret key " +
       
  2188                                 "with CKA_LABEL [" +
       
  2189                                 cka_label +
       
  2190                                 "]");
       
  2191                 } else if (type == ATTR_CLASS_CERT) {
       
  2192                     debug.println
       
  2193                         ("getTokenObject did not find cert with CKA_ID [" +
       
  2194                         getID(cka_id) +
       
  2195                         "]");
       
  2196                 } else {
       
  2197                     debug.println("getTokenObject did not find private key " +
       
  2198                         "with CKA_ID [" +
       
  2199                         getID(cka_id) +
       
  2200                         "]");
       
  2201                 }
       
  2202             }
       
  2203         } else if (h.length == 1) {
       
  2204 
       
  2205             // found object handle - return it
       
  2206             return new THandle(h[0], type);
       
  2207 
       
  2208         } else {
       
  2209 
       
  2210             // found multiple object handles -
       
  2211             // see if token ignored CKA_LABEL during search (e.g. NSS)
       
  2212 
       
  2213             if (type == ATTR_CLASS_SKEY) {
       
  2214 
       
  2215                 ArrayList<THandle> list = new ArrayList<THandle>(h.length);
       
  2216                 for (int i = 0; i < h.length; i++) {
       
  2217 
       
  2218                     CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[]
       
  2219                                         { new CK_ATTRIBUTE(CKA_LABEL) };
       
  2220                     token.p11.C_GetAttributeValue(session.id(), h[i], label);
       
  2221                     if (label[0].pValue != null &&
       
  2222                         cka_label.equals(new String(label[0].getCharArray()))) {
       
  2223                         list.add(new THandle(h[i], ATTR_CLASS_SKEY));
       
  2224                     }
       
  2225                 }
       
  2226                 if (list.size() == 1) {
       
  2227                     // yes, there was only one CKA_LABEL that matched
       
  2228                     return list.get(0);
       
  2229                 } else {
       
  2230                     throw new KeyStoreException("invalid KeyStore state: " +
       
  2231                         "found " +
       
  2232                         list.size() +
       
  2233                         " secret keys sharing CKA_LABEL [" +
       
  2234                         cka_label +
       
  2235                         "]");
       
  2236                 }
       
  2237             } else if (type == ATTR_CLASS_CERT) {
       
  2238                 throw new KeyStoreException("invalid KeyStore state: " +
       
  2239                         "found " +
       
  2240                         h.length +
       
  2241                         " certificates sharing CKA_ID " +
       
  2242                         getID(cka_id));
       
  2243             } else {
       
  2244                 throw new KeyStoreException("invalid KeyStore state: " +
       
  2245                         "found " +
       
  2246                         h.length +
       
  2247                         " private keys sharing CKA_ID " +
       
  2248                         getID(cka_id));
       
  2249             }
       
  2250         }
       
  2251         return new THandle(NO_HANDLE, null);
       
  2252     }
       
  2253 
       
  2254     /**
       
  2255      * Create a mapping of all key pairs, trusted certs, and secret keys
       
  2256      * on the token into logical KeyStore entries unambiguously
       
  2257      * accessible via an alias.
       
  2258      *
       
  2259      * If the token is removed, the map may contain stale values.
       
  2260      * KeyStore.load should be called to re-create the map.
       
  2261      *
       
  2262      * Assume all private keys and matching certs share a unique CKA_ID.
       
  2263      *
       
  2264      * Assume all secret keys have a unique CKA_LABEL.
       
  2265      *
       
  2266      * @return true if multiple certs found sharing the same CKA_LABEL
       
  2267      *          (if so, write capabilities are disabled)
       
  2268      */
       
  2269     private boolean mapLabels() throws
       
  2270                 PKCS11Exception, CertificateException, KeyStoreException {
       
  2271 
       
  2272         CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] {
       
  2273                                 new CK_ATTRIBUTE(CKA_TRUSTED) };
       
  2274 
       
  2275         Session session = null;
       
  2276         try {
       
  2277             session = token.getOpSession();
       
  2278 
       
  2279             // get all private key CKA_IDs
       
  2280 
       
  2281             ArrayList<byte[]> pkeyIDs = new ArrayList<byte[]>();
       
  2282             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
       
  2283                 ATTR_TOKEN_TRUE,
       
  2284                 ATTR_CLASS_PKEY,
       
  2285             };
       
  2286             long[] handles = findObjects(session, attrs);
       
  2287 
       
  2288             for (long handle : handles) {
       
  2289                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
       
  2290                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
       
  2291 
       
  2292                 if (attrs[0].pValue != null) {
       
  2293                     pkeyIDs.add(attrs[0].getByteArray());
       
  2294                 }
       
  2295             }
       
  2296 
       
  2297             // Get all certificates
       
  2298             //
       
  2299             // If cert does not have a CKA_LABEL nor CKA_ID, it is ignored.
       
  2300             //
       
  2301             // Get the CKA_LABEL for each cert
       
  2302             // (if the cert does not have a CKA_LABEL, use the CKA_ID).
       
  2303             //
       
  2304             // Map each cert to the its CKA_LABEL
       
  2305             // (multiple certs may be mapped to a single CKA_LABEL)
       
  2306 
       
  2307             HashMap<String, HashSet<AliasInfo>> certMap =
       
  2308                                 new HashMap<String, HashSet<AliasInfo>>();
       
  2309 
       
  2310             attrs = new CK_ATTRIBUTE[] {
       
  2311                 ATTR_TOKEN_TRUE,
       
  2312                 ATTR_CLASS_CERT,
       
  2313             };
       
  2314             handles = findObjects(session, attrs);
       
  2315 
       
  2316             for (long handle : handles) {
       
  2317                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
       
  2318 
       
  2319                 String cka_label = null;
       
  2320                 byte[] cka_id = null;
       
  2321                 try {
       
  2322                     token.p11.C_GetAttributeValue(session.id(), handle, attrs);
       
  2323                     if (attrs[0].pValue != null) {
       
  2324                         // there is a CKA_LABEL
       
  2325                         cka_label = new String(attrs[0].getCharArray());
       
  2326                     }
       
  2327                 } catch (PKCS11Exception pe) {
       
  2328                     if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) {
       
  2329                         throw pe;
       
  2330                     }
       
  2331 
       
  2332                     // GetAttributeValue for CKA_LABEL not supported
       
  2333                     //
       
  2334                     // XXX SCA1000
       
  2335                 }
       
  2336 
       
  2337                 // get CKA_ID
       
  2338 
       
  2339                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
       
  2340                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
       
  2341                 if (attrs[0].pValue == null) {
       
  2342                     if (cka_label == null) {
       
  2343                         // no cka_label nor cka_id - ignore
       
  2344                         continue;
       
  2345                     }
       
  2346                 } else {
       
  2347                     if (cka_label == null) {
       
  2348                         // use CKA_ID as CKA_LABEL
       
  2349                         cka_label = getID(attrs[0].getByteArray());
       
  2350                     }
       
  2351                     cka_id = attrs[0].getByteArray();
       
  2352                 }
       
  2353 
       
  2354                 X509Certificate cert = loadCert(session, handle);
       
  2355 
       
  2356                 // get CKA_TRUSTED
       
  2357 
       
  2358                 boolean cka_trusted = false;
       
  2359 
       
  2360                 if (useSecmodTrust) {
       
  2361                     cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType);
       
  2362                 } else {
       
  2363                     if (CKA_TRUSTED_SUPPORTED) {
       
  2364                         try {
       
  2365                             token.p11.C_GetAttributeValue
       
  2366                                     (session.id(), handle, trustedAttr);
       
  2367                             cka_trusted = trustedAttr[0].getBoolean();
       
  2368                         } catch (PKCS11Exception pe) {
       
  2369                             if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) {
       
  2370                                 // XXX  NSS, ibutton, sca1000
       
  2371                                 CKA_TRUSTED_SUPPORTED = false;
       
  2372                                 if (debug != null) {
       
  2373                                     debug.println
       
  2374                                             ("CKA_TRUSTED attribute not supported");
       
  2375                                 }
       
  2376                             }
       
  2377                         }
       
  2378                     }
       
  2379                 }
       
  2380 
       
  2381                 HashSet<AliasInfo> infoSet = certMap.get(cka_label);
       
  2382                 if (infoSet == null) {
       
  2383                     infoSet = new HashSet<AliasInfo>(2);
       
  2384                     certMap.put(cka_label, infoSet);
       
  2385                 }
       
  2386 
       
  2387                 // initially create private key entry AliasInfo entries -
       
  2388                 // these entries will get resolved into their true
       
  2389                 // entry types later
       
  2390 
       
  2391                 infoSet.add(new AliasInfo
       
  2392                                 (cka_label,
       
  2393                                 cka_id,
       
  2394                                 cka_trusted,
       
  2395                                 cert));
       
  2396             }
       
  2397 
       
  2398             // create list secret key CKA_LABELS -
       
  2399             // if there are duplicates (either between secret keys,
       
  2400             // or between a secret key and another object),
       
  2401             // throw an exception
       
  2402             HashMap<String, AliasInfo> sKeyMap =
       
  2403                     new HashMap<String, AliasInfo>();
       
  2404 
       
  2405             attrs = new CK_ATTRIBUTE[] {
       
  2406                 ATTR_SKEY_TOKEN_TRUE,
       
  2407                 ATTR_CLASS_SKEY,
       
  2408             };
       
  2409             handles = findObjects(session, attrs);
       
  2410 
       
  2411             for (long handle : handles) {
       
  2412                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
       
  2413                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
       
  2414                 if (attrs[0].pValue != null) {
       
  2415 
       
  2416                     // there is a CKA_LABEL
       
  2417                     String cka_label = new String(attrs[0].getCharArray());
       
  2418                     if (sKeyMap.get(cka_label) == null) {
       
  2419                         sKeyMap.put(cka_label, new AliasInfo(cka_label));
       
  2420                     } else {
       
  2421                         throw new KeyStoreException("invalid KeyStore state: " +
       
  2422                                 "found multiple secret keys sharing same " +
       
  2423                                 "CKA_LABEL [" +
       
  2424                                 cka_label +
       
  2425                                 "]");
       
  2426                     }
       
  2427                 }
       
  2428             }
       
  2429 
       
  2430             // update global aliasMap with alias mappings
       
  2431             ArrayList<AliasInfo> matchedCerts =
       
  2432                                 mapPrivateKeys(pkeyIDs, certMap);
       
  2433             boolean sharedLabel = mapCerts(matchedCerts, certMap);
       
  2434             mapSecretKeys(sKeyMap);
       
  2435 
       
  2436             return sharedLabel;
       
  2437 
       
  2438         } finally {
       
  2439             token.releaseSession(session);
       
  2440         }
       
  2441     }
       
  2442 
       
  2443     /**
       
  2444      * for each private key CKA_ID, find corresponding cert with same CKA_ID.
       
  2445      * if found cert, see if cert CKA_LABEL is unique.
       
  2446      *     if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL.
       
  2447      *     if CKA_LABEL not unique, map private key/cert alias to:
       
  2448      *                   CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL
       
  2449      * if cert not found, ignore private key
       
  2450      * (don't support private key entries without a cert chain yet)
       
  2451      *
       
  2452      * @return a list of AliasInfo entries that represents all matches
       
  2453      */
       
  2454     private ArrayList<AliasInfo> mapPrivateKeys(ArrayList<byte[]> pkeyIDs,
       
  2455                         HashMap<String, HashSet<AliasInfo>> certMap)
       
  2456                 throws PKCS11Exception, CertificateException {
       
  2457 
       
  2458         // reset global alias map
       
  2459         aliasMap = new HashMap<String, AliasInfo>();
       
  2460 
       
  2461         // list of matched certs that we will return
       
  2462         ArrayList<AliasInfo> matchedCerts = new ArrayList<AliasInfo>();
       
  2463 
       
  2464         for (byte[] pkeyID : pkeyIDs) {
       
  2465 
       
  2466             // try to find a matching CKA_ID in a certificate
       
  2467 
       
  2468             boolean foundMatch = false;
       
  2469             Set<String> certLabels = certMap.keySet();
       
  2470             for (String certLabel : certLabels) {
       
  2471 
       
  2472                 // get cert CKA_IDs (if present) for each cert
       
  2473 
       
  2474                 HashSet<AliasInfo> infoSet = certMap.get(certLabel);
       
  2475                 for (AliasInfo aliasInfo : infoSet) {
       
  2476                     if (Arrays.equals(pkeyID, aliasInfo.id)) {
       
  2477 
       
  2478                         // found private key with matching cert
       
  2479 
       
  2480                         if (infoSet.size() == 1) {
       
  2481                             // unique CKA_LABEL - use certLabel as alias
       
  2482                             aliasInfo.matched = true;
       
  2483                             aliasMap.put(certLabel, aliasInfo);
       
  2484                         } else {
       
  2485                             // create new alias
       
  2486                             aliasInfo.matched = true;
       
  2487                             aliasMap.put(getID(certLabel, aliasInfo.cert),
       
  2488                                         aliasInfo);
       
  2489                         }
       
  2490                         matchedCerts.add(aliasInfo);
       
  2491                         foundMatch = true;
       
  2492                         break;
       
  2493                     }
       
  2494                 }
       
  2495                 if (foundMatch) {
       
  2496                     break;
       
  2497                 }
       
  2498             }
       
  2499 
       
  2500             if (!foundMatch) {
       
  2501                 if (debug != null) {
       
  2502                     debug.println
       
  2503                         ("did not find match for private key with CKA_ID [" +
       
  2504                         getID(pkeyID) +
       
  2505                         "] (ignoring entry)");
       
  2506                 }
       
  2507             }
       
  2508         }
       
  2509 
       
  2510         return matchedCerts;
       
  2511     }
       
  2512 
       
  2513     /**
       
  2514      * for each cert not matched with a private key but is CKA_TRUSTED:
       
  2515      *     if CKA_LABEL unique, map cert to CKA_LABEL.
       
  2516      *     if CKA_LABEL not unique, map cert to [label+issuer+serialNum]
       
  2517      *
       
  2518      * if CKA_TRUSTED not supported, treat all certs not part of a chain
       
  2519      * as trusted
       
  2520      *
       
  2521      * @return true if multiple certs found sharing the same CKA_LABEL
       
  2522      */
       
  2523     private boolean mapCerts(ArrayList<AliasInfo> matchedCerts,
       
  2524                         HashMap<String, HashSet<AliasInfo>> certMap)
       
  2525                 throws PKCS11Exception, CertificateException {
       
  2526 
       
  2527         // load all cert chains
       
  2528         for (AliasInfo aliasInfo : matchedCerts) {
       
  2529             Session session = null;
       
  2530             try {
       
  2531                 session = token.getOpSession();
       
  2532                 aliasInfo.chain = loadChain(session, aliasInfo.cert);
       
  2533             } finally {
       
  2534                 token.releaseSession(session);
       
  2535             }
       
  2536         }
       
  2537 
       
  2538         // find all certs in certMap not part of a cert chain
       
  2539         // - these are trusted
       
  2540 
       
  2541         boolean sharedLabel = false;
       
  2542 
       
  2543         Set<String> certLabels = certMap.keySet();
       
  2544         for (String certLabel : certLabels) {
       
  2545             HashSet<AliasInfo> infoSet = certMap.get(certLabel);
       
  2546             for (AliasInfo aliasInfo : infoSet) {
       
  2547 
       
  2548                 if (aliasInfo.matched == true) {
       
  2549                     // already found a private key match for this cert -
       
  2550                     // just continue
       
  2551                     aliasInfo.trusted = false;
       
  2552                     continue;
       
  2553                 }
       
  2554 
       
  2555                 // cert in this aliasInfo is not matched yet
       
  2556                 //
       
  2557                 // if CKA_TRUSTED_SUPPORTED == true,
       
  2558                 // then check if cert is trusted
       
  2559 
       
  2560                 if (CKA_TRUSTED_SUPPORTED) {
       
  2561                     if (aliasInfo.trusted) {
       
  2562                         // trusted certificate
       
  2563                         if (mapTrustedCert
       
  2564                                 (certLabel, aliasInfo, infoSet) == true) {
       
  2565                             sharedLabel = true;
       
  2566                         }
       
  2567                     }
       
  2568                     continue;
       
  2569                 }
       
  2570 
       
  2571                 // CKA_TRUSTED_SUPPORTED == false
       
  2572                 //
       
  2573                 // XXX treat all certs not part of a chain as trusted
       
  2574                 // XXX
       
  2575                 // XXX Unsupported
       
  2576                 //
       
  2577                 // boolean partOfChain = false;
       
  2578                 // for (AliasInfo matchedInfo : matchedCerts) {
       
  2579                 //     for (int i = 0; i < matchedInfo.chain.length; i++) {
       
  2580                 //      if (matchedInfo.chain[i].equals(aliasInfo.cert)) {
       
  2581                 //          partOfChain = true;
       
  2582                 //          break;
       
  2583                 //      }
       
  2584                 //     }
       
  2585                 //     if (partOfChain) {
       
  2586                 //      break;
       
  2587                 //     }
       
  2588                 // }
       
  2589                 //
       
  2590                 // if (!partOfChain) {
       
  2591                 //     if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){
       
  2592                 //      sharedLabel = true;
       
  2593                 //     }
       
  2594                 // } else {
       
  2595                 //    if (debug != null) {
       
  2596                 //      debug.println("ignoring unmatched/untrusted cert " +
       
  2597                 //          "that is part of cert chain - cert subject is [" +
       
  2598                 //          aliasInfo.cert.getSubjectX500Principal().getName
       
  2599                 //                              (X500Principal.CANONICAL) +
       
  2600                 //          "]");
       
  2601                 //     }
       
  2602                 // }
       
  2603             }
       
  2604         }
       
  2605 
       
  2606         return sharedLabel;
       
  2607     }
       
  2608 
       
  2609     private boolean mapTrustedCert(String certLabel,
       
  2610                                 AliasInfo aliasInfo,
       
  2611                                 HashSet<AliasInfo> infoSet) {
       
  2612 
       
  2613         boolean sharedLabel = false;
       
  2614 
       
  2615         aliasInfo.type = ATTR_CLASS_CERT;
       
  2616         aliasInfo.trusted = true;
       
  2617         if (infoSet.size() == 1) {
       
  2618             // unique CKA_LABEL - use certLabel as alias
       
  2619             aliasMap.put(certLabel, aliasInfo);
       
  2620         } else {
       
  2621             // create new alias
       
  2622             sharedLabel = true;
       
  2623             aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo);
       
  2624         }
       
  2625 
       
  2626         return sharedLabel;
       
  2627     }
       
  2628 
       
  2629     /**
       
  2630      * If the secret key shares a CKA_LABEL with another entry,
       
  2631      * throw an exception
       
  2632      */
       
  2633     private void mapSecretKeys(HashMap<String, AliasInfo> sKeyMap)
       
  2634                 throws KeyStoreException {
       
  2635         for (String label : sKeyMap.keySet()) {
       
  2636             if (aliasMap.containsKey(label)) {
       
  2637                 throw new KeyStoreException("invalid KeyStore state: " +
       
  2638                         "found secret key sharing CKA_LABEL [" +
       
  2639                         label +
       
  2640                         "] with another token object");
       
  2641             }
       
  2642         }
       
  2643         aliasMap.putAll(sKeyMap);
       
  2644     }
       
  2645 
       
  2646     private void dumpTokenMap() {
       
  2647         Set<String> aliases = aliasMap.keySet();
       
  2648         System.out.println("Token Alias Map:");
       
  2649         if (aliases.isEmpty()) {
       
  2650             System.out.println("  [empty]");
       
  2651         } else {
       
  2652             for (String s : aliases) {
       
  2653                 System.out.println("  " + s + aliasMap.get(s));
       
  2654             }
       
  2655         }
       
  2656     }
       
  2657 
       
  2658     private void checkWrite() throws KeyStoreException {
       
  2659         if (writeDisabled) {
       
  2660             throw new KeyStoreException
       
  2661                 ("This PKCS11KeyStore does not support write capabilities");
       
  2662         }
       
  2663     }
       
  2664 
       
  2665     private final static long[] LONG0 = new long[0];
       
  2666 
       
  2667     private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs)
       
  2668             throws PKCS11Exception {
       
  2669         Token token = session.token;
       
  2670         long[] handles = LONG0;
       
  2671         token.p11.C_FindObjectsInit(session.id(), attrs);
       
  2672         while (true) {
       
  2673             long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX);
       
  2674             if (h.length == 0) {
       
  2675                 break;
       
  2676             }
       
  2677             handles = P11Util.concat(handles, h);
       
  2678         }
       
  2679         token.p11.C_FindObjectsFinal(session.id());
       
  2680         return handles;
       
  2681     }
       
  2682 
       
  2683 }