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