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