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