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     }
1296 
1297     private PrivateKey loadPkey(Session session, long oHandle)
1298         throws PKCS11Exception, KeyStoreException {
1299 
1300         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1301                         new CK_ATTRIBUTE(CKA_KEY_TYPE) };
1302         token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1303         long kType = attrs[0].getLong();
1304         String keyType = null;
1305         int keyLength = 0;
1306 
1307         if (kType == CKK_RSA) {
1308 
1309             keyType = "RSA";
1310 
1311             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) };
1312             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1313             BigInteger modulus = attrs[0].getBigInteger();
1314             keyLength = modulus.bitLength();
1315 
1316             // This check will combine our "don't care" values here
1317             // with the system-wide min/max values.
1318             try {
1319                 RSAKeyFactory.checkKeyLengths(keyLength, null,
1320                     -1, Integer.MAX_VALUE);
1321             } catch (InvalidKeyException e) {
1322                 throw new KeyStoreException(e.getMessage());
1323             }
1324 
1325             return P11Key.privateKey(session,
1326                                 oHandle,
1327                                 keyType,
1328                                 keyLength,
1329                                 null);
1330 
1331         } else if (kType == CKK_DSA) {
1332 
1333             keyType = "DSA";
1334 
1335             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
1336             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1337             BigInteger prime = attrs[0].getBigInteger();
1338             keyLength = prime.bitLength();
1339 
1340             return P11Key.privateKey(session,
1341                                 oHandle,
1342                                 keyType,
1343                                 keyLength,
1344                                 null);
1345 
1346         } else if (kType == CKK_DH) {
1347 
1348             keyType = "DH";
1349 
1350             attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
1351             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1352             BigInteger prime = attrs[0].getBigInteger();
1353             keyLength = prime.bitLength();
1354 
1355             return P11Key.privateKey(session,
1356                                 oHandle,
1357                                 keyType,
1358                                 keyLength,
1359                                 null);
1360 
1361         } else if (kType == CKK_EC) {
1362 
1363             attrs = new CK_ATTRIBUTE[] {
1364                 new CK_ATTRIBUTE(CKA_EC_PARAMS),
1365             };
1366             token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1367             byte[] encodedParams = attrs[0].getByteArray();
1368             try {
1369                 ECParameterSpec params =
1370                     ECUtil.getECParameterSpec(null, encodedParams);
1371                 keyLength = params.getCurve().getField().getFieldSize();
1372             } catch (IOException e) {
1373                 // we do not want to accept key with unsupported parameters
1374                 throw new KeyStoreException("Unsupported parameters", e);
1375             }
1376 
1377             return P11Key.privateKey(session, oHandle, "EC", keyLength, null);
1378 
1379         } else {
1380             if (debug != null) {
1381                 debug.println("unknown key type [" + kType + "]");
1382             }
1383             throw new KeyStoreException("unknown key type");
1384         }
1385     }
1386 
1387 
1388     /**
1389      * XXX  On ibutton, when you C_SetAttribute(CKA_ID) for a private key
1390      *      it not only changes the CKA_ID of the private key,
1391      *      it changes the CKA_ID of the corresponding cert too.
1392      *      And vice versa.
1393      *
1394      * XXX  On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID)
1395      *      for a private key, and then try to delete the corresponding cert.
1396      *      So this code reverses the order.
1397      *      After the cert is first destroyed (if necessary),
1398      *      then the CKA_ID of the private key can be changed successfully.
1399      *
1400      * @param replaceCert if true, then caller is updating alias info for
1401      *                  existing cert (only update CKA_ID/CKA_LABEL).
1402      *                  if false, then caller is updating cert chain
1403      *                  (delete old end cert and add new chain).
1404      */
1405     private void updatePkey(String alias,
1406                         byte[] cka_id,
1407                         X509Certificate[] chain,
1408                         boolean replaceCert) throws
1409                 KeyStoreException, CertificateException, PKCS11Exception {
1410 
1411         // XXX
1412         //
1413         // always set replaceCert to true
1414         //
1415         // NSS does not allow resetting of CKA_LABEL on an existing cert
1416         // (C_SetAttribute call succeeds, but is ignored)
1417 
1418         replaceCert = true;
1419 
1420         Session session = null;
1421         try {
1422             session = token.getOpSession();
1423 
1424             // first get private key object handle and hang onto it
1425 
1426             THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
1427             long pKeyHandle;
1428             if (h.type == ATTR_CLASS_PKEY) {
1429                 pKeyHandle = h.handle;
1430             } else {
1431                 throw new KeyStoreException
1432                         ("expected but could not find private key " +
1433                         "with CKA_ID " +
1434                         getID(cka_id));
1435             }
1436 
1437             // next find existing end entity cert
1438 
1439             h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1440             if (h.type != ATTR_CLASS_CERT) {
1441                 throw new KeyStoreException
1442                         ("expected but could not find certificate " +
1443                         "with CKA_ID " +
1444                         getID(cka_id));
1445             } else {
1446                 if (replaceCert) {
1447                     // replacing existing cert and chain
1448                     destroyChain(cka_id);
1449                 } else {
1450                     // renaming alias for existing cert
1451                     CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1452                         new CK_ATTRIBUTE(CKA_LABEL, alias),
1453                         new CK_ATTRIBUTE(CKA_ID, alias) };
1454                     token.p11.C_SetAttributeValue
1455                         (session.id(), h.handle, attrs);
1456                 }
1457             }
1458 
1459             // add new chain
1460 
1461             if (replaceCert) {
1462                 // add all certs in chain
1463                 storeChain(alias, chain);
1464             } else {
1465                 // already updated alias info for existing end cert -
1466                 // just update CA certs
1467                 storeCaCerts(chain, 1);
1468             }
1469 
1470             // finally update CKA_ID for private key
1471             //
1472             // ibutton may have already done this (that is ok)
1473 
1474             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1475                                 new CK_ATTRIBUTE(CKA_ID, alias) };
1476             token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs);
1477 
1478             if (debug != null) {
1479                 debug.println("updatePkey set new alias [" +
1480                                 alias +
1481                                 "] for private key entry");
1482             }
1483         } finally {
1484             token.releaseSession(session);
1485         }
1486     }
1487 
1488     private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)
1489                 throws PKCS11Exception {
1490 
1491         // if token key, update alias.
1492         // if session key, convert to token key.
1493 
1494         Session session = null;
1495         try {
1496             session = token.getOpSession();
1497             if (key.tokenObject == true) {
1498 
1499                 // token key - set new CKA_ID
1500 
1501                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1502                                 new CK_ATTRIBUTE(CKA_ID, alias) };
1503                 token.p11.C_SetAttributeValue
1504                                 (session.id(), key.keyID, attrs);
1505                 if (debug != null) {
1506                     debug.println("updateP11Pkey set new alias [" +
1507                                 alias +
1508                                 "] for key entry");
1509                 }
1510             } else {
1511 
1512                 // session key - convert to token key and set CKA_ID
1513 
1514                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1515                     ATTR_TOKEN_TRUE,
1516                     new CK_ATTRIBUTE(CKA_ID, alias),
1517                 };
1518                 if (attribute != null) {
1519                     attrs = addAttribute(attrs, attribute);
1520                 }
1521                 token.p11.C_CopyObject(session.id(), key.keyID, attrs);
1522                 if (debug != null) {
1523                     debug.println("updateP11Pkey copied private session key " +
1524                                 "for [" +
1525                                 alias +
1526                                 "] to token entry");
1527                 }
1528             }
1529         } finally {
1530             token.releaseSession(session);
1531         }
1532     }
1533 
1534     private void storeCert(String alias, X509Certificate cert)
1535                 throws PKCS11Exception, CertificateException {
1536 
1537         ArrayList<CK_ATTRIBUTE> attrList = new ArrayList<CK_ATTRIBUTE>();
1538         attrList.add(ATTR_TOKEN_TRUE);
1539         attrList.add(ATTR_CLASS_CERT);
1540         attrList.add(ATTR_X509_CERT_TYPE);
1541         attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT,
1542                                 cert.getSubjectX500Principal().getEncoded()));
1543         attrList.add(new CK_ATTRIBUTE(CKA_ISSUER,
1544                                 cert.getIssuerX500Principal().getEncoded()));
1545         attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER,
1546                                 cert.getSerialNumber().toByteArray()));
1547         attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded()));
1548 
1549         if (alias != null) {
1550             attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias));
1551             attrList.add(new CK_ATTRIBUTE(CKA_ID, alias));
1552         } else {
1553             // ibutton requires something to be set
1554             // - alias must be unique
1555             attrList.add(new CK_ATTRIBUTE(CKA_ID,
1556                         getID(cert.getSubjectX500Principal().getName
1557                                         (X500Principal.CANONICAL), cert)));
1558         }
1559 
1560         Session session = null;
1561         try {
1562             session = token.getOpSession();
1563             token.p11.C_CreateObject(session.id(),
1564                         attrList.toArray(new CK_ATTRIBUTE[attrList.size()]));
1565         } finally {
1566             token.releaseSession(session);
1567         }
1568     }
1569 
1570     private void storeChain(String alias, X509Certificate[] chain)
1571                 throws PKCS11Exception, CertificateException {
1572 
1573         // add new chain
1574         //
1575         // end cert has CKA_LABEL and CKA_ID set to alias.
1576         // other certs in chain have neither set.
1577 
1578         storeCert(alias, chain[0]);
1579         storeCaCerts(chain, 1);
1580     }
1581 
1582     private void storeCaCerts(X509Certificate[] chain, int start)
1583                 throws PKCS11Exception, CertificateException {
1584 
1585         // do not add duplicate CA cert if already in token
1586         //
1587         // XXX   ibutton stores duplicate CA certs, NSS does not
1588 
1589         Session session = null;
1590         HashSet<X509Certificate> cacerts = new HashSet<X509Certificate>();
1591         try {
1592             session = token.getOpSession();
1593             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1594                         ATTR_TOKEN_TRUE,
1595                         ATTR_CLASS_CERT };
1596             long[] handles = findObjects(session, attrs);
1597 
1598             // load certs currently on the token
1599             for (long handle : handles) {
1600                 cacerts.add(loadCert(session, handle));
1601             }
1602         } finally {
1603             token.releaseSession(session);
1604         }
1605 
1606         for (int i = start; i < chain.length; i++) {
1607             if (!cacerts.contains(chain[i])) {
1608                 storeCert(null, chain[i]);
1609             } else if (debug != null) {
1610                 debug.println("ignoring duplicate CA cert for [" +
1611                         chain[i].getSubjectX500Principal() +
1612                         "]");
1613             }
1614         }
1615     }
1616 
1617     private void storeSkey(String alias, KeyStore.SecretKeyEntry ske)
1618                 throws PKCS11Exception, KeyStoreException {
1619 
1620         SecretKey skey = ske.getSecretKey();
1621         // No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since
1622         // they are handled in P11SecretKeyFactory.createKey() method.
1623         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1624             ATTR_SKEY_TOKEN_TRUE,
1625             ATTR_PRIVATE_TRUE,
1626             new CK_ATTRIBUTE(CKA_LABEL, alias),
1627         };
1628         try {
1629             P11SecretKeyFactory.convertKey(token, skey, null, attrs);
1630         } catch (InvalidKeyException ike) {
1631             // re-throw KeyStoreException to match javadoc
1632             throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);
1633         }
1634 
1635         // update global alias map
1636         aliasMap.put(alias, new AliasInfo(alias));
1637 
1638         if (debug != null) {
1639             debug.println("storeSkey created token secret key for [" +
1640                           alias + "]");
1641         }
1642     }
1643 
1644     private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) {
1645         int n = attrs.length;
1646         CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1];
1647         System.arraycopy(attrs, 0, newAttrs, 0, n);
1648         newAttrs[n] = attr;
1649         return newAttrs;
1650     }
1651 
1652     private void storePkey(String alias, KeyStore.PrivateKeyEntry pke)
1653         throws PKCS11Exception, CertificateException, KeyStoreException  {
1654 
1655         PrivateKey key = pke.getPrivateKey();
1656         CK_ATTRIBUTE[] attrs = null;
1657 
1658         // If the key is a token object on this token, update it instead
1659         // of creating a duplicate key object.
1660         // Otherwise, treat a P11Key like any other key, if it is extractable.
1661         if (key instanceof P11Key) {
1662             P11Key p11Key = (P11Key)key;
1663             if (p11Key.tokenObject && (p11Key.token == this.token)) {
1664                 updateP11Pkey(alias, null, p11Key);
1665                 storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1666                 return;
1667             }
1668         }
1669 
1670         boolean useNDB = token.config.getNssNetscapeDbWorkaround();
1671         PublicKey publicKey = pke.getCertificate().getPublicKey();
1672 
1673         if (key instanceof RSAPrivateKey) {
1674 
1675             X509Certificate cert = (X509Certificate)pke.getCertificate();
1676             attrs = getRsaPrivKeyAttrs
1677                 (alias, (RSAPrivateKey)key, cert.getSubjectX500Principal());
1678 
1679         } else if (key instanceof DSAPrivateKey) {
1680 
1681             DSAPrivateKey dsaKey = (DSAPrivateKey)key;
1682 
1683             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1684             if (idAttrs[0] == null) {
1685                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1686             }
1687 
1688             attrs = new CK_ATTRIBUTE[] {
1689                 ATTR_TOKEN_TRUE,
1690                 ATTR_CLASS_PKEY,
1691                 ATTR_PRIVATE_TRUE,
1692                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),
1693                 idAttrs[0],
1694                 new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()),
1695                 new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()),
1696                 new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()),
1697                 new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()),
1698             };
1699             if (idAttrs[1] != null) {
1700                 attrs = addAttribute(attrs, idAttrs[1]);
1701             }
1702 
1703             attrs = token.getAttributes
1704                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs);
1705 
1706             if (debug != null) {
1707                 debug.println("storePkey created DSA template");
1708             }
1709 
1710         } else if (key instanceof DHPrivateKey) {
1711 
1712             DHPrivateKey dhKey = (DHPrivateKey)key;
1713 
1714             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1715             if (idAttrs[0] == null) {
1716                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1717             }
1718 
1719             attrs = new CK_ATTRIBUTE[] {
1720                 ATTR_TOKEN_TRUE,
1721                 ATTR_CLASS_PKEY,
1722                 ATTR_PRIVATE_TRUE,
1723                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),
1724                 idAttrs[0],
1725                 new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()),
1726                 new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()),
1727                 new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()),
1728             };
1729             if (idAttrs[1] != null) {
1730                 attrs = addAttribute(attrs, idAttrs[1]);
1731             }
1732 
1733             attrs = token.getAttributes
1734                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs);
1735 
1736         } else if (key instanceof ECPrivateKey) {
1737 
1738             ECPrivateKey ecKey = (ECPrivateKey)key;
1739 
1740             CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1741             if (idAttrs[0] == null) {
1742                 idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1743             }
1744 
1745             byte[] encodedParams =
1746                 ECUtil.encodeECParameterSpec(null, ecKey.getParams());
1747             attrs = new CK_ATTRIBUTE[] {
1748                 ATTR_TOKEN_TRUE,
1749                 ATTR_CLASS_PKEY,
1750                 ATTR_PRIVATE_TRUE,
1751                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
1752                 idAttrs[0],
1753                 new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()),
1754                 new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
1755             };
1756             if (idAttrs[1] != null) {
1757                 attrs = addAttribute(attrs, idAttrs[1]);
1758             }
1759 
1760             attrs = token.getAttributes
1761                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs);
1762 
1763             if (debug != null) {
1764                 debug.println("storePkey created EC template");
1765             }
1766 
1767         } else if (key instanceof P11Key) {
1768             // sensitive/non-extractable P11Key
1769             P11Key p11Key = (P11Key)key;
1770             if (p11Key.token != this.token) {
1771                 throw new KeyStoreException
1772                     ("Cannot move sensitive keys across tokens");
1773             }
1774             CK_ATTRIBUTE netscapeDB = null;
1775             if (useNDB) {
1776                 // Note that this currently fails due to an NSS bug.
1777                 // They do not allow the CKA_NETSCAPE_DB attribute to be
1778                 // specified during C_CopyObject() and fail with
1779                 // CKR_ATTRIBUTE_READ_ONLY.
1780                 // But if we did not specify it, they would fail with
1781                 // CKA_TEMPLATE_INCOMPLETE, so leave this code in here.
1782                 CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true);
1783                 netscapeDB = idAttrs[1];
1784             }
1785             // Update the key object.
1786             updateP11Pkey(alias, netscapeDB, p11Key);
1787             storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1788             return;
1789 
1790         } else {
1791             throw new KeyStoreException("unsupported key type: " + key);
1792         }
1793 
1794         Session session = null;
1795         try {
1796             session = token.getOpSession();
1797 
1798             // create private key entry
1799             token.p11.C_CreateObject(session.id(), attrs);
1800             if (debug != null) {
1801                 debug.println("storePkey created token key for [" +
1802                                 alias +
1803                                 "]");
1804             }
1805         } finally {
1806             token.releaseSession(session);
1807         }
1808 
1809         storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1810     }
1811 
1812     private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias,
1813                                 RSAPrivateKey key,
1814                                 X500Principal subject) throws PKCS11Exception {
1815 
1816         // subject is currently ignored - could be used to set CKA_SUBJECT
1817 
1818         CK_ATTRIBUTE[] attrs = null;
1819         if (key instanceof RSAPrivateCrtKey) {
1820 
1821             if (debug != null) {
1822                 debug.println("creating RSAPrivateCrtKey attrs");
1823             }
1824 
1825             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key;
1826 
1827             attrs = new CK_ATTRIBUTE[] {
1828                 ATTR_TOKEN_TRUE,
1829                 ATTR_CLASS_PKEY,
1830                 ATTR_PRIVATE_TRUE,
1831                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
1832                 new CK_ATTRIBUTE(CKA_ID, alias),
1833                 new CK_ATTRIBUTE(CKA_MODULUS,
1834                                 rsaKey.getModulus()),
1835                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
1836                                 rsaKey.getPrivateExponent()),
1837                 new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT,
1838                                 rsaKey.getPublicExponent()),
1839                 new CK_ATTRIBUTE(CKA_PRIME_1,
1840                                 rsaKey.getPrimeP()),
1841                 new CK_ATTRIBUTE(CKA_PRIME_2,
1842                                 rsaKey.getPrimeQ()),
1843                 new CK_ATTRIBUTE(CKA_EXPONENT_1,
1844                                 rsaKey.getPrimeExponentP()),
1845                 new CK_ATTRIBUTE(CKA_EXPONENT_2,
1846                                 rsaKey.getPrimeExponentQ()),
1847                 new CK_ATTRIBUTE(CKA_COEFFICIENT,
1848                                 rsaKey.getCrtCoefficient()) };
1849             attrs = token.getAttributes
1850                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
1851 
1852         } else {
1853 
1854             if (debug != null) {
1855                 debug.println("creating RSAPrivateKey attrs");
1856             }
1857 
1858             RSAPrivateKey rsaKey = key;
1859 
1860             attrs = new CK_ATTRIBUTE[] {
1861                 ATTR_TOKEN_TRUE,
1862                 ATTR_CLASS_PKEY,
1863                 ATTR_PRIVATE_TRUE,
1864                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
1865                 new CK_ATTRIBUTE(CKA_ID, alias),
1866                 new CK_ATTRIBUTE(CKA_MODULUS,
1867                                 rsaKey.getModulus()),
1868                 new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
1869                                 rsaKey.getPrivateExponent()) };
1870             attrs = token.getAttributes
1871                 (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
1872         }
1873 
1874         return attrs;
1875     }
1876 
1877     /**
1878      * Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be
1879      * used for this private key. It uses the same algorithm to calculate the
1880      * values as NSS. The public and private keys MUST match for the result to
1881      * be correct.
1882      *
1883      * It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB
1884      * at index 1. The boolean flags determine what is to be calculated.
1885      * If false or if we could not calculate the value, that element is null.
1886      *
1887      * NOTE that we currently do not use the CKA_ID value calculated by this
1888      * method.
1889      */
1890     private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey,
1891             PublicKey publicKey, boolean id, boolean netscapeDb) {
1892         CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2];
1893         if ((id || netscapeDb) == false) {
1894             return attrs;
1895         }
1896         String alg = privateKey.getAlgorithm();
1897         if (id && alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {
1898             // CKA_NETSCAPE_DB not needed for RSA public keys
1899             BigInteger n = ((RSAPublicKey)publicKey).getModulus();
1900             attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));
1901         } else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) {
1902             BigInteger y = ((DSAPublicKey)publicKey).getY();
1903             if (id) {
1904                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
1905             }
1906             if (netscapeDb) {
1907                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
1908             }
1909         } else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) {
1910             BigInteger y = ((DHPublicKey)publicKey).getY();
1911             if (id) {
1912                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
1913             }
1914             if (netscapeDb) {
1915                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
1916             }
1917         } else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) {
1918             ECPublicKey ecPub = (ECPublicKey)publicKey;
1919             ECPoint point = ecPub.getW();
1920             ECParameterSpec params = ecPub.getParams();
1921             byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve());
1922             if (id) {
1923                 attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint));
1924             }
1925             if (netscapeDb) {
1926                 attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint);
1927             }
1928         } else {
1929             throw new RuntimeException("Unknown key algorithm " + alg);
1930         }
1931         return attrs;
1932     }
1933 
1934     /**
1935      * return true if cert destroyed
1936      */
1937     private boolean destroyCert(byte[] cka_id)
1938                 throws PKCS11Exception, KeyStoreException {
1939         Session session = null;
1940         try {
1941             session = token.getOpSession();
1942             THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1943             if (h.type != ATTR_CLASS_CERT) {
1944                 return false;
1945             }
1946 
1947             token.p11.C_DestroyObject(session.id(), h.handle);
1948             if (debug != null) {
1949                 debug.println("destroyCert destroyed cert with CKA_ID [" +
1950                                                 getID(cka_id) +
1951                                                 "]");
1952             }
1953             return true;
1954         } finally {
1955             token.releaseSession(session);
1956         }
1957     }
1958 
1959     /**
1960      * return true if chain destroyed
1961      */
1962     private boolean destroyChain(byte[] cka_id)
1963         throws PKCS11Exception, CertificateException, KeyStoreException {
1964 
1965         Session session = null;
1966         try {
1967             session = token.getOpSession();
1968 
1969             THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1970             if (h.type != ATTR_CLASS_CERT) {
1971                 if (debug != null) {
1972                     debug.println("destroyChain could not find " +
1973                         "end entity cert with CKA_ID [0x" +
1974                         Functions.toHexString(cka_id) +
1975                         "]");
1976                 }
1977                 return false;
1978             }
1979 
1980             X509Certificate endCert = loadCert(session, h.handle);
1981             token.p11.C_DestroyObject(session.id(), h.handle);
1982             if (debug != null) {
1983                 debug.println("destroyChain destroyed end entity cert " +
1984                         "with CKA_ID [" +
1985                         getID(cka_id) +
1986                         "]");
1987             }
1988 
1989             // build chain following issuer->subject links
1990 
1991             X509Certificate next = endCert;
1992             while (true) {
1993 
1994                 if (next.getSubjectX500Principal().equals
1995                     (next.getIssuerX500Principal())) {
1996                     // self signed - done
1997                     break;
1998                 }
1999 
2000                 CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
2001                         ATTR_TOKEN_TRUE,
2002                         ATTR_CLASS_CERT,
2003                         new CK_ATTRIBUTE(CKA_SUBJECT,
2004                                   next.getIssuerX500Principal().getEncoded()) };
2005                 long[] ch = findObjects(session, attrs);
2006 
2007                 if (ch == null || ch.length == 0) {
2008                     // done
2009                     break;
2010                 } else {
2011                     // if more than one found, use first
2012                     if (debug != null && ch.length > 1) {
2013                         debug.println("destroyChain found " +
2014                                 ch.length +
2015                                 " certificate entries for subject [" +
2016                                 next.getIssuerX500Principal() +
2017                                 "] in token - using first entry");
2018                     }
2019 
2020                     next = loadCert(session, ch[0]);
2021 
2022                     // only delete if not part of any other chain
2023 
2024                     attrs = new CK_ATTRIBUTE[] {
2025                         ATTR_TOKEN_TRUE,
2026                         ATTR_CLASS_CERT,
2027                         new CK_ATTRIBUTE(CKA_ISSUER,
2028                                 next.getSubjectX500Principal().getEncoded()) };
2029                     long[] issuers = findObjects(session, attrs);
2030 
2031                     boolean destroyIt = false;
2032                     if (issuers == null || issuers.length == 0) {
2033                         // no other certs with this issuer -
2034                         // destroy it
2035                         destroyIt = true;
2036                     } else if (issuers.length == 1) {
2037                         X509Certificate iCert = loadCert(session, issuers[0]);
2038                         if (next.equals(iCert)) {
2039                             // only cert with issuer is itself (self-signed) -
2040                             // destroy it
2041                             destroyIt = true;
2042                         }
2043                     }
2044 
2045                     if (destroyIt) {
2046                         token.p11.C_DestroyObject(session.id(), ch[0]);
2047                         if (debug != null) {
2048                             debug.println
2049                                 ("destroyChain destroyed cert in chain " +
2050                                 "with subject [" +
2051                                 next.getSubjectX500Principal() + "]");
2052                         }
2053                     } else {
2054                         if (debug != null) {
2055                             debug.println("destroyChain did not destroy " +
2056                                 "shared cert in chain with subject [" +
2057                                 next.getSubjectX500Principal() + "]");
2058                         }
2059                     }
2060                 }
2061             }
2062 
2063             return true;
2064 
2065         } finally {
2066             token.releaseSession(session);
2067         }
2068     }
2069 
2070     /**
2071      * return true if secret key destroyed
2072      */
2073     private boolean destroySkey(String alias)
2074                 throws PKCS11Exception, KeyStoreException {
2075         Session session = null;
2076         try {
2077             session = token.getOpSession();
2078 
2079             THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias);
2080             if (h.type != ATTR_CLASS_SKEY) {
2081                 if (debug != null) {
2082                     debug.println("destroySkey did not find secret key " +
2083                         "with CKA_LABEL [" +
2084                         alias +
2085                         "]");
2086                 }
2087                 return false;
2088             }
2089             token.p11.C_DestroyObject(session.id(), h.handle);
2090             return true;
2091         } finally {
2092             token.releaseSession(session);
2093         }
2094     }
2095 
2096     /**
2097      * return true if private key destroyed
2098      */
2099     private boolean destroyPkey(byte[] cka_id)
2100                 throws PKCS11Exception, KeyStoreException {
2101         Session session = null;
2102         try {
2103             session = token.getOpSession();
2104 
2105             THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
2106             if (h.type != ATTR_CLASS_PKEY) {
2107                 if (debug != null) {
2108                     debug.println
2109                         ("destroyPkey did not find private key with CKA_ID [" +
2110                         getID(cka_id) +
2111                         "]");
2112                 }
2113                 return false;
2114             }
2115             token.p11.C_DestroyObject(session.id(), h.handle);
2116             return true;
2117         } finally {
2118             token.releaseSession(session);
2119         }
2120     }
2121 
2122     /**
2123      * build [alias + issuer + serialNumber] string from a cert
2124      */
2125     private String getID(String alias, X509Certificate cert) {
2126         X500Principal issuer = cert.getIssuerX500Principal();
2127         BigInteger serialNum = cert.getSerialNumber();
2128 
2129         return alias +
2130                 ALIAS_SEP +
2131                 issuer.getName(X500Principal.CANONICAL) +
2132                 ALIAS_SEP +
2133                 serialNum.toString();
2134     }
2135 
2136     /**
2137      * build CKA_ID string from bytes
2138      */
2139     private static String getID(byte[] bytes) {
2140         boolean printable = true;
2141         for (int i = 0; i < bytes.length; i++) {
2142             if (!DerValue.isPrintableStringChar((char)bytes[i])) {
2143                 printable = false;
2144                 break;
2145             }
2146         }
2147 
2148         if (!printable) {
2149             return "0x" + Functions.toHexString(bytes);
2150         } else {
2151             try {
2152                 return new String(bytes, "UTF-8");
2153             } catch (UnsupportedEncodingException uee) {
2154                 return "0x" + Functions.toHexString(bytes);
2155             }
2156         }
2157     }
2158 
2159     /**
2160      * find an object on the token
2161      *
2162      * @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY
2163      * @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY
2164      * @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY
2165      */
2166     private THandle getTokenObject(Session session,
2167                                 CK_ATTRIBUTE type,
2168                                 byte[] cka_id,
2169                                 String cka_label)
2170                 throws PKCS11Exception, KeyStoreException {
2171 
2172         CK_ATTRIBUTE[] attrs;
2173         if (type == ATTR_CLASS_SKEY) {
2174             attrs = new CK_ATTRIBUTE[] {
2175                         ATTR_SKEY_TOKEN_TRUE,
2176                         new CK_ATTRIBUTE(CKA_LABEL, cka_label),
2177                         type };
2178         } else {
2179             attrs = new CK_ATTRIBUTE[] {
2180                         ATTR_TOKEN_TRUE,
2181                         new CK_ATTRIBUTE(CKA_ID, cka_id),
2182                         type };
2183         }
2184         long[] h = findObjects(session, attrs);
2185         if (h.length == 0) {
2186             if (debug != null) {
2187                 if (type == ATTR_CLASS_SKEY) {
2188                     debug.println("getTokenObject did not find secret key " +
2189                                 "with CKA_LABEL [" +
2190                                 cka_label +
2191                                 "]");
2192                 } else if (type == ATTR_CLASS_CERT) {
2193                     debug.println
2194                         ("getTokenObject did not find cert with CKA_ID [" +
2195                         getID(cka_id) +
2196                         "]");
2197                 } else {
2198                     debug.println("getTokenObject did not find private key " +
2199                         "with CKA_ID [" +
2200                         getID(cka_id) +
2201                         "]");
2202                 }
2203             }
2204         } else if (h.length == 1) {
2205 
2206             // found object handle - return it
2207             return new THandle(h[0], type);
2208 
2209         } else {
2210 
2211             // found multiple object handles -
2212             // see if token ignored CKA_LABEL during search (e.g. NSS)
2213 
2214             if (type == ATTR_CLASS_SKEY) {
2215 
2216                 ArrayList<THandle> list = new ArrayList<THandle>(h.length);
2217                 for (int i = 0; i < h.length; i++) {
2218 
2219                     CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[]
2220                                         { new CK_ATTRIBUTE(CKA_LABEL) };
2221                     token.p11.C_GetAttributeValue(session.id(), h[i], label);
2222                     if (label[0].pValue != null &&
2223                         cka_label.equals(new String(label[0].getCharArray()))) {
2224                         list.add(new THandle(h[i], ATTR_CLASS_SKEY));
2225                     }
2226                 }
2227                 if (list.size() == 1) {
2228                     // yes, there was only one CKA_LABEL that matched
2229                     return list.get(0);
2230                 } else {
2231                     throw new KeyStoreException("invalid KeyStore state: " +
2232                         "found " +
2233                         list.size() +
2234                         " secret keys sharing CKA_LABEL [" +
2235                         cka_label +
2236                         "]");
2237                 }
2238             } else if (type == ATTR_CLASS_CERT) {
2239                 throw new KeyStoreException("invalid KeyStore state: " +
2240                         "found " +
2241                         h.length +
2242                         " certificates sharing CKA_ID " +
2243                         getID(cka_id));
2244             } else {
2245                 throw new KeyStoreException("invalid KeyStore state: " +
2246                         "found " +
2247                         h.length +
2248                         " private keys sharing CKA_ID " +
2249                         getID(cka_id));
2250             }
2251         }
2252         return new THandle(NO_HANDLE, null);
2253     }
2254 
2255     /**
2256      * Create a mapping of all key pairs, trusted certs, and secret keys
2257      * on the token into logical KeyStore entries unambiguously
2258      * accessible via an alias.
2259      *
2260      * If the token is removed, the map may contain stale values.
2261      * KeyStore.load should be called to re-create the map.
2262      *
2263      * Assume all private keys and matching certs share a unique CKA_ID.
2264      *
2265      * Assume all secret keys have a unique CKA_LABEL.
2266      *
2267      * @return true if multiple certs found sharing the same CKA_LABEL
2268      *          (if so, write capabilities are disabled)
2269      */
2270     private boolean mapLabels() throws
2271                 PKCS11Exception, CertificateException, KeyStoreException {
2272 
2273         CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] {
2274                                 new CK_ATTRIBUTE(CKA_TRUSTED) };
2275 
2276         Session session = null;
2277         try {
2278             session = token.getOpSession();
2279 
2280             // get all private key CKA_IDs
2281 
2282             ArrayList<byte[]> pkeyIDs = new ArrayList<byte[]>();
2283             CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
2284                 ATTR_TOKEN_TRUE,
2285                 ATTR_CLASS_PKEY,
2286             };
2287             long[] handles = findObjects(session, attrs);
2288 
2289             for (long handle : handles) {
2290                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
2291                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2292 
2293                 if (attrs[0].pValue != null) {
2294                     pkeyIDs.add(attrs[0].getByteArray());
2295                 }
2296             }
2297 
2298             // Get all certificates
2299             //
2300             // If cert does not have a CKA_LABEL nor CKA_ID, it is ignored.
2301             //
2302             // Get the CKA_LABEL for each cert
2303             // (if the cert does not have a CKA_LABEL, use the CKA_ID).
2304             //
2305             // Map each cert to the its CKA_LABEL
2306             // (multiple certs may be mapped to a single CKA_LABEL)
2307 
2308             HashMap<String, HashSet<AliasInfo>> certMap =
2309                                 new HashMap<String, HashSet<AliasInfo>>();
2310 
2311             attrs = new CK_ATTRIBUTE[] {
2312                 ATTR_TOKEN_TRUE,
2313                 ATTR_CLASS_CERT,
2314             };
2315             handles = findObjects(session, attrs);
2316 
2317             for (long handle : handles) {
2318                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
2319 
2320                 String cka_label = null;
2321                 byte[] cka_id = null;
2322                 try {
2323                     token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2324                     if (attrs[0].pValue != null) {
2325                         // there is a CKA_LABEL
2326                         cka_label = new String(attrs[0].getCharArray());
2327                     }
2328                 } catch (PKCS11Exception pe) {
2329                     if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) {
2330                         throw pe;
2331                     }
2332 
2333                     // GetAttributeValue for CKA_LABEL not supported
2334                     //
2335                     // XXX SCA1000
2336                 }
2337 
2338                 // get CKA_ID
2339 
2340                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
2341                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2342                 if (attrs[0].pValue == null) {
2343                     if (cka_label == null) {
2344                         // no cka_label nor cka_id - ignore
2345                         continue;
2346                     }
2347                 } else {
2348                     if (cka_label == null) {
2349                         // use CKA_ID as CKA_LABEL
2350                         cka_label = getID(attrs[0].getByteArray());
2351                     }
2352                     cka_id = attrs[0].getByteArray();
2353                 }
2354 
2355                 X509Certificate cert = loadCert(session, handle);
2356 
2357                 // get CKA_TRUSTED
2358 
2359                 boolean cka_trusted = false;
2360 
2361                 if (useSecmodTrust) {
2362                     cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType);
2363                 } else {
2364                     if (CKA_TRUSTED_SUPPORTED) {
2365                         try {
2366                             token.p11.C_GetAttributeValue
2367                                     (session.id(), handle, trustedAttr);
2368                             cka_trusted = trustedAttr[0].getBoolean();
2369                         } catch (PKCS11Exception pe) {
2370                             if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) {
2371                                 // XXX  NSS, ibutton, sca1000
2372                                 CKA_TRUSTED_SUPPORTED = false;
2373                                 if (debug != null) {
2374                                     debug.println
2375                                             ("CKA_TRUSTED attribute not supported");
2376                                 }
2377                             }
2378                         }
2379                     }
2380                 }
2381 
2382                 HashSet<AliasInfo> infoSet = certMap.get(cka_label);
2383                 if (infoSet == null) {
2384                     infoSet = new HashSet<AliasInfo>(2);
2385                     certMap.put(cka_label, infoSet);
2386                 }
2387 
2388                 // initially create private key entry AliasInfo entries -
2389                 // these entries will get resolved into their true
2390                 // entry types later
2391 
2392                 infoSet.add(new AliasInfo
2393                                 (cka_label,
2394                                 cka_id,
2395                                 cka_trusted,
2396                                 cert));
2397             }
2398 
2399             // create list secret key CKA_LABELS -
2400             // if there are duplicates (either between secret keys,
2401             // or between a secret key and another object),
2402             // throw an exception
2403             HashMap<String, AliasInfo> sKeyMap =
2404                     new HashMap<String, AliasInfo>();
2405 
2406             attrs = new CK_ATTRIBUTE[] {
2407                 ATTR_SKEY_TOKEN_TRUE,
2408                 ATTR_CLASS_SKEY,
2409             };
2410             handles = findObjects(session, attrs);
2411 
2412             for (long handle : handles) {
2413                 attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
2414                 token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2415                 if (attrs[0].pValue != null) {
2416 
2417                     // there is a CKA_LABEL
2418                     String cka_label = new String(attrs[0].getCharArray());
2419                     if (sKeyMap.get(cka_label) == null) {
2420                         sKeyMap.put(cka_label, new AliasInfo(cka_label));
2421                     } else {
2422                         throw new KeyStoreException("invalid KeyStore state: " +
2423                                 "found multiple secret keys sharing same " +
2424                                 "CKA_LABEL [" +
2425                                 cka_label +
2426                                 "]");
2427                     }
2428                 }
2429             }
2430 
2431             // update global aliasMap with alias mappings
2432             ArrayList<AliasInfo> matchedCerts =
2433                                 mapPrivateKeys(pkeyIDs, certMap);
2434             boolean sharedLabel = mapCerts(matchedCerts, certMap);
2435             mapSecretKeys(sKeyMap);
2436 
2437             return sharedLabel;
2438 
2439         } finally {
2440             token.releaseSession(session);
2441         }
2442     }
2443 
2444     /**
2445      * for each private key CKA_ID, find corresponding cert with same CKA_ID.
2446      * if found cert, see if cert CKA_LABEL is unique.
2447      *     if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL.
2448      *     if CKA_LABEL not unique, map private key/cert alias to:
2449      *                   CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL
2450      * if cert not found, ignore private key
2451      * (don't support private key entries without a cert chain yet)
2452      *
2453      * @return a list of AliasInfo entries that represents all matches
2454      */
2455     private ArrayList<AliasInfo> mapPrivateKeys(ArrayList<byte[]> pkeyIDs,
2456                         HashMap<String, HashSet<AliasInfo>> certMap)
2457                 throws PKCS11Exception, CertificateException {
2458 
2459         // reset global alias map
2460         aliasMap = new HashMap<String, AliasInfo>();
2461 
2462         // list of matched certs that we will return
2463         ArrayList<AliasInfo> matchedCerts = new ArrayList<AliasInfo>();
2464 
2465         for (byte[] pkeyID : pkeyIDs) {
2466 
2467             // try to find a matching CKA_ID in a certificate
2468 
2469             boolean foundMatch = false;
2470             Set<String> certLabels = certMap.keySet();
2471             for (String certLabel : certLabels) {
2472 
2473                 // get cert CKA_IDs (if present) for each cert
2474 
2475                 HashSet<AliasInfo> infoSet = certMap.get(certLabel);
2476                 for (AliasInfo aliasInfo : infoSet) {
2477                     if (Arrays.equals(pkeyID, aliasInfo.id)) {
2478 
2479                         // found private key with matching cert
2480 
2481                         if (infoSet.size() == 1) {
2482                             // unique CKA_LABEL - use certLabel as alias
2483                             aliasInfo.matched = true;
2484                             aliasMap.put(certLabel, aliasInfo);
2485                         } else {
2486                             // create new alias
2487                             aliasInfo.matched = true;
2488                             aliasMap.put(getID(certLabel, aliasInfo.cert),
2489                                         aliasInfo);
2490                         }
2491                         matchedCerts.add(aliasInfo);
2492                         foundMatch = true;
2493                         break;
2494                     }
2495                 }
2496                 if (foundMatch) {
2497                     break;
2498                 }
2499             }
2500 
2501             if (!foundMatch) {
2502                 if (debug != null) {
2503                     debug.println
2504                         ("did not find match for private key with CKA_ID [" +
2505                         getID(pkeyID) +
2506                         "] (ignoring entry)");
2507                 }
2508             }
2509         }
2510 
2511         return matchedCerts;
2512     }
2513 
2514     /**
2515      * for each cert not matched with a private key but is CKA_TRUSTED:
2516      *     if CKA_LABEL unique, map cert to CKA_LABEL.
2517      *     if CKA_LABEL not unique, map cert to [label+issuer+serialNum]
2518      *
2519      * if CKA_TRUSTED not supported, treat all certs not part of a chain
2520      * as trusted
2521      *
2522      * @return true if multiple certs found sharing the same CKA_LABEL
2523      */
2524     private boolean mapCerts(ArrayList<AliasInfo> matchedCerts,
2525                         HashMap<String, HashSet<AliasInfo>> certMap)
2526                 throws PKCS11Exception, CertificateException {
2527 
2528         // load all cert chains
2529         for (AliasInfo aliasInfo : matchedCerts) {
2530             Session session = null;
2531             try {
2532                 session = token.getOpSession();
2533                 aliasInfo.chain = loadChain(session, aliasInfo.cert);
2534             } finally {
2535                 token.releaseSession(session);
2536             }
2537         }
2538 
2539         // find all certs in certMap not part of a cert chain
2540         // - these are trusted
2541 
2542         boolean sharedLabel = false;
2543 
2544         Set<String> certLabels = certMap.keySet();
2545         for (String certLabel : certLabels) {
2546             HashSet<AliasInfo> infoSet = certMap.get(certLabel);
2547             for (AliasInfo aliasInfo : infoSet) {
2548 
2549                 if (aliasInfo.matched == true) {
2550                     // already found a private key match for this cert -
2551                     // just continue
2552                     aliasInfo.trusted = false;
2553                     continue;
2554                 }
2555 
2556                 // cert in this aliasInfo is not matched yet
2557                 //
2558                 // if CKA_TRUSTED_SUPPORTED == true,
2559                 // then check if cert is trusted
2560 
2561                 if (CKA_TRUSTED_SUPPORTED) {
2562                     if (aliasInfo.trusted) {
2563                         // trusted certificate
2564                         if (mapTrustedCert
2565                                 (certLabel, aliasInfo, infoSet) == true) {
2566                             sharedLabel = true;
2567                         }
2568                     }
2569                     continue;
2570                 }
2571 
2572                 // CKA_TRUSTED_SUPPORTED == false
2573                 //
2574                 // XXX treat all certs not part of a chain as trusted
2575                 // XXX
2576                 // XXX Unsupported
2577                 //
2578                 // boolean partOfChain = false;
2579                 // for (AliasInfo matchedInfo : matchedCerts) {
2580                 //     for (int i = 0; i < matchedInfo.chain.length; i++) {
2581                 //      if (matchedInfo.chain[i].equals(aliasInfo.cert)) {
2582                 //          partOfChain = true;
2583                 //          break;
2584                 //      }
2585                 //     }
2586                 //     if (partOfChain) {
2587                 //      break;
2588                 //     }
2589                 // }
2590                 //
2591                 // if (!partOfChain) {
2592                 //     if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){
2593                 //      sharedLabel = true;
2594                 //     }
2595                 // } else {
2596                 //    if (debug != null) {
2597                 //      debug.println("ignoring unmatched/untrusted cert " +
2598                 //          "that is part of cert chain - cert subject is [" +
2599                 //          aliasInfo.cert.getSubjectX500Principal().getName
2600                 //                              (X500Principal.CANONICAL) +
2601                 //          "]");
2602                 //     }
2603                 // }
2604             }
2605         }
2606 
2607         return sharedLabel;
2608     }
2609 
2610     private boolean mapTrustedCert(String certLabel,
2611                                 AliasInfo aliasInfo,
2612                                 HashSet<AliasInfo> infoSet) {
2613 
2614         boolean sharedLabel = false;
2615 
2616         aliasInfo.type = ATTR_CLASS_CERT;
2617         aliasInfo.trusted = true;
2618         if (infoSet.size() == 1) {
2619             // unique CKA_LABEL - use certLabel as alias
2620             aliasMap.put(certLabel, aliasInfo);
2621         } else {
2622             // create new alias
2623             sharedLabel = true;
2624             aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo);
2625         }
2626 
2627         return sharedLabel;
2628     }
2629 
2630     /**
2631      * If the secret key shares a CKA_LABEL with another entry,
2632      * throw an exception
2633      */
2634     private void mapSecretKeys(HashMap<String, AliasInfo> sKeyMap)
2635                 throws KeyStoreException {
2636         for (String label : sKeyMap.keySet()) {
2637             if (aliasMap.containsKey(label)) {
2638                 throw new KeyStoreException("invalid KeyStore state: " +
2639                         "found secret key sharing CKA_LABEL [" +
2640                         label +
2641                         "] with another token object");
2642             }
2643         }
2644         aliasMap.putAll(sKeyMap);
2645     }
2646 
2647     private void dumpTokenMap() {
2648         Set<String> aliases = aliasMap.keySet();
2649         System.out.println("Token Alias Map:");
2650         if (aliases.isEmpty()) {
2651             System.out.println("  [empty]");
2652         } else {
2653             for (String s : aliases) {
2654                 System.out.println("  " + s + aliasMap.get(s));
2655             }
2656         }
2657     }
2658 
2659     private void checkWrite() throws KeyStoreException {
2660         if (writeDisabled) {
2661             throw new KeyStoreException
2662                 ("This PKCS11KeyStore does not support write capabilities");
2663         }
2664     }
2665 
2666     private final static long[] LONG0 = new long[0];
2667 
2668     private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs)
2669             throws PKCS11Exception {
2670         Token token = session.token;
2671         long[] handles = LONG0;
2672         token.p11.C_FindObjectsInit(session.id(), attrs);
2673         while (true) {
2674             long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX);
2675             if (h.length == 0) {
2676                 break;
2677             }
2678             handles = P11Util.concat(handles, h);
2679         }
2680         token.p11.C_FindObjectsFinal(session.id());
2681         return handles;
2682     }
2683 
2684 }