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