1 /*
   2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.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             }
 777         } catch (KeyStoreException | PKCS11Exception e) {
 778             throw new IOException("load failed", e);
 779         }
 780     }
 781 
 782     /**
 783      * Loads the keystore using the given
 784      * <code>KeyStore.LoadStoreParameter</code>.
 785      *
 786      * <p> The <code>LoadStoreParameter.getProtectionParameter()</code>
 787      * method is expected to return a <code>KeyStore.PasswordProtection</code>
 788      * object.  The password is retrieved from that object and used
 789      * to unlock the PKCS#11 token.
 790      *
 791      * <p> If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH
 792      * then the provided password must be <code>null</code>.
 793      *
 794      * @param param the <code>KeyStore.LoadStoreParameter</code>
 795      *
 796      * @exception IllegalArgumentException if the given
 797      *          <code>KeyStore.LoadStoreParameter</code> is <code>null</code>,
 798      *          or if that parameter returns a <code>null</code>
 799      *          <code>ProtectionParameter</code> object.
 800      *          input is not recognized
 801      * @exception IOException if the token supports a
 802      *          CKF_PROTECTED_AUTHENTICATION_PATH and the provided password
 803      *          is non-null, or if the token login operation fails
 804      */
 805     public synchronized void engineLoad(KeyStore.LoadStoreParameter param)
 806                 throws IOException, NoSuchAlgorithmException,
 807                 CertificateException {
 808 
 809         token.ensureValid();
 810 
 811         if (NSS_TEST) {
 812             ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);
 813         }
 814 
 815         // if caller wants to pass a NULL password,
 816         // force it to pass a non-NULL PasswordProtection that returns
 817         // a NULL password
 818 
 819         if (param == null) {
 820             throw new IllegalArgumentException
 821                         ("invalid null LoadStoreParameter");
 822         }
 823         if (useSecmodTrust) {
 824             if (param instanceof Secmod.KeyStoreLoadParameter) {
 825                 nssTrustType = ((Secmod.KeyStoreLoadParameter)param).getTrustType();
 826             } else {
 827                 nssTrustType = Secmod.TrustType.ALL;
 828             }
 829         }
 830 
 831         CallbackHandler handler;
 832         KeyStore.ProtectionParameter pp = param.getProtectionParameter();
 833         if (pp instanceof PasswordProtection) {
 834             char[] password = ((PasswordProtection)pp).getPassword();
 835             if (password == null) {
 836                 handler = null;
 837             } else {
 838                 handler = new PasswordCallbackHandler(password);
 839             }
 840         } else if (pp instanceof CallbackHandlerProtection) {
 841             handler = ((CallbackHandlerProtection)pp).getCallbackHandler();
 842         } else {
 843             throw new IllegalArgumentException
 844                         ("ProtectionParameter must be either " +
 845                         "PasswordProtection or CallbackHandlerProtection");
 846         }
 847 
 848         try {
 849             login(handler);
 850             if (mapLabels() == true) {
 851                 // CKA_LABELs are shared by multiple certs
 852                 writeDisabled = true;
 853             }
 854             if (debug != null) {
 855                 dumpTokenMap();
 856             }
 857         } catch (LoginException | KeyStoreException | PKCS11Exception e) {
 858             throw new IOException("load failed", e);
 859         }
 860     }
 861 
 862     private void login(CallbackHandler handler) throws LoginException {
 863         if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) {
 864             token.provider.login(null, handler);
 865         } else {
 866             // token supports protected authentication path
 867             // (external pin-pad, for example)
 868             if (handler != null &&
 869                 !token.config.getKeyStoreCompatibilityMode()) {
 870                 throw new LoginException("can not specify password if token " +
 871                                 "supports protected authentication path");
 872             }
 873 
 874             // must rely on application-set or default handler
 875             // if one is necessary
 876             token.provider.login(null, null);
 877         }
 878     }
 879 
 880     /**
 881      * Get a <code>KeyStore.Entry</code> for the specified alias
 882      *
 883      * @param alias get the <code>KeyStore.Entry</code> for this alias
 884      * @param protParam this must be <code>null</code>
 885      *
 886      * @return the <code>KeyStore.Entry</code> for the specified alias,
 887      *          or <code>null</code> if there is no such entry
 888      *
 889      * @exception KeyStoreException if the operation failed
 890      * @exception NoSuchAlgorithmException if the algorithm for recovering the
 891      *          entry cannot be found
 892      * @exception UnrecoverableEntryException if the specified
 893      *          <code>protParam</code> were insufficient or invalid
 894      *
 895      * @since 1.5
 896      */
 897     public synchronized KeyStore.Entry engineGetEntry(String alias,
 898                         KeyStore.ProtectionParameter protParam)
 899                 throws KeyStoreException, NoSuchAlgorithmException,
 900                 UnrecoverableEntryException {
 901 
 902         token.ensureValid();
 903 
 904         if (protParam != null &&
 905             protParam instanceof KeyStore.PasswordProtection &&
 906             ((KeyStore.PasswordProtection)protParam).getPassword() != null &&
 907             !token.config.getKeyStoreCompatibilityMode()) {
 908             throw new KeyStoreException("ProtectionParameter must be null");
 909         }
 910 
 911         AliasInfo aliasInfo = aliasMap.get(alias);
 912         if (aliasInfo == null) {
 913             if (debug != null) {
 914                 debug.println("engineGetEntry did not find alias [" +
 915                         alias +
 916                         "] in map");
 917             }
 918             return null;
 919         }
 920 
 921         Session session = null;
 922         try {
 923             session = token.getOpSession();
 924 
 925             if (aliasInfo.type == ATTR_CLASS_CERT) {
 926                 // trusted certificate entry
 927                 if (debug != null) {
 928                     debug.println("engineGetEntry found trusted cert entry");
 929                 }
 930                 return new KeyStore.TrustedCertificateEntry(aliasInfo.cert);
 931             } else if (aliasInfo.type == ATTR_CLASS_SKEY) {
 932                 // secret key entry
 933                 if (debug != null) {
 934                     debug.println("engineGetEntry found secret key entry");
 935                 }
 936 
 937                 THandle h = getTokenObject
 938                         (session, ATTR_CLASS_SKEY, null, aliasInfo.label);
 939                 if (h.type != ATTR_CLASS_SKEY) {
 940                     throw new KeyStoreException
 941                         ("expected but could not find secret key");
 942                 } else {
 943                     SecretKey skey = loadSkey(session, h.handle);
 944                     return new KeyStore.SecretKeyEntry(skey);
 945                 }
 946             } else {
 947                 // private key entry
 948                 if (debug != null) {
 949                     debug.println("engineGetEntry found private key entry");
 950                 }
 951 
 952                 THandle h = getTokenObject
 953                         (session, ATTR_CLASS_PKEY, aliasInfo.id, null);
 954                 if (h.type != ATTR_CLASS_PKEY) {
 955                     throw new KeyStoreException
 956                         ("expected but could not find private key");
 957                 } else {
 958                     PrivateKey pkey = loadPkey(session, h.handle);
 959                     Certificate[] chain = aliasInfo.chain;
 960                     if ((pkey != null) && (chain != null)) {
 961                         return new KeyStore.PrivateKeyEntry(pkey, chain);
 962                     } else {
 963                         if (debug != null) {
 964                             debug.println
 965                                 ("engineGetEntry got null cert chain or private key");
 966                         }
 967                     }
 968                 }
 969             }
 970             return null;
 971         } catch (PKCS11Exception pe) {
 972             throw new KeyStoreException(pe);
 973         } finally {
 974             token.releaseSession(session);
 975         }
 976     }
 977 
 978     /**
 979      * Save a <code>KeyStore.Entry</code> under the specified alias.
 980      *
 981      * <p> If an entry already exists for the specified alias,
 982      * it is overridden.
 983      *
 984      * <p> This KeyStore implementation only supports the standard
 985      * entry types, and only supports X509Certificates in
 986      * TrustedCertificateEntries.  Also, this implementation does not support
 987      * protecting entries using a different password
 988      * from the one used for token login.
 989      *
 990      * <p> Entries are immediately stored on the token.
 991      *
 992      * @param alias save the <code>KeyStore.Entry</code> under this alias
 993      * @param entry the <code>Entry</code> to save
 994      * @param protParam this must be <code>null</code>
 995      *
 996      * @exception KeyStoreException if this operation fails
 997      *
 998      * @since 1.5
 999      */
1000     public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
1001                         KeyStore.ProtectionParameter protParam)
1002                 throws KeyStoreException {
1003 
1004         token.ensureValid();
1005         checkWrite();
1006 
1007         if (protParam != null &&
1008             protParam instanceof KeyStore.PasswordProtection &&
1009             ((KeyStore.PasswordProtection)protParam).getPassword() != null &&
1010             !token.config.getKeyStoreCompatibilityMode()) {
1011             throw new KeyStoreException(new UnsupportedOperationException
1012                                 ("ProtectionParameter must be null"));
1013         }
1014 
1015         if (token.isWriteProtected()) {
1016             throw new KeyStoreException("token write-protected");
1017         }
1018 
1019         if (entry instanceof KeyStore.TrustedCertificateEntry) {
1020 
1021             if (useSecmodTrust == false) {
1022                 // PKCS #11 does not allow app to modify trusted certs -
1023                 throw new KeyStoreException(new UnsupportedOperationException
1024                                     ("trusted certificates may only be set by " +
1025                                     "token initialization application"));
1026             }
1027             Module module = token.provider.nssModule;
1028             if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) {
1029                 // XXX allow TRUSTANCHOR module
1030                 throw new KeyStoreException("Trusted certificates can only be "
1031                     + "added to the NSS KeyStore module");
1032             }
1033             Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate();
1034             if (cert instanceof X509Certificate == false) {
1035                 throw new KeyStoreException("Certificate must be an X509Certificate");
1036             }
1037             X509Certificate xcert = (X509Certificate)cert;
1038             AliasInfo info = aliasMap.get(alias);
1039             if (info != null) {
1040                 // XXX try to update
1041                 deleteEntry(alias);
1042             }
1043             try {
1044                 storeCert(alias, xcert);
1045                 module.setTrust(token, xcert);
1046                 mapLabels();
1047             } catch (PKCS11Exception | CertificateException e) {
1048                 throw new KeyStoreException(e);
1049             }
1050 
1051         } else {
1052 
1053             if (entry instanceof KeyStore.PrivateKeyEntry) {
1054 
1055                 PrivateKey key =
1056                         ((KeyStore.PrivateKeyEntry)entry).getPrivateKey();
1057                 if (!(key instanceof P11Key) &&
1058                     !(key instanceof RSAPrivateKey) &&
1059                     !(key instanceof DSAPrivateKey) &&
1060                     !(key instanceof DHPrivateKey) &&
1061                     !(key instanceof ECPrivateKey)) {
1062                     throw new KeyStoreException("unsupported key type: " +
1063                                                 key.getClass().getName());
1064                 }
1065 
1066                 // only support X509Certificate chains
1067                 Certificate[] chain =
1068                     ((KeyStore.PrivateKeyEntry)entry).getCertificateChain();
1069                 if (!(chain instanceof X509Certificate[])) {
1070                     throw new KeyStoreException
1071                         (new UnsupportedOperationException
1072                                 ("unsupported certificate array type: " +
1073                                 chain.getClass().getName()));
1074                 }
1075 
1076                 try {
1077                     boolean updatedAlias = false;
1078                     Set<String> aliases = aliasMap.keySet();
1079                     for (String oldAlias : aliases) {
1080 
1081                         // see if there's an existing entry with the same info
1082 
1083                         AliasInfo aliasInfo = aliasMap.get(oldAlias);
1084                         if (aliasInfo.type == ATTR_CLASS_PKEY &&
1085                             aliasInfo.cert.getPublicKey().equals
1086                                         (chain[0].getPublicKey())) {
1087 
1088                             // found existing entry -
1089                             // caller is renaming entry or updating cert chain
1090                             //
1091                             // set new CKA_LABEL/CKA_ID
1092                             // and update certs if necessary
1093 
1094                             updatePkey(alias,
1095                                         aliasInfo.id,
1096                                         (X509Certificate[])chain,
1097                                         !aliasInfo.cert.equals(chain[0]));
1098                             updatedAlias = true;
1099                             break;
1100                         }
1101                     }
1102 
1103                     if (!updatedAlias) {
1104                         // caller adding new entry
1105                         engineDeleteEntry(alias);
1106                         storePkey(alias, (KeyStore.PrivateKeyEntry)entry);
1107                     }
1108 
1109                 } catch (PKCS11Exception | CertificateException pe) {
1110                     throw new KeyStoreException(pe);
1111                 }
1112 
1113             } else if (entry instanceof KeyStore.SecretKeyEntry) {
1114 
1115                 KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;
1116                 SecretKey skey = ske.getSecretKey();
1117 
1118                 try {
1119                     // first check if the key already exists
1120                     AliasInfo aliasInfo = aliasMap.get(alias);
1121 
1122                     if (aliasInfo != null) {
1123                         engineDeleteEntry(alias);
1124                     }
1125                     storeSkey(alias, ske);
1126 
1127                 } catch (PKCS11Exception pe) {
1128                     throw new KeyStoreException(pe);
1129                 }
1130 
1131             } else {
1132                 throw new KeyStoreException(new UnsupportedOperationException
1133                     ("unsupported entry type: " + entry.getClass().getName()));
1134             }
1135 
1136             try {
1137 
1138                 // XXX  NSS does not write out the CKA_ID we pass to them
1139                 //
1140                 // therefore we must re-map labels
1141                 // (can not simply update aliasMap)
1142 
1143                 mapLabels();
1144                 if (debug != null) {
1145                     dumpTokenMap();
1146                 }
1147             } catch (PKCS11Exception | CertificateException pe) {
1148                 throw new KeyStoreException(pe);
1149             }
1150         }
1151 
1152         if (debug != null) {
1153             debug.println
1154                 ("engineSetEntry added new entry for [" +
1155                 alias +
1156                 "] to token");
1157         }
1158     }
1159 
1160     /**
1161      * Determines if the keystore <code>Entry</code> for the specified
1162      * <code>alias</code> is an instance or subclass of the specified
1163      * <code>entryClass</code>.
1164      *
1165      * @param alias the alias name
1166      * @param entryClass the entry class
1167      *
1168      * @return true if the keystore <code>Entry</code> for the specified
1169      *          <code>alias</code> is an instance or subclass of the
1170      *          specified <code>entryClass</code>, false otherwise
1171      */
1172     public synchronized boolean engineEntryInstanceOf
1173                 (String alias, Class<? extends KeyStore.Entry> entryClass) {
1174         token.ensureValid();
1175         return super.engineEntryInstanceOf(alias, entryClass);
1176     }
1177 
1178     private X509Certificate loadCert(Session session, long oHandle)
1179                 throws PKCS11Exception, CertificateException {
1180 
1181         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[]
1182                         { new CK_ATTRIBUTE(CKA_VALUE) };
1183         token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1184 
1185         byte[] bytes = attrs[0].getByteArray();
1186         if (bytes == null) {
1187             throw new CertificateException
1188                         ("unexpectedly retrieved null byte array");
1189         }
1190         CertificateFactory cf = CertificateFactory.getInstance("X.509");
1191         return (X509Certificate)cf.generateCertificate
1192                         (new ByteArrayInputStream(bytes));
1193     }
1194 
1195     private X509Certificate[] loadChain(Session session,
1196                                         X509Certificate endCert)
1197                 throws PKCS11Exception, CertificateException {
1198 
1199         ArrayList<X509Certificate> lChain = null;
1200 
1201         if (endCert.getSubjectX500Principal().equals
1202             (endCert.getIssuerX500Principal())) {
1203             // self signed
1204             return new X509Certificate[] { endCert };
1205         } else {
1206             lChain = new ArrayList<X509Certificate>();
1207             lChain.add(endCert);
1208         }
1209 
1210         // try loading remaining certs in chain by following
1211         // issuer->subject links
1212 
1213         X509Certificate next = endCert;
1214         while (true) {
1215             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1216                         ATTR_TOKEN_TRUE,
1217                         ATTR_CLASS_CERT,
1218                         new CK_ATTRIBUTE(CKA_SUBJECT,
1219                                 next.getIssuerX500Principal().getEncoded()) };
1220             long[] ch = findObjects(session, attrs);
1221 
1222             if (ch == null || ch.length == 0) {
1223                 // done
1224                 break;
1225             } else {
1226                 // if more than one found, use first
1227                 if (debug != null && ch.length > 1) {
1228                     debug.println("engineGetEntry found " +
1229                                 ch.length +
1230                                 " certificate entries for subject [" +
1231                                 next.getIssuerX500Principal().toString() +
1232                                 "] in token - using first entry");
1233                 }
1234 
1235                 next = loadCert(session, ch[0]);
1236                 lChain.add(next);
1237                 if (next.getSubjectX500Principal().equals
1238                     (next.getIssuerX500Principal())) {
1239                     // self signed
1240                     break;
1241                 }
1242             }
1243         }
1244 
1245         return lChain.toArray(new X509Certificate[lChain.size()]);
1246     }
1247 
1248     private SecretKey loadSkey(Session session, long oHandle)
1249                 throws PKCS11Exception {
1250 
1251         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1252                         new CK_ATTRIBUTE(CKA_KEY_TYPE) };
1253         token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1254         long kType = attrs[0].getLong();
1255 
1256         String keyType = null;
1257         int keyLength = -1;
1258 
1259         // XXX NSS mangles the stored key type for secret key token objects
1260 
1261         if (kType == CKK_DES || kType == CKK_DES3) {
1262             if (kType == CKK_DES) {
1263                 keyType = "DES";
1264                 keyLength = 64;
1265             } else if (kType == CKK_DES3) {
1266                 keyType = "DESede";
1267                 keyLength = 192;
1268             }
1269         } else {
1270             if (kType == CKK_AES) {
1271                 keyType = "AES";
1272             } else if (kType == CKK_BLOWFISH) {
1273                 keyType = "Blowfish";
1274             } else if (kType == CKK_RC4) {
1275                 keyType = "ARCFOUR";
1276             } else {
1277                 if (debug != null) {
1278                     debug.println("unknown key type [" +
1279                                 kType +
1280                                 "] - using 'Generic Secret'");
1281                 }
1282                 keyType = "Generic Secret";
1283             }
1284 
1285             // XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID?
1286             if (NSS_TEST) {
1287                 keyLength = 128;
1288             } else {
1289                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) };
1290                 token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1291                 keyLength = (int)attrs[0].getLong();
1292             }
1293         }
1294 
1295         return P11Key.secretKey(session, oHandle, keyType, keyLength, null);
1296     }
1297 
1298     private PrivateKey loadPkey(Session session, long oHandle)
1299         throws PKCS11Exception, KeyStoreException {
1300 
1301         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1302                         new CK_ATTRIBUTE(CKA_KEY_TYPE) };
1303         token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1304         long kType = attrs[0].getLong();
1305         String keyType = null;
1306         int keyLength = 0;
1307 
1308         if (kType == CKK_RSA) {
1309 
1310             keyType = "RSA";
1311 
1312             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) };
1313             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1314             BigInteger modulus = attrs[0].getBigInteger();
1315             keyLength = modulus.bitLength();
1316 
1317             // This check will combine our "don't care" values here
1318             // with the system-wide min/max values.
1319             try {
1320                 RSAKeyFactory.checkKeyLengths(keyLength, null,
1321                     -1, Integer.MAX_VALUE);
1322             } catch (InvalidKeyException e) {
1323                 throw new KeyStoreException(e.getMessage());
1324             }
1325 
1326             return P11Key.privateKey(session,
1327                                 oHandle,
1328                                 keyType,
1329                                 keyLength,
1330                                 null);
1331 
1332         } else if (kType == CKK_DSA) {
1333 
1334             keyType = "DSA";
1335 
1336             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
1337             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1338             BigInteger prime = attrs[0].getBigInteger();
1339             keyLength = prime.bitLength();
1340 
1341             return P11Key.privateKey(session,
1342                                 oHandle,
1343                                 keyType,
1344                                 keyLength,
1345                                 null);
1346 
1347         } else if (kType == CKK_DH) {
1348 
1349             keyType = "DH";
1350 
1351             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
1352             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1353             BigInteger prime = attrs[0].getBigInteger();
1354             keyLength = prime.bitLength();
1355 
1356             return P11Key.privateKey(session,
1357                                 oHandle,
1358                                 keyType,
1359                                 keyLength,
1360                                 null);
1361 
1362         } else if (kType == CKK_EC) {
1363 
1364             attrs = new CK_ATTRIBUTE[] {
1365                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
1366             };
1367             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1368             byte[] encodedParams = attrs[0].getByteArray();
1369             try {
1370                 ECParameterSpec params =
1371                     ECUtil.getECParameterSpec(null, encodedParams);
1372                 keyLength = params.getCurve().getField().getFieldSize();
1373             } catch (IOException e) {
1374                 // we do not want to accept key with unsupported parameters
1375                 throw new KeyStoreException("Unsupported parameters", e);
1376             }
1377 
1378             return P11Key.privateKey(session, oHandle, "EC", keyLength, null);
1379 
1380         } else {
1381             if (debug != null) {
1382                 debug.println("unknown key type [" + kType + "]");
1383             }
1384             throw new KeyStoreException("unknown key type");
1385         }
1386     }
1387 
1388 
1389     /**
1390      * XXX  On ibutton, when you C_SetAttribute(CKA_ID) for a private key
1391      *      it not only changes the CKA_ID of the private key,
1392      *      it changes the CKA_ID of the corresponding cert too.
1393      *      And vice versa.
1394      *
1395      * XXX  On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID)
1396      *      for a private key, and then try to delete the corresponding cert.
1397      *      So this code reverses the order.
1398      *      After the cert is first destroyed (if necessary),
1399      *      then the CKA_ID of the private key can be changed successfully.
1400      *
1401      * @param replaceCert if true, then caller is updating alias info for
1402      *                  existing cert (only update CKA_ID/CKA_LABEL).
1403      *                  if false, then caller is updating cert chain
1404      *                  (delete old end cert and add new chain).
1405      */
1406     private void updatePkey(String alias,
1407                         byte[] cka_id,
1408                         X509Certificate[] chain,
1409                         boolean replaceCert) throws
1410                 KeyStoreException, CertificateException, PKCS11Exception {
1411 
1412         // XXX
1413         //
1414         // always set replaceCert to true
1415         //
1416         // NSS does not allow resetting of CKA_LABEL on an existing cert
1417         // (C_SetAttribute call succeeds, but is ignored)
1418 
1419         replaceCert = true;
1420 
1421         Session session = null;
1422         try {
1423             session = token.getOpSession();
1424 
1425             // first get private key object handle and hang onto it
1426 
1427             THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
1428             long pKeyHandle;
1429             if (h.type == ATTR_CLASS_PKEY) {
1430                 pKeyHandle = h.handle;
1431             } else {
1432                 throw new KeyStoreException
1433                         ("expected but could not find private key " +
1434                         "with CKA_ID " +
1435                         getID(cka_id));
1436             }
1437 
1438             // next find existing end entity cert
1439 
1440             h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1441             if (h.type != ATTR_CLASS_CERT) {
1442                 throw new KeyStoreException
1443                         ("expected but could not find certificate " +
1444                         "with CKA_ID " +
1445                         getID(cka_id));
1446             } else {
1447                 if (replaceCert) {
1448                     // replacing existing cert and chain
1449                     destroyChain(cka_id);
1450                 } else {
1451                     // renaming alias for existing cert
1452                     CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1453                         new CK_ATTRIBUTE(CKA_LABEL, alias),
1454                         new CK_ATTRIBUTE(CKA_ID, alias) };
1455                     token.p11.C_SetAttributeValue
1456                         (session.id(), h.handle, attrs);
1457                 }
1458             }
1459 
1460             // add new chain
1461 
1462             if (replaceCert) {
1463                 // add all certs in chain
1464                 storeChain(alias, chain);
1465             } else {
1466                 // already updated alias info for existing end cert -
1467                 // just update CA certs
1468                 storeCaCerts(chain, 1);
1469             }
1470 
1471             // finally update CKA_ID for private key
1472             //
1473             // ibutton may have already done this (that is ok)
1474 
1475             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1476                                 new CK_ATTRIBUTE(CKA_ID, alias) };
1477             token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs);
1478 
1479             if (debug != null) {
1480                 debug.println("updatePkey set new alias [" +
1481                                 alias +
1482                                 "] for private key entry");
1483             }
1484         } finally {
1485             token.releaseSession(session);
1486         }
1487     }
1488 
1489     // retrieves the native key handle and either update it directly or make a copy
1490     private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)
1491                 throws PKCS11Exception {
1492 
1493         // if token key, update alias.
1494         // if session key, convert to token key.
1495 
1496         Session session = null;
1497         long keyID = key.getKeyID();
1498         try {
1499             session = token.getOpSession();
1500             if (key.tokenObject == true) {
1501                 // token key - set new CKA_ID
1502 
1503                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1504                                 new CK_ATTRIBUTE(CKA_ID, alias) };
1505                 token.p11.C_SetAttributeValue
1506                                 (session.id(), keyID, attrs);
1507                 if (debug != null) {
1508                     debug.println("updateP11Pkey set new alias [" +
1509                                 alias +
1510                                 "] for key entry");
1511                 }
1512             } else {
1513                 // session key - convert to token key and set CKA_ID
1514 
1515                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1516                     ATTR_TOKEN_TRUE,
1517                     new CK_ATTRIBUTE(CKA_ID, alias),
1518                 };
1519                 if (attribute != null) {
1520                     attrs = addAttribute(attrs, attribute);
1521                 }
1522                 // creates a new token key with the desired CKA_ID
1523                 token.p11.C_CopyObject(session.id(), keyID, attrs);
1524                 if (debug != null) {
1525                     debug.println("updateP11Pkey copied private session key " +
1526                                 "for [" +
1527                                 alias +
1528                                 "] to token entry");
1529                 }
1530             }
1531         } finally {
1532             token.releaseSession(session);
1533             key.releaseKeyID();
1534         }
1535     }
1536 
1537     private void storeCert(String alias, X509Certificate cert)
1538                 throws PKCS11Exception, CertificateException {
1539 
1540         ArrayList<CK_ATTRIBUTE> attrList = new ArrayList<CK_ATTRIBUTE>();
1541         attrList.add(ATTR_TOKEN_TRUE);
1542         attrList.add(ATTR_CLASS_CERT);
1543         attrList.add(ATTR_X509_CERT_TYPE);
1544         attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT,
1545                                 cert.getSubjectX500Principal().getEncoded()));
1546         attrList.add(new CK_ATTRIBUTE(CKA_ISSUER,
1547                                 cert.getIssuerX500Principal().getEncoded()));
1548         attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER,
1549                                 cert.getSerialNumber().toByteArray()));
1550         attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded()));
1551 
1552         if (alias != null) {
1553             attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias));
1554             attrList.add(new CK_ATTRIBUTE(CKA_ID, alias));
1555         } else {
1556             // ibutton requires something to be set
1557             // - alias must be unique
1558             attrList.add(new CK_ATTRIBUTE(CKA_ID,
1559                         getID(cert.getSubjectX500Principal().getName
1560                                         (X500Principal.CANONICAL), cert)));
1561         }
1562 
1563         Session session = null;
1564         try {
1565             session = token.getOpSession();
1566             token.p11.C_CreateObject(session.id(),
1567                         attrList.toArray(new CK_ATTRIBUTE[attrList.size()]));
1568         } finally {
1569             token.releaseSession(session);
1570         }
1571     }
1572 
1573     private void storeChain(String alias, X509Certificate[] chain)
1574                 throws PKCS11Exception, CertificateException {
1575 
1576         // add new chain
1577         //
1578         // end cert has CKA_LABEL and CKA_ID set to alias.
1579         // other certs in chain have neither set.
1580 
1581         storeCert(alias, chain[0]);
1582         storeCaCerts(chain, 1);
1583     }
1584 
1585     private void storeCaCerts(X509Certificate[] chain, int start)
1586                 throws PKCS11Exception, CertificateException {
1587 
1588         // do not add duplicate CA cert if already in token
1589         //
1590         // XXX   ibutton stores duplicate CA certs, NSS does not
1591 
1592         Session session = null;
1593         HashSet<X509Certificate> cacerts = new HashSet<X509Certificate>();
1594         try {
1595             session = token.getOpSession();
1596             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1597                         ATTR_TOKEN_TRUE,
1598                         ATTR_CLASS_CERT };
1599             long[] handles = findObjects(session, attrs);
1600 
1601             // load certs currently on the token
1602             for (long handle : handles) {
1603                 cacerts.add(loadCert(session, handle));
1604             }
1605         } finally {
1606             token.releaseSession(session);
1607         }
1608 
1609         for (int i = start; i < chain.length; i++) {
1610             if (!cacerts.contains(chain[i])) {
1611                 storeCert(null, chain[i]);
1612             } else if (debug != null) {
1613                 debug.println("ignoring duplicate CA cert for [" +
1614                         chain[i].getSubjectX500Principal() +
1615                         "]");
1616             }
1617         }
1618     }
1619 
1620     private void storeSkey(String alias, KeyStore.SecretKeyEntry ske)
1621                 throws PKCS11Exception, KeyStoreException {
1622 
1623         SecretKey skey = ske.getSecretKey();
1624         // No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since
1625         // they are handled in P11SecretKeyFactory.createKey() method.
1626         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1627             ATTR_SKEY_TOKEN_TRUE,
1628             ATTR_PRIVATE_TRUE,
1629             new CK_ATTRIBUTE(CKA_LABEL, alias),
1630         };
1631         try {
1632             P11SecretKeyFactory.convertKey(token, skey, null, attrs);
1633         } catch (InvalidKeyException ike) {
1634             // re-throw KeyStoreException to match javadoc
1635             throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);
1636         }
1637 
1638         // update global alias map
1639         aliasMap.put(alias, new AliasInfo(alias));
1640 
1641         if (debug != null) {
1642             debug.println("storeSkey created token secret key for [" +
1643                           alias + "]");
1644         }
1645     }
1646 
1647     private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) {
1648         int n = attrs.length;
1649         CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1];
1650         System.arraycopy(attrs, 0, newAttrs, 0, n);
1651         newAttrs[n] = attr;
1652         return newAttrs;
1653     }
1654 
1655     private void storePkey(String alias, KeyStore.PrivateKeyEntry pke)
1656         throws PKCS11Exception, CertificateException, KeyStoreException  {
1657 
1658         PrivateKey key = pke.getPrivateKey();
1659         CK_ATTRIBUTE[] attrs = null;
1660 
1661         // If the key is a token object on this token, update it instead
1662         // of creating a duplicate key object.
1663         // Otherwise, treat a P11Key like any other key, if is is extractable.
1664         if (key instanceof P11Key) {
1665             P11Key p11Key = (P11Key)key;
1666             if (p11Key.tokenObject && (p11Key.token == this.token)) {
1667                 updateP11Pkey(alias, null, p11Key);
1668                 storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1669                 return;
1670             }
1671         }
1672 
1673         boolean useNDB = token.config.getNssNetscapeDbWorkaround();
1674         PublicKey publicKey = pke.getCertificate().getPublicKey();
1675 
1676         if (key instanceof RSAPrivateKey) {
1677 
1678             X509Certificate cert = (X509Certificate)pke.getCertificate();
1679             attrs = getRsaPrivKeyAttrs
1680                 (alias, (RSAPrivateKey)key, cert.getSubjectX500Principal());
1681 
1682         } else if (key instanceof DSAPrivateKey) {
1683 
1684             DSAPrivateKey dsaKey = (DSAPrivateKey)key;
1685 
1686             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1687             if (idAttrs[0] == null) {
1688                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1689             }
1690 
1691             attrs = new CK_ATTRIBUTE[] {
1692                 ATTR_TOKEN_TRUE,
1693                 ATTR_CLASS_PKEY,
1694                 ATTR_PRIVATE_TRUE,
1695                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),
1696                 idAttrs[0],
1697                 new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()),
1698                 new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()),
1699                 new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()),
1700                 new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()),
1701             };
1702             if (idAttrs[1] != null) {
1703                 attrs = addAttribute(attrs, idAttrs[1]);
1704             }
1705 
1706             attrs = token.getAttributes
1707                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs);
1708 
1709             if (debug != null) {
1710                 debug.println("storePkey created DSA template");
1711             }
1712 
1713         } else if (key instanceof DHPrivateKey) {
1714 
1715             DHPrivateKey dhKey = (DHPrivateKey)key;
1716 
1717             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1718             if (idAttrs[0] == null) {
1719                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1720             }
1721 
1722             attrs = new CK_ATTRIBUTE[] {
1723                 ATTR_TOKEN_TRUE,
1724                 ATTR_CLASS_PKEY,
1725                 ATTR_PRIVATE_TRUE,
1726                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),
1727                 idAttrs[0],
1728                 new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()),
1729                 new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()),
1730                 new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()),
1731             };
1732             if (idAttrs[1] != null) {
1733                 attrs = addAttribute(attrs, idAttrs[1]);
1734             }
1735 
1736             attrs = token.getAttributes
1737                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs);
1738 
1739         } else if (key instanceof ECPrivateKey) {
1740 
1741             ECPrivateKey ecKey = (ECPrivateKey)key;
1742 
1743             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1744             if (idAttrs[0] == null) {
1745                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1746             }
1747 
1748             byte[] encodedParams =
1749                 ECUtil.encodeECParameterSpec(null, ecKey.getParams());
1750             attrs = new CK_ATTRIBUTE[] {
1751                 ATTR_TOKEN_TRUE,
1752                 ATTR_CLASS_PKEY,
1753                 ATTR_PRIVATE_TRUE,
1754                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
1755                 idAttrs[0],
1756                 new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()),
1757                 new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
1758             };
1759             if (idAttrs[1] != null) {
1760                 attrs = addAttribute(attrs, idAttrs[1]);
1761             }
1762 
1763             attrs = token.getAttributes
1764                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs);
1765 
1766             if (debug != null) {
1767                 debug.println("storePkey created EC template");
1768             }
1769 
1770         } else if (key instanceof P11Key) {
1771             // sensitive/non-extractable P11Key
1772             P11Key p11Key = (P11Key)key;
1773             if (p11Key.token != this.token) {
1774                 throw new KeyStoreException
1775                     ("Cannot move sensitive keys across tokens");
1776             }
1777             CK_ATTRIBUTE netscapeDB = null;
1778             if (useNDB) {
1779                 // Note that this currently fails due to an NSS bug.
1780                 // They do not allow the CKA_NETSCAPE_DB attribute to be
1781                 // specified during C_CopyObject() and fail with
1782                 // CKR_ATTRIBUTE_READ_ONLY.
1783                 // But if we did not specify it, they would fail with
1784                 // CKA_TEMPLATE_INCOMPLETE, so leave this code in here.
1785                 CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true);
1786                 netscapeDB = idAttrs[1];
1787             }
1788             // Update the key object.
1789             updateP11Pkey(alias, netscapeDB, p11Key);
1790             storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1791             return;
1792 
1793         } else {
1794             throw new KeyStoreException("unsupported key type: " + key);
1795         }
1796 
1797         Session session = null;
1798         try {
1799             session = token.getOpSession();
1800 
1801             // create private key entry
1802             token.p11.C_CreateObject(session.id(), attrs);
1803             if (debug != null) {
1804                 debug.println("storePkey created token key for [" +
1805                                 alias +
1806                                 "]");
1807             }
1808         } finally {
1809             token.releaseSession(session);
1810         }
1811 
1812         storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1813     }
1814 
1815     private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias,
1816                                 RSAPrivateKey key,
1817                                 X500Principal subject) throws PKCS11Exception {
1818 
1819         // subject is currently ignored - could be used to set CKA_SUBJECT
1820 
1821         CK_ATTRIBUTE[] attrs = null;
1822         if (key instanceof RSAPrivateCrtKey) {
1823 
1824             if (debug != null) {
1825                 debug.println("creating RSAPrivateCrtKey attrs");
1826             }
1827 
1828             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key;
1829 
1830             attrs = new CK_ATTRIBUTE[] {
1831                 ATTR_TOKEN_TRUE,
1832                 ATTR_CLASS_PKEY,
1833                 ATTR_PRIVATE_TRUE,
1834                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
1835                 new CK_ATTRIBUTE(CKA_ID, alias),
1836                 new CK_ATTRIBUTE(CKA_MODULUS,
1837                                 rsaKey.getModulus()),
1838                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
1839                                 rsaKey.getPrivateExponent()),
1840                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT,
1841                                 rsaKey.getPublicExponent()),
1842                 new CK_ATTRIBUTE(CKA_PRIME_1,
1843                                 rsaKey.getPrimeP()),
1844                 new CK_ATTRIBUTE(CKA_PRIME_2,
1845                                 rsaKey.getPrimeQ()),
1846                 new CK_ATTRIBUTE(CKA_EXPONENT_1,
1847                                 rsaKey.getPrimeExponentP()),
1848                 new CK_ATTRIBUTE(CKA_EXPONENT_2,
1849                                 rsaKey.getPrimeExponentQ()),
1850                 new CK_ATTRIBUTE(CKA_COEFFICIENT,
1851                                 rsaKey.getCrtCoefficient()) };
1852             attrs = token.getAttributes
1853                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
1854 
1855         } else {
1856 
1857             if (debug != null) {
1858                 debug.println("creating RSAPrivateKey attrs");
1859             }
1860 
1861             RSAPrivateKey rsaKey = key;
1862 
1863             attrs = new CK_ATTRIBUTE[] {
1864                 ATTR_TOKEN_TRUE,
1865                 ATTR_CLASS_PKEY,
1866                 ATTR_PRIVATE_TRUE,
1867                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
1868                 new CK_ATTRIBUTE(CKA_ID, alias),
1869                 new CK_ATTRIBUTE(CKA_MODULUS,
1870                                 rsaKey.getModulus()),
1871                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
1872                                 rsaKey.getPrivateExponent()) };
1873             attrs = token.getAttributes
1874                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
1875         }
1876 
1877         return attrs;
1878     }
1879 
1880     /**
1881      * Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be
1882      * used for this private key. It uses the same algorithm to calculate the
1883      * values as NSS. The public and private keys MUST match for the result to
1884      * be correct.
1885      *
1886      * It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB
1887      * at index 1. The boolean flags determine what is to be calculated.
1888      * If false or if we could not calculate the value, that element is null.
1889      *
1890      * NOTE that we currently do not use the CKA_ID value calculated by this
1891      * method.
1892      */
1893     private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey,
1894             PublicKey publicKey, boolean id, boolean netscapeDb) {
1895         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2];
1896         if ((id || netscapeDb) == false) {
1897             return attrs;
1898         }
1899         String alg = privateKey.getAlgorithm();
1900         if (alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {
1901             if (id) {
1902                 BigInteger n = ((RSAPublicKey)publicKey).getModulus();
1903                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));
1904             }
1905             // CKA_NETSCAPE_DB not needed for RSA public keys
1906         } else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) {
1907             BigInteger y = ((DSAPublicKey)publicKey).getY();
1908             if (id) {
1909                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
1910             }
1911             if (netscapeDb) {
1912                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
1913             }
1914         } else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) {
1915             BigInteger y = ((DHPublicKey)publicKey).getY();
1916             if (id) {
1917                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
1918             }
1919             if (netscapeDb) {
1920                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
1921             }
1922         } else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) {
1923             ECPublicKey ecPub = (ECPublicKey)publicKey;
1924             ECPoint point = ecPub.getW();
1925             ECParameterSpec params = ecPub.getParams();
1926             byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve());
1927             if (id) {
1928                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint));
1929             }
1930             if (netscapeDb) {
1931                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint);
1932             }
1933         } else {
1934             throw new RuntimeException("Unknown key algorithm " + alg);
1935         }
1936         return attrs;
1937     }
1938 
1939     /**
1940      * return true if cert destroyed
1941      */
1942     private boolean destroyCert(byte[] cka_id)
1943                 throws PKCS11Exception, KeyStoreException {
1944         Session session = null;
1945         try {
1946             session = token.getOpSession();
1947             THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1948             if (h.type != ATTR_CLASS_CERT) {
1949                 return false;
1950             }
1951 
1952             token.p11.C_DestroyObject(session.id(), h.handle);
1953             if (debug != null) {
1954                 debug.println("destroyCert destroyed cert with CKA_ID [" +
1955                                                 getID(cka_id) +
1956                                                 "]");
1957             }
1958             return true;
1959         } finally {
1960             token.releaseSession(session);
1961         }
1962     }
1963 
1964     /**
1965      * return true if chain destroyed
1966      */
1967     private boolean destroyChain(byte[] cka_id)
1968         throws PKCS11Exception, CertificateException, KeyStoreException {
1969 
1970         Session session = null;
1971         try {
1972             session = token.getOpSession();
1973 
1974             THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1975             if (h.type != ATTR_CLASS_CERT) {
1976                 if (debug != null) {
1977                     debug.println("destroyChain could not find " +
1978                         "end entity cert with CKA_ID [0x" +
1979                         Functions.toHexString(cka_id) +
1980                         "]");
1981                 }
1982                 return false;
1983             }
1984 
1985             X509Certificate endCert = loadCert(session, h.handle);
1986             token.p11.C_DestroyObject(session.id(), h.handle);
1987             if (debug != null) {
1988                 debug.println("destroyChain destroyed end entity cert " +
1989                         "with CKA_ID [" +
1990                         getID(cka_id) +
1991                         "]");
1992             }
1993 
1994             // build chain following issuer->subject links
1995 
1996             X509Certificate next = endCert;
1997             while (true) {
1998 
1999                 if (next.getSubjectX500Principal().equals
2000                     (next.getIssuerX500Principal())) {
2001                     // self signed - done
2002                     break;
2003                 }
2004 
2005                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
2006                         ATTR_TOKEN_TRUE,
2007                         ATTR_CLASS_CERT,
2008                         new CK_ATTRIBUTE(CKA_SUBJECT,
2009                                   next.getIssuerX500Principal().getEncoded()) };
2010                 long[] ch = findObjects(session, attrs);
2011 
2012                 if (ch == null || ch.length == 0) {
2013                     // done
2014                     break;
2015                 } else {
2016                     // if more than one found, use first
2017                     if (debug != null && ch.length > 1) {
2018                         debug.println("destroyChain found " +
2019                                 ch.length +
2020                                 " certificate entries for subject [" +
2021                                 next.getIssuerX500Principal() +
2022                                 "] in token - using first entry");
2023                     }
2024 
2025                     next = loadCert(session, ch[0]);
2026 
2027                     // only delete if not part of any other chain
2028 
2029                     attrs = new CK_ATTRIBUTE[] {
2030                         ATTR_TOKEN_TRUE,
2031                         ATTR_CLASS_CERT,
2032                         new CK_ATTRIBUTE(CKA_ISSUER,
2033                                 next.getSubjectX500Principal().getEncoded()) };
2034                     long[] issuers = findObjects(session, attrs);
2035 
2036                     boolean destroyIt = false;
2037                     if (issuers == null || issuers.length == 0) {
2038                         // no other certs with this issuer -
2039                         // destroy it
2040                         destroyIt = true;
2041                     } else if (issuers.length == 1) {
2042                         X509Certificate iCert = loadCert(session, issuers[0]);
2043                         if (next.equals(iCert)) {
2044                             // only cert with issuer is itself (self-signed) -
2045                             // destroy it
2046                             destroyIt = true;
2047                         }
2048                     }
2049 
2050                     if (destroyIt) {
2051                         token.p11.C_DestroyObject(session.id(), ch[0]);
2052                         if (debug != null) {
2053                             debug.println
2054                                 ("destroyChain destroyed cert in chain " +
2055                                 "with subject [" +
2056                                 next.getSubjectX500Principal() + "]");
2057                         }
2058                     } else {
2059                         if (debug != null) {
2060                             debug.println("destroyChain did not destroy " +
2061                                 "shared cert in chain with subject [" +
2062                                 next.getSubjectX500Principal() + "]");
2063                         }
2064                     }
2065                 }
2066             }
2067 
2068             return true;
2069 
2070         } finally {
2071             token.releaseSession(session);
2072         }
2073     }
2074 
2075     /**
2076      * return true if secret key destroyed
2077      */
2078     private boolean destroySkey(String alias)
2079                 throws PKCS11Exception, KeyStoreException {
2080         Session session = null;
2081         try {
2082             session = token.getOpSession();
2083 
2084             THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias);
2085             if (h.type != ATTR_CLASS_SKEY) {
2086                 if (debug != null) {
2087                     debug.println("destroySkey did not find secret key " +
2088                         "with CKA_LABEL [" +
2089                         alias +
2090                         "]");
2091                 }
2092                 return false;
2093             }
2094             token.p11.C_DestroyObject(session.id(), h.handle);
2095             return true;
2096         } finally {
2097             token.releaseSession(session);
2098         }
2099     }
2100 
2101     /**
2102      * return true if private key destroyed
2103      */
2104     private boolean destroyPkey(byte[] cka_id)
2105                 throws PKCS11Exception, KeyStoreException {
2106         Session session = null;
2107         try {
2108             session = token.getOpSession();
2109 
2110             THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
2111             if (h.type != ATTR_CLASS_PKEY) {
2112                 if (debug != null) {
2113                     debug.println
2114                         ("destroyPkey did not find private key with CKA_ID [" +
2115                         getID(cka_id) +
2116                         "]");
2117                 }
2118                 return false;
2119             }
2120             token.p11.C_DestroyObject(session.id(), h.handle);
2121             return true;
2122         } finally {
2123             token.releaseSession(session);
2124         }
2125     }
2126 
2127     /**
2128      * build [alias + issuer + serialNumber] string from a cert
2129      */
2130     private String getID(String alias, X509Certificate cert) {
2131         X500Principal issuer = cert.getIssuerX500Principal();
2132         BigInteger serialNum = cert.getSerialNumber();
2133 
2134         return alias +
2135                 ALIAS_SEP +
2136                 issuer.getName(X500Principal.CANONICAL) +
2137                 ALIAS_SEP +
2138                 serialNum.toString();
2139     }
2140 
2141     /**
2142      * build CKA_ID string from bytes
2143      */
2144     private static String getID(byte[] bytes) {
2145         boolean printable = true;
2146         for (int i = 0; i < bytes.length; i++) {
2147             if (!DerValue.isPrintableStringChar((char)bytes[i])) {
2148                 printable = false;
2149                 break;
2150             }
2151         }
2152 
2153         if (!printable) {
2154             return "0x" + Functions.toHexString(bytes);
2155         } else {
2156             try {
2157                 return new String(bytes, "UTF-8");
2158             } catch (UnsupportedEncodingException uee) {
2159                 return "0x" + Functions.toHexString(bytes);
2160             }
2161         }
2162     }
2163 
2164     /**
2165      * find an object on the token
2166      *
2167      * @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY
2168      * @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY
2169      * @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY
2170      */
2171     private THandle getTokenObject(Session session,
2172                                 CK_ATTRIBUTE type,
2173                                 byte[] cka_id,
2174                                 String cka_label)
2175                 throws PKCS11Exception, KeyStoreException {
2176 
2177         CK_ATTRIBUTE[] attrs;
2178         if (type == ATTR_CLASS_SKEY) {
2179             attrs = new CK_ATTRIBUTE[] {
2180                         ATTR_SKEY_TOKEN_TRUE,
2181                         new CK_ATTRIBUTE(CKA_LABEL, cka_label),
2182                         type };
2183         } else {
2184             attrs = new CK_ATTRIBUTE[] {
2185                         ATTR_TOKEN_TRUE,
2186                         new CK_ATTRIBUTE(CKA_ID, cka_id),
2187                         type };
2188         }
2189         long[] h = findObjects(session, attrs);
2190         if (h.length == 0) {
2191             if (debug != null) {
2192                 if (type == ATTR_CLASS_SKEY) {
2193                     debug.println("getTokenObject did not find secret key " +
2194                                 "with CKA_LABEL [" +
2195                                 cka_label +
2196                                 "]");
2197                 } else if (type == ATTR_CLASS_CERT) {
2198                     debug.println
2199                         ("getTokenObject did not find cert with CKA_ID [" +
2200                         getID(cka_id) +
2201                         "]");
2202                 } else {
2203                     debug.println("getTokenObject did not find private key " +
2204                         "with CKA_ID [" +
2205                         getID(cka_id) +
2206                         "]");
2207                 }
2208             }
2209         } else if (h.length == 1) {
2210 
2211             // found object handle - return it
2212             return new THandle(h[0], type);
2213 
2214         } else {
2215 
2216             // found multiple object handles -
2217             // see if token ignored CKA_LABEL during search (e.g. NSS)
2218 
2219             if (type == ATTR_CLASS_SKEY) {
2220 
2221                 ArrayList<THandle> list = new ArrayList<THandle>(h.length);
2222                 for (int i = 0; i < h.length; i++) {
2223 
2224                     CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[]
2225                                         { new CK_ATTRIBUTE(CKA_LABEL) };
2226                     token.p11.C_GetAttributeValue(session.id(), h[i], label);
2227                     if (label[0].pValue != null &&
2228                         cka_label.equals(new String(label[0].getCharArray()))) {
2229                         list.add(new THandle(h[i], ATTR_CLASS_SKEY));
2230                     }
2231                 }
2232                 if (list.size() == 1) {
2233                     // yes, there was only one CKA_LABEL that matched
2234                     return list.get(0);
2235                 } else {
2236                     throw new KeyStoreException("invalid KeyStore state: " +
2237                         "found " +
2238                         list.size() +
2239                         " secret keys sharing CKA_LABEL [" +
2240                         cka_label +
2241                         "]");
2242                 }
2243             } else if (type == ATTR_CLASS_CERT) {
2244                 throw new KeyStoreException("invalid KeyStore state: " +
2245                         "found " +
2246                         h.length +
2247                         " certificates sharing CKA_ID " +
2248                         getID(cka_id));
2249             } else {
2250                 throw new KeyStoreException("invalid KeyStore state: " +
2251                         "found " +
2252                         h.length +
2253                         " private keys sharing CKA_ID " +
2254                         getID(cka_id));
2255             }
2256         }
2257         return new THandle(NO_HANDLE, null);
2258     }
2259 
2260     /**
2261      * Create a mapping of all key pairs, trusted certs, and secret keys
2262      * on the token into logical KeyStore entries unambiguously
2263      * accessible via an alias.
2264      *
2265      * If the token is removed, the map may contain stale values.
2266      * KeyStore.load should be called to re-create the map.
2267      *
2268      * Assume all private keys and matching certs share a unique CKA_ID.
2269      *
2270      * Assume all secret keys have a unique CKA_LABEL.
2271      *
2272      * @return true if multiple certs found sharing the same CKA_LABEL
2273      *          (if so, write capabilities are disabled)
2274      */
2275     private boolean mapLabels() throws
2276                 PKCS11Exception, CertificateException, KeyStoreException {
2277 
2278         CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] {
2279                                 new CK_ATTRIBUTE(CKA_TRUSTED) };
2280 
2281         Session session = null;
2282         try {
2283             session = token.getOpSession();
2284 
2285             // get all private key CKA_IDs
2286 
2287             ArrayList<byte[]> pkeyIDs = new ArrayList<byte[]>();
2288             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
2289                 ATTR_TOKEN_TRUE,
2290                 ATTR_CLASS_PKEY,
2291             };
2292             long[] handles = findObjects(session, attrs);
2293 
2294             for (long handle : handles) {
2295                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
2296                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2297 
2298                 if (attrs[0].pValue != null) {
2299                     pkeyIDs.add(attrs[0].getByteArray());
2300                 }
2301             }
2302 
2303             // Get all certificates
2304             //
2305             // If cert does not have a CKA_LABEL nor CKA_ID, it is ignored.
2306             //
2307             // Get the CKA_LABEL for each cert
2308             // (if the cert does not have a CKA_LABEL, use the CKA_ID).
2309             //
2310             // Map each cert to the its CKA_LABEL
2311             // (multiple certs may be mapped to a single CKA_LABEL)
2312 
2313             HashMap<String, HashSet<AliasInfo>> certMap =
2314                                 new HashMap<String, HashSet<AliasInfo>>();
2315 
2316             attrs = new CK_ATTRIBUTE[] {
2317                 ATTR_TOKEN_TRUE,
2318                 ATTR_CLASS_CERT,
2319             };
2320             handles = findObjects(session, attrs);
2321 
2322             for (long handle : handles) {
2323                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
2324 
2325                 String cka_label = null;
2326                 byte[] cka_id = null;
2327                 try {
2328                     token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2329                     if (attrs[0].pValue != null) {
2330                         // there is a CKA_LABEL
2331                         cka_label = new String(attrs[0].getCharArray());
2332                     }
2333                 } catch (PKCS11Exception pe) {
2334                     if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) {
2335                         throw pe;
2336                     }
2337 
2338                     // GetAttributeValue for CKA_LABEL not supported
2339                     //
2340                     // XXX SCA1000
2341                 }
2342 
2343                 // get CKA_ID
2344 
2345                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
2346                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2347                 if (attrs[0].pValue == null) {
2348                     if (cka_label == null) {
2349                         // no cka_label nor cka_id - ignore
2350                         continue;
2351                     }
2352                 } else {
2353                     if (cka_label == null) {
2354                         // use CKA_ID as CKA_LABEL
2355                         cka_label = getID(attrs[0].getByteArray());
2356                     }
2357                     cka_id = attrs[0].getByteArray();
2358                 }
2359 
2360                 X509Certificate cert = loadCert(session, handle);
2361 
2362                 // get CKA_TRUSTED
2363 
2364                 boolean cka_trusted = false;
2365 
2366                 if (useSecmodTrust) {
2367                     cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType);
2368                 } else {
2369                     if (CKA_TRUSTED_SUPPORTED) {
2370                         try {
2371                             token.p11.C_GetAttributeValue
2372                                     (session.id(), handle, trustedAttr);
2373                             cka_trusted = trustedAttr[0].getBoolean();
2374                         } catch (PKCS11Exception pe) {
2375                             if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) {
2376                                 // XXX  NSS, ibutton, sca1000
2377                                 CKA_TRUSTED_SUPPORTED = false;
2378                                 if (debug != null) {
2379                                     debug.println
2380                                             ("CKA_TRUSTED attribute not supported");
2381                                 }
2382                             }
2383                         }
2384                     }
2385                 }
2386 
2387                 HashSet<AliasInfo> infoSet = certMap.get(cka_label);
2388                 if (infoSet == null) {
2389                     infoSet = new HashSet<AliasInfo>(2);
2390                     certMap.put(cka_label, infoSet);
2391                 }
2392 
2393                 // initially create private key entry AliasInfo entries -
2394                 // these entries will get resolved into their true
2395                 // entry types later
2396 
2397                 infoSet.add(new AliasInfo
2398                                 (cka_label,
2399                                 cka_id,
2400                                 cka_trusted,
2401                                 cert));
2402             }
2403 
2404             // create list secret key CKA_LABELS -
2405             // if there are duplicates (either between secret keys,
2406             // or between a secret key and another object),
2407             // throw an exception
2408             HashMap<String, AliasInfo> sKeyMap =
2409                     new HashMap<String, AliasInfo>();
2410 
2411             attrs = new CK_ATTRIBUTE[] {
2412                 ATTR_SKEY_TOKEN_TRUE,
2413                 ATTR_CLASS_SKEY,
2414             };
2415             handles = findObjects(session, attrs);
2416 
2417             for (long handle : handles) {
2418                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
2419                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2420                 if (attrs[0].pValue != null) {
2421 
2422                     // there is a CKA_LABEL
2423                     String cka_label = new String(attrs[0].getCharArray());
2424                     if (sKeyMap.get(cka_label) == null) {
2425                         sKeyMap.put(cka_label, new AliasInfo(cka_label));
2426                     } else {
2427                         throw new KeyStoreException("invalid KeyStore state: " +
2428                                 "found multiple secret keys sharing same " +
2429                                 "CKA_LABEL [" +
2430                                 cka_label +
2431                                 "]");
2432                     }
2433                 }
2434             }
2435 
2436             // update global aliasMap with alias mappings
2437             ArrayList<AliasInfo> matchedCerts =
2438                                 mapPrivateKeys(pkeyIDs, certMap);
2439             boolean sharedLabel = mapCerts(matchedCerts, certMap);
2440             mapSecretKeys(sKeyMap);
2441 
2442             return sharedLabel;
2443 
2444         } finally {
2445             token.releaseSession(session);
2446         }
2447     }
2448 
2449     /**
2450      * for each private key CKA_ID, find corresponding cert with same CKA_ID.
2451      * if found cert, see if cert CKA_LABEL is unique.
2452      *     if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL.
2453      *     if CKA_LABEL not unique, map private key/cert alias to:
2454      *                   CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL
2455      * if cert not found, ignore private key
2456      * (don't support private key entries without a cert chain yet)
2457      *
2458      * @return a list of AliasInfo entries that represents all matches
2459      */
2460     private ArrayList<AliasInfo> mapPrivateKeys(ArrayList<byte[]> pkeyIDs,
2461                         HashMap<String, HashSet<AliasInfo>> certMap)
2462                 throws PKCS11Exception, CertificateException {
2463 
2464         // reset global alias map
2465         aliasMap = new HashMap<String, AliasInfo>();
2466 
2467         // list of matched certs that we will return
2468         ArrayList<AliasInfo> matchedCerts = new ArrayList<AliasInfo>();
2469 
2470         for (byte[] pkeyID : pkeyIDs) {
2471 
2472             // try to find a matching CKA_ID in a certificate
2473 
2474             boolean foundMatch = false;
2475             Set<String> certLabels = certMap.keySet();
2476             for (String certLabel : certLabels) {
2477 
2478                 // get cert CKA_IDs (if present) for each cert
2479 
2480                 HashSet<AliasInfo> infoSet = certMap.get(certLabel);
2481                 for (AliasInfo aliasInfo : infoSet) {
2482                     if (Arrays.equals(pkeyID, aliasInfo.id)) {
2483 
2484                         // found private key with matching cert
2485 
2486                         if (infoSet.size() == 1) {
2487                             // unique CKA_LABEL - use certLabel as alias
2488                             aliasInfo.matched = true;
2489                             aliasMap.put(certLabel, aliasInfo);
2490                         } else {
2491                             // create new alias
2492                             aliasInfo.matched = true;
2493                             aliasMap.put(getID(certLabel, aliasInfo.cert),
2494                                         aliasInfo);
2495                         }
2496                         matchedCerts.add(aliasInfo);
2497                         foundMatch = true;
2498                         break;
2499                     }
2500                 }
2501                 if (foundMatch) {
2502                     break;
2503                 }
2504             }
2505 
2506             if (!foundMatch) {
2507                 if (debug != null) {
2508                     debug.println
2509                         ("did not find match for private key with CKA_ID [" +
2510                         getID(pkeyID) +
2511                         "] (ignoring entry)");
2512                 }
2513             }
2514         }
2515 
2516         return matchedCerts;
2517     }
2518 
2519     /**
2520      * for each cert not matched with a private key but is CKA_TRUSTED:
2521      *     if CKA_LABEL unique, map cert to CKA_LABEL.
2522      *     if CKA_LABEL not unique, map cert to [label+issuer+serialNum]
2523      *
2524      * if CKA_TRUSTED not supported, treat all certs not part of a chain
2525      * as trusted
2526      *
2527      * @return true if multiple certs found sharing the same CKA_LABEL
2528      */
2529     private boolean mapCerts(ArrayList<AliasInfo> matchedCerts,
2530                         HashMap<String, HashSet<AliasInfo>> certMap)
2531                 throws PKCS11Exception, CertificateException {
2532 
2533         // load all cert chains
2534         for (AliasInfo aliasInfo : matchedCerts) {
2535             Session session = null;
2536             try {
2537                 session = token.getOpSession();
2538                 aliasInfo.chain = loadChain(session, aliasInfo.cert);
2539             } finally {
2540                 token.releaseSession(session);
2541             }
2542         }
2543 
2544         // find all certs in certMap not part of a cert chain
2545         // - these are trusted
2546 
2547         boolean sharedLabel = false;
2548 
2549         Set<String> certLabels = certMap.keySet();
2550         for (String certLabel : certLabels) {
2551             HashSet<AliasInfo> infoSet = certMap.get(certLabel);
2552             for (AliasInfo aliasInfo : infoSet) {
2553 
2554                 if (aliasInfo.matched == true) {
2555                     // already found a private key match for this cert -
2556                     // just continue
2557                     aliasInfo.trusted = false;
2558                     continue;
2559                 }
2560 
2561                 // cert in this aliasInfo is not matched yet
2562                 //
2563                 // if CKA_TRUSTED_SUPPORTED == true,
2564                 // then check if cert is trusted
2565 
2566                 if (CKA_TRUSTED_SUPPORTED) {
2567                     if (aliasInfo.trusted) {
2568                         // trusted certificate
2569                         if (mapTrustedCert
2570                                 (certLabel, aliasInfo, infoSet) == true) {
2571                             sharedLabel = true;
2572                         }
2573                     }
2574                     continue;
2575                 }
2576 
2577                 // CKA_TRUSTED_SUPPORTED == false
2578                 //
2579                 // XXX treat all certs not part of a chain as trusted
2580                 // XXX
2581                 // XXX Unsupported
2582                 //
2583                 // boolean partOfChain = false;
2584                 // for (AliasInfo matchedInfo : matchedCerts) {
2585                 //     for (int i = 0; i < matchedInfo.chain.length; i++) {
2586                 //      if (matchedInfo.chain[i].equals(aliasInfo.cert)) {
2587                 //          partOfChain = true;
2588                 //          break;
2589                 //      }
2590                 //     }
2591                 //     if (partOfChain) {
2592                 //      break;
2593                 //     }
2594                 // }
2595                 //
2596                 // if (!partOfChain) {
2597                 //     if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){
2598                 //      sharedLabel = true;
2599                 //     }
2600                 // } else {
2601                 //    if (debug != null) {
2602                 //      debug.println("ignoring unmatched/untrusted cert " +
2603                 //          "that is part of cert chain - cert subject is [" +
2604                 //          aliasInfo.cert.getSubjectX500Principal().getName
2605                 //                              (X500Principal.CANONICAL) +
2606                 //          "]");
2607                 //     }
2608                 // }
2609             }
2610         }
2611 
2612         return sharedLabel;
2613     }
2614 
2615     private boolean mapTrustedCert(String certLabel,
2616                                 AliasInfo aliasInfo,
2617                                 HashSet<AliasInfo> infoSet) {
2618 
2619         boolean sharedLabel = false;
2620 
2621         aliasInfo.type = ATTR_CLASS_CERT;
2622         aliasInfo.trusted = true;
2623         if (infoSet.size() == 1) {
2624             // unique CKA_LABEL - use certLabel as alias
2625             aliasMap.put(certLabel, aliasInfo);
2626         } else {
2627             // create new alias
2628             sharedLabel = true;
2629             aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo);
2630         }
2631 
2632         return sharedLabel;
2633     }
2634 
2635     /**
2636      * If the secret key shares a CKA_LABEL with another entry,
2637      * throw an exception
2638      */
2639     private void mapSecretKeys(HashMap<String, AliasInfo> sKeyMap)
2640                 throws KeyStoreException {
2641         for (String label : sKeyMap.keySet()) {
2642             if (aliasMap.containsKey(label)) {
2643                 throw new KeyStoreException("invalid KeyStore state: " +
2644                         "found secret key sharing CKA_LABEL [" +
2645                         label +
2646                         "] with another token object");
2647             }
2648         }
2649         aliasMap.putAll(sKeyMap);
2650     }
2651 
2652     private void dumpTokenMap() {
2653         Set<String> aliases = aliasMap.keySet();
2654         System.out.println("Token Alias Map:");
2655         if (aliases.isEmpty()) {
2656             System.out.println("  [empty]");
2657         } else {
2658             for (String s : aliases) {
2659                 System.out.println("  " + s + aliasMap.get(s));
2660             }
2661         }
2662     }
2663 
2664     private void checkWrite() throws KeyStoreException {
2665         if (writeDisabled) {
2666             throw new KeyStoreException
2667                 ("This PKCS11KeyStore does not support write capabilities");
2668         }
2669     }
2670 
2671     private final static long[] LONG0 = new long[0];
2672 
2673     private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs)
2674             throws PKCS11Exception {
2675         Token token = session.token;
2676         long[] handles = LONG0;
2677         token.p11.C_FindObjectsInit(session.id(), attrs);
2678         while (true) {
2679             long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX);
2680             if (h.length == 0) {
2681                 break;
2682             }
2683             handles = P11Util.concat(handles, h);
2684         }
2685         token.p11.C_FindObjectsFinal(session.id());
2686         return handles;
2687     }
2688 
2689 }