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