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+
26+package sun.security.pkcs11;
27+
28+import java.math.BigInteger;
29+
30+import java.io.InputStream;
31+import java.io.OutputStream;
32+import java.io.IOException;
33+import java.io.ByteArrayInputStream;
34+import java.io.UnsupportedEncodingException;
35+
36+import java.util.Arrays;
37+import java.util.Collection;
38+import java.util.Collections;
39+import java.util.Date;
40+import java.util.Enumeration;
41+import java.util.Iterator;
42+import java.util.List;
43+import java.util.ArrayList;
44+import java.util.HashSet;
45+import java.util.HashMap;
46+import java.util.Set;
47+
48+import java.security.*;
49+import java.security.KeyStore.*;
50+
51+import java.security.cert.CertPath;
52+import java.security.cert.Certificate;
53+import java.security.cert.X509Certificate;
54+import java.security.cert.CertificateFactory;
55+import java.security.cert.CertificateException;
56+
57+import java.security.interfaces.*;
58+import java.security.spec.*;
59+
60+import javax.crypto.SecretKey;
61+import javax.crypto.interfaces.*;
62+
63+import javax.security.auth.Subject;
64+import javax.security.auth.x500.X500Principal;
65+import javax.security.auth.login.LoginException;
66+import javax.security.auth.callback.Callback;
67+import javax.security.auth.callback.PasswordCallback;
68+import javax.security.auth.callback.CallbackHandler;
69+import javax.security.auth.callback.UnsupportedCallbackException;
70+
71+import sun.security.util.Debug;
72+import sun.security.x509.SerialNumber;
73+import sun.security.util.DerValue;
74+
75+import sun.security.ec.ECParameters;
76+
77+import sun.security.pkcs11.Secmod.*;
78+import static sun.security.pkcs11.P11Util.*;
79+
80+import sun.security.pkcs11.wrapper.*;
81+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
82+
83+import sun.security.rsa.RSAKeyFactory;
84+
85+final 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+}