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