History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/pkcs11/P11KeyStore.java (1 of 18352 files). Show all

Unified · Split

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