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