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