Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs11/P11KeyStore.java
38919 views
1
/*
2
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.security.pkcs11;
27
28
import java.math.BigInteger;
29
30
import java.io.InputStream;
31
import java.io.OutputStream;
32
import java.io.IOException;
33
import java.io.ByteArrayInputStream;
34
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
debug.println("P11KeyStore load. Entry count: " +
775
aliasMap.size());
776
}
777
} catch (KeyStoreException | PKCS11Exception e) {
778
throw new IOException("load failed", e);
779
}
780
}
781
782
/**
783
* Loads the keystore using the given
784
* <code>KeyStore.LoadStoreParameter</code>.
785
*
786
* <p> The <code>LoadStoreParameter.getProtectionParameter()</code>
787
* method is expected to return a <code>KeyStore.PasswordProtection</code>
788
* object. The password is retrieved from that object and used
789
* to unlock the PKCS#11 token.
790
*
791
* <p> If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH
792
* then the provided password must be <code>null</code>.
793
*
794
* @param param the <code>KeyStore.LoadStoreParameter</code>
795
*
796
* @exception IllegalArgumentException if the given
797
* <code>KeyStore.LoadStoreParameter</code> is <code>null</code>,
798
* or if that parameter returns a <code>null</code>
799
* <code>ProtectionParameter</code> object.
800
* input is not recognized
801
* @exception IOException if the token supports a
802
* CKF_PROTECTED_AUTHENTICATION_PATH and the provided password
803
* is non-null, or if the token login operation fails
804
*/
805
public synchronized void engineLoad(KeyStore.LoadStoreParameter param)
806
throws IOException, NoSuchAlgorithmException,
807
CertificateException {
808
809
token.ensureValid();
810
811
if (NSS_TEST) {
812
ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false);
813
}
814
815
// if caller wants to pass a NULL password,
816
// force it to pass a non-NULL PasswordProtection that returns
817
// a NULL password
818
819
if (param == null) {
820
throw new IllegalArgumentException
821
("invalid null LoadStoreParameter");
822
}
823
if (useSecmodTrust) {
824
if (param instanceof Secmod.KeyStoreLoadParameter) {
825
nssTrustType = ((Secmod.KeyStoreLoadParameter)param).getTrustType();
826
} else {
827
nssTrustType = Secmod.TrustType.ALL;
828
}
829
}
830
831
CallbackHandler handler;
832
KeyStore.ProtectionParameter pp = param.getProtectionParameter();
833
if (pp instanceof PasswordProtection) {
834
char[] password = ((PasswordProtection)pp).getPassword();
835
if (password == null) {
836
handler = null;
837
} else {
838
handler = new PasswordCallbackHandler(password);
839
}
840
} else if (pp instanceof CallbackHandlerProtection) {
841
handler = ((CallbackHandlerProtection)pp).getCallbackHandler();
842
} else {
843
throw new IllegalArgumentException
844
("ProtectionParameter must be either " +
845
"PasswordProtection or CallbackHandlerProtection");
846
}
847
848
try {
849
login(handler);
850
if (mapLabels() == true) {
851
// CKA_LABELs are shared by multiple certs
852
writeDisabled = true;
853
}
854
if (debug != null) {
855
dumpTokenMap();
856
}
857
} catch (LoginException | KeyStoreException | PKCS11Exception e) {
858
throw new IOException("load failed", e);
859
}
860
}
861
862
private void login(CallbackHandler handler) throws LoginException {
863
if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) {
864
token.provider.login(null, handler);
865
} else {
866
// token supports protected authentication path
867
// (external pin-pad, for example)
868
if (handler != null &&
869
!token.config.getKeyStoreCompatibilityMode()) {
870
throw new LoginException("can not specify password if token " +
871
"supports protected authentication path");
872
}
873
874
// must rely on application-set or default handler
875
// if one is necessary
876
token.provider.login(null, null);
877
}
878
}
879
880
/**
881
* Get a <code>KeyStore.Entry</code> for the specified alias
882
*
883
* @param alias get the <code>KeyStore.Entry</code> for this alias
884
* @param protParam this must be <code>null</code>
885
*
886
* @return the <code>KeyStore.Entry</code> for the specified alias,
887
* or <code>null</code> if there is no such entry
888
*
889
* @exception KeyStoreException if the operation failed
890
* @exception NoSuchAlgorithmException if the algorithm for recovering the
891
* entry cannot be found
892
* @exception UnrecoverableEntryException if the specified
893
* <code>protParam</code> were insufficient or invalid
894
*
895
* @since 1.5
896
*/
897
public synchronized KeyStore.Entry engineGetEntry(String alias,
898
KeyStore.ProtectionParameter protParam)
899
throws KeyStoreException, NoSuchAlgorithmException,
900
UnrecoverableEntryException {
901
902
token.ensureValid();
903
904
if (protParam != null &&
905
protParam instanceof KeyStore.PasswordProtection &&
906
((KeyStore.PasswordProtection)protParam).getPassword() != null &&
907
!token.config.getKeyStoreCompatibilityMode()) {
908
throw new KeyStoreException("ProtectionParameter must be null");
909
}
910
911
AliasInfo aliasInfo = aliasMap.get(alias);
912
if (aliasInfo == null) {
913
if (debug != null) {
914
debug.println("engineGetEntry did not find alias [" +
915
alias +
916
"] in map");
917
}
918
return null;
919
}
920
921
Session session = null;
922
try {
923
session = token.getOpSession();
924
925
if (aliasInfo.type == ATTR_CLASS_CERT) {
926
// trusted certificate entry
927
if (debug != null) {
928
debug.println("engineGetEntry found trusted cert entry");
929
}
930
return new KeyStore.TrustedCertificateEntry(aliasInfo.cert);
931
} else if (aliasInfo.type == ATTR_CLASS_SKEY) {
932
// secret key entry
933
if (debug != null) {
934
debug.println("engineGetEntry found secret key entry");
935
}
936
937
THandle h = getTokenObject
938
(session, ATTR_CLASS_SKEY, null, aliasInfo.label);
939
if (h.type != ATTR_CLASS_SKEY) {
940
throw new KeyStoreException
941
("expected but could not find secret key");
942
} else {
943
SecretKey skey = loadSkey(session, h.handle);
944
return new KeyStore.SecretKeyEntry(skey);
945
}
946
} else {
947
// private key entry
948
if (debug != null) {
949
debug.println("engineGetEntry found private key entry");
950
}
951
952
THandle h = getTokenObject
953
(session, ATTR_CLASS_PKEY, aliasInfo.id, null);
954
if (h.type != ATTR_CLASS_PKEY) {
955
throw new KeyStoreException
956
("expected but could not find private key");
957
} else {
958
PrivateKey pkey = loadPkey(session, h.handle);
959
Certificate[] chain = aliasInfo.chain;
960
if ((pkey != null) && (chain != null)) {
961
return new KeyStore.PrivateKeyEntry(pkey, chain);
962
} else {
963
if (debug != null) {
964
debug.println
965
("engineGetEntry got null cert chain or private key");
966
}
967
}
968
}
969
}
970
return null;
971
} catch (PKCS11Exception pe) {
972
throw new KeyStoreException(pe);
973
} finally {
974
token.releaseSession(session);
975
}
976
}
977
978
/**
979
* Save a <code>KeyStore.Entry</code> under the specified alias.
980
*
981
* <p> If an entry already exists for the specified alias,
982
* it is overridden.
983
*
984
* <p> This KeyStore implementation only supports the standard
985
* entry types, and only supports X509Certificates in
986
* TrustedCertificateEntries. Also, this implementation does not support
987
* protecting entries using a different password
988
* from the one used for token login.
989
*
990
* <p> Entries are immediately stored on the token.
991
*
992
* @param alias save the <code>KeyStore.Entry</code> under this alias
993
* @param entry the <code>Entry</code> to save
994
* @param protParam this must be <code>null</code>
995
*
996
* @exception KeyStoreException if this operation fails
997
*
998
* @since 1.5
999
*/
1000
public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
1001
KeyStore.ProtectionParameter protParam)
1002
throws KeyStoreException {
1003
1004
token.ensureValid();
1005
checkWrite();
1006
1007
if (protParam != null &&
1008
protParam instanceof KeyStore.PasswordProtection &&
1009
((KeyStore.PasswordProtection)protParam).getPassword() != null &&
1010
!token.config.getKeyStoreCompatibilityMode()) {
1011
throw new KeyStoreException(new UnsupportedOperationException
1012
("ProtectionParameter must be null"));
1013
}
1014
1015
if (token.isWriteProtected()) {
1016
throw new KeyStoreException("token write-protected");
1017
}
1018
1019
if (entry instanceof KeyStore.TrustedCertificateEntry) {
1020
1021
if (useSecmodTrust == false) {
1022
// PKCS #11 does not allow app to modify trusted certs -
1023
throw new KeyStoreException(new UnsupportedOperationException
1024
("trusted certificates may only be set by " +
1025
"token initialization application"));
1026
}
1027
Module module = token.provider.nssModule;
1028
if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) {
1029
// XXX allow TRUSTANCHOR module
1030
throw new KeyStoreException("Trusted certificates can only be "
1031
+ "added to the NSS KeyStore module");
1032
}
1033
Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate();
1034
if (cert instanceof X509Certificate == false) {
1035
throw new KeyStoreException("Certificate must be an X509Certificate");
1036
}
1037
X509Certificate xcert = (X509Certificate)cert;
1038
AliasInfo info = aliasMap.get(alias);
1039
if (info != null) {
1040
// XXX try to update
1041
deleteEntry(alias);
1042
}
1043
try {
1044
storeCert(alias, xcert);
1045
module.setTrust(token, xcert);
1046
mapLabels();
1047
} catch (PKCS11Exception | CertificateException e) {
1048
throw new KeyStoreException(e);
1049
}
1050
1051
} else {
1052
1053
if (entry instanceof KeyStore.PrivateKeyEntry) {
1054
1055
PrivateKey key =
1056
((KeyStore.PrivateKeyEntry)entry).getPrivateKey();
1057
if (!(key instanceof P11Key) &&
1058
!(key instanceof RSAPrivateKey) &&
1059
!(key instanceof DSAPrivateKey) &&
1060
!(key instanceof DHPrivateKey) &&
1061
!(key instanceof ECPrivateKey)) {
1062
throw new KeyStoreException("unsupported key type: " +
1063
key.getClass().getName());
1064
}
1065
1066
// only support X509Certificate chains
1067
Certificate[] chain =
1068
((KeyStore.PrivateKeyEntry)entry).getCertificateChain();
1069
if (!(chain instanceof X509Certificate[])) {
1070
throw new KeyStoreException
1071
(new UnsupportedOperationException
1072
("unsupported certificate array type: " +
1073
chain.getClass().getName()));
1074
}
1075
1076
try {
1077
boolean updatedAlias = false;
1078
Set<String> aliases = aliasMap.keySet();
1079
for (String oldAlias : aliases) {
1080
1081
// see if there's an existing entry with the same info
1082
1083
AliasInfo aliasInfo = aliasMap.get(oldAlias);
1084
if (aliasInfo.type == ATTR_CLASS_PKEY &&
1085
aliasInfo.cert.getPublicKey().equals
1086
(chain[0].getPublicKey())) {
1087
1088
// found existing entry -
1089
// caller is renaming entry or updating cert chain
1090
//
1091
// set new CKA_LABEL/CKA_ID
1092
// and update certs if necessary
1093
1094
updatePkey(alias,
1095
aliasInfo.id,
1096
(X509Certificate[])chain,
1097
!aliasInfo.cert.equals(chain[0]));
1098
updatedAlias = true;
1099
break;
1100
}
1101
}
1102
1103
if (!updatedAlias) {
1104
// caller adding new entry
1105
engineDeleteEntry(alias);
1106
storePkey(alias, (KeyStore.PrivateKeyEntry)entry);
1107
}
1108
1109
} catch (PKCS11Exception | CertificateException pe) {
1110
throw new KeyStoreException(pe);
1111
}
1112
1113
} else if (entry instanceof KeyStore.SecretKeyEntry) {
1114
1115
KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;
1116
SecretKey skey = ske.getSecretKey();
1117
1118
try {
1119
// first check if the key already exists
1120
AliasInfo aliasInfo = aliasMap.get(alias);
1121
1122
if (aliasInfo != null) {
1123
engineDeleteEntry(alias);
1124
}
1125
storeSkey(alias, ske);
1126
1127
} catch (PKCS11Exception pe) {
1128
throw new KeyStoreException(pe);
1129
}
1130
1131
} else {
1132
throw new KeyStoreException(new UnsupportedOperationException
1133
("unsupported entry type: " + entry.getClass().getName()));
1134
}
1135
1136
try {
1137
1138
// XXX NSS does not write out the CKA_ID we pass to them
1139
//
1140
// therefore we must re-map labels
1141
// (can not simply update aliasMap)
1142
1143
mapLabels();
1144
if (debug != null) {
1145
dumpTokenMap();
1146
}
1147
} catch (PKCS11Exception | CertificateException pe) {
1148
throw new KeyStoreException(pe);
1149
}
1150
}
1151
1152
if (debug != null) {
1153
debug.println
1154
("engineSetEntry added new entry for [" +
1155
alias +
1156
"] to token");
1157
}
1158
}
1159
1160
/**
1161
* Determines if the keystore <code>Entry</code> for the specified
1162
* <code>alias</code> is an instance or subclass of the specified
1163
* <code>entryClass</code>.
1164
*
1165
* @param alias the alias name
1166
* @param entryClass the entry class
1167
*
1168
* @return true if the keystore <code>Entry</code> for the specified
1169
* <code>alias</code> is an instance or subclass of the
1170
* specified <code>entryClass</code>, false otherwise
1171
*/
1172
public synchronized boolean engineEntryInstanceOf
1173
(String alias, Class<? extends KeyStore.Entry> entryClass) {
1174
token.ensureValid();
1175
return super.engineEntryInstanceOf(alias, entryClass);
1176
}
1177
1178
private X509Certificate loadCert(Session session, long oHandle)
1179
throws PKCS11Exception, CertificateException {
1180
1181
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[]
1182
{ new CK_ATTRIBUTE(CKA_VALUE) };
1183
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1184
1185
byte[] bytes = attrs[0].getByteArray();
1186
if (bytes == null) {
1187
throw new CertificateException
1188
("unexpectedly retrieved null byte array");
1189
}
1190
CertificateFactory cf = CertificateFactory.getInstance("X.509");
1191
return (X509Certificate)cf.generateCertificate
1192
(new ByteArrayInputStream(bytes));
1193
}
1194
1195
private X509Certificate[] loadChain(Session session,
1196
X509Certificate endCert)
1197
throws PKCS11Exception, CertificateException {
1198
1199
ArrayList<X509Certificate> lChain = null;
1200
1201
if (endCert.getSubjectX500Principal().equals
1202
(endCert.getIssuerX500Principal())) {
1203
// self signed
1204
return new X509Certificate[] { endCert };
1205
} else {
1206
lChain = new ArrayList<X509Certificate>();
1207
lChain.add(endCert);
1208
}
1209
1210
// try loading remaining certs in chain by following
1211
// issuer->subject links
1212
1213
X509Certificate next = endCert;
1214
while (true) {
1215
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1216
ATTR_TOKEN_TRUE,
1217
ATTR_CLASS_CERT,
1218
new CK_ATTRIBUTE(CKA_SUBJECT,
1219
next.getIssuerX500Principal().getEncoded()) };
1220
long[] ch = findObjects(session, attrs);
1221
1222
if (ch == null || ch.length == 0) {
1223
// done
1224
break;
1225
} else {
1226
// if more than one found, use first
1227
if (debug != null && ch.length > 1) {
1228
debug.println("engineGetEntry found " +
1229
ch.length +
1230
" certificate entries for subject [" +
1231
next.getIssuerX500Principal().toString() +
1232
"] in token - using first entry");
1233
}
1234
1235
next = loadCert(session, ch[0]);
1236
lChain.add(next);
1237
if (next.getSubjectX500Principal().equals
1238
(next.getIssuerX500Principal())) {
1239
// self signed
1240
break;
1241
}
1242
}
1243
}
1244
1245
return lChain.toArray(new X509Certificate[lChain.size()]);
1246
}
1247
1248
private SecretKey loadSkey(Session session, long oHandle)
1249
throws PKCS11Exception {
1250
1251
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1252
new CK_ATTRIBUTE(CKA_KEY_TYPE) };
1253
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1254
long kType = attrs[0].getLong();
1255
1256
String keyType = null;
1257
int keyLength = -1;
1258
1259
// XXX NSS mangles the stored key type for secret key token objects
1260
1261
if (kType == CKK_DES || kType == CKK_DES3) {
1262
if (kType == CKK_DES) {
1263
keyType = "DES";
1264
keyLength = 64;
1265
} else if (kType == CKK_DES3) {
1266
keyType = "DESede";
1267
keyLength = 192;
1268
}
1269
} else {
1270
if (kType == CKK_AES) {
1271
keyType = "AES";
1272
} else if (kType == CKK_BLOWFISH) {
1273
keyType = "Blowfish";
1274
} else if (kType == CKK_RC4) {
1275
keyType = "ARCFOUR";
1276
} else {
1277
if (debug != null) {
1278
debug.println("unknown key type [" +
1279
kType +
1280
"] - using 'Generic Secret'");
1281
}
1282
keyType = "Generic Secret";
1283
}
1284
1285
// XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID?
1286
if (NSS_TEST) {
1287
keyLength = 128;
1288
} else {
1289
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) };
1290
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1291
keyLength = (int)attrs[0].getLong();
1292
}
1293
}
1294
1295
return P11Key.secretKey(session, oHandle, keyType, keyLength, null);
1296
}
1297
1298
private PrivateKey loadPkey(Session session, long oHandle)
1299
throws PKCS11Exception, KeyStoreException {
1300
1301
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1302
new CK_ATTRIBUTE(CKA_KEY_TYPE) };
1303
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1304
long kType = attrs[0].getLong();
1305
String keyType = null;
1306
int keyLength = 0;
1307
1308
if (kType == CKK_RSA) {
1309
1310
keyType = "RSA";
1311
1312
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) };
1313
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1314
BigInteger modulus = attrs[0].getBigInteger();
1315
keyLength = modulus.bitLength();
1316
1317
// This check will combine our "don't care" values here
1318
// with the system-wide min/max values.
1319
try {
1320
RSAKeyFactory.checkKeyLengths(keyLength, null,
1321
-1, Integer.MAX_VALUE);
1322
} catch (InvalidKeyException e) {
1323
throw new KeyStoreException(e.getMessage());
1324
}
1325
1326
return P11Key.privateKey(session,
1327
oHandle,
1328
keyType,
1329
keyLength,
1330
null);
1331
1332
} else if (kType == CKK_DSA) {
1333
1334
keyType = "DSA";
1335
1336
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
1337
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1338
BigInteger prime = attrs[0].getBigInteger();
1339
keyLength = prime.bitLength();
1340
1341
return P11Key.privateKey(session,
1342
oHandle,
1343
keyType,
1344
keyLength,
1345
null);
1346
1347
} else if (kType == CKK_DH) {
1348
1349
keyType = "DH";
1350
1351
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) };
1352
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1353
BigInteger prime = attrs[0].getBigInteger();
1354
keyLength = prime.bitLength();
1355
1356
return P11Key.privateKey(session,
1357
oHandle,
1358
keyType,
1359
keyLength,
1360
null);
1361
1362
} else if (kType == CKK_EC) {
1363
1364
attrs = new CK_ATTRIBUTE[] {
1365
new CK_ATTRIBUTE(CKA_EC_PARAMS),
1366
};
1367
token.p11.C_GetAttributeValue(session.id(), oHandle, attrs);
1368
byte[] encodedParams = attrs[0].getByteArray();
1369
try {
1370
ECParameterSpec params =
1371
ECUtil.getECParameterSpec(null, encodedParams);
1372
keyLength = params.getCurve().getField().getFieldSize();
1373
} catch (IOException e) {
1374
// we do not want to accept key with unsupported parameters
1375
throw new KeyStoreException("Unsupported parameters", e);
1376
}
1377
1378
return P11Key.privateKey(session, oHandle, "EC", keyLength, null);
1379
1380
} else {
1381
if (debug != null) {
1382
debug.println("unknown key type [" + kType + "]");
1383
}
1384
throw new KeyStoreException("unknown key type");
1385
}
1386
}
1387
1388
1389
/**
1390
* XXX On ibutton, when you C_SetAttribute(CKA_ID) for a private key
1391
* it not only changes the CKA_ID of the private key,
1392
* it changes the CKA_ID of the corresponding cert too.
1393
* And vice versa.
1394
*
1395
* XXX On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID)
1396
* for a private key, and then try to delete the corresponding cert.
1397
* So this code reverses the order.
1398
* After the cert is first destroyed (if necessary),
1399
* then the CKA_ID of the private key can be changed successfully.
1400
*
1401
* @param replaceCert if true, then caller is updating alias info for
1402
* existing cert (only update CKA_ID/CKA_LABEL).
1403
* if false, then caller is updating cert chain
1404
* (delete old end cert and add new chain).
1405
*/
1406
private void updatePkey(String alias,
1407
byte[] cka_id,
1408
X509Certificate[] chain,
1409
boolean replaceCert) throws
1410
KeyStoreException, CertificateException, PKCS11Exception {
1411
1412
// XXX
1413
//
1414
// always set replaceCert to true
1415
//
1416
// NSS does not allow resetting of CKA_LABEL on an existing cert
1417
// (C_SetAttribute call succeeds, but is ignored)
1418
1419
replaceCert = true;
1420
1421
Session session = null;
1422
try {
1423
session = token.getOpSession();
1424
1425
// first get private key object handle and hang onto it
1426
1427
THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
1428
long pKeyHandle;
1429
if (h.type == ATTR_CLASS_PKEY) {
1430
pKeyHandle = h.handle;
1431
} else {
1432
throw new KeyStoreException
1433
("expected but could not find private key " +
1434
"with CKA_ID " +
1435
getID(cka_id));
1436
}
1437
1438
// next find existing end entity cert
1439
1440
h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1441
if (h.type != ATTR_CLASS_CERT) {
1442
throw new KeyStoreException
1443
("expected but could not find certificate " +
1444
"with CKA_ID " +
1445
getID(cka_id));
1446
} else {
1447
if (replaceCert) {
1448
// replacing existing cert and chain
1449
destroyChain(cka_id);
1450
} else {
1451
// renaming alias for existing cert
1452
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1453
new CK_ATTRIBUTE(CKA_LABEL, alias),
1454
new CK_ATTRIBUTE(CKA_ID, alias) };
1455
token.p11.C_SetAttributeValue
1456
(session.id(), h.handle, attrs);
1457
}
1458
}
1459
1460
// add new chain
1461
1462
if (replaceCert) {
1463
// add all certs in chain
1464
storeChain(alias, chain);
1465
} else {
1466
// already updated alias info for existing end cert -
1467
// just update CA certs
1468
storeCaCerts(chain, 1);
1469
}
1470
1471
// finally update CKA_ID for private key
1472
//
1473
// ibutton may have already done this (that is ok)
1474
1475
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1476
new CK_ATTRIBUTE(CKA_ID, alias) };
1477
token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs);
1478
1479
if (debug != null) {
1480
debug.println("updatePkey set new alias [" +
1481
alias +
1482
"] for private key entry");
1483
}
1484
} finally {
1485
token.releaseSession(session);
1486
}
1487
}
1488
1489
// retrieves the native key handle and either update it directly or make a copy
1490
private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key)
1491
throws PKCS11Exception {
1492
1493
// if token key, update alias.
1494
// if session key, convert to token key.
1495
1496
Session session = null;
1497
long keyID = key.getKeyID();
1498
try {
1499
session = token.getOpSession();
1500
if (key.tokenObject == true) {
1501
// token key - set new CKA_ID
1502
1503
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1504
new CK_ATTRIBUTE(CKA_ID, alias) };
1505
token.p11.C_SetAttributeValue
1506
(session.id(), keyID, attrs);
1507
if (debug != null) {
1508
debug.println("updateP11Pkey set new alias [" +
1509
alias +
1510
"] for key entry");
1511
}
1512
} else {
1513
// session key - convert to token key and set CKA_ID
1514
1515
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1516
ATTR_TOKEN_TRUE,
1517
new CK_ATTRIBUTE(CKA_ID, alias),
1518
};
1519
if (attribute != null) {
1520
attrs = addAttribute(attrs, attribute);
1521
}
1522
// creates a new token key with the desired CKA_ID
1523
token.p11.C_CopyObject(session.id(), keyID, attrs);
1524
if (debug != null) {
1525
debug.println("updateP11Pkey copied private session key " +
1526
"for [" +
1527
alias +
1528
"] to token entry");
1529
}
1530
}
1531
} finally {
1532
token.releaseSession(session);
1533
key.releaseKeyID();
1534
}
1535
}
1536
1537
private void storeCert(String alias, X509Certificate cert)
1538
throws PKCS11Exception, CertificateException {
1539
1540
ArrayList<CK_ATTRIBUTE> attrList = new ArrayList<CK_ATTRIBUTE>();
1541
attrList.add(ATTR_TOKEN_TRUE);
1542
attrList.add(ATTR_CLASS_CERT);
1543
attrList.add(ATTR_X509_CERT_TYPE);
1544
attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT,
1545
cert.getSubjectX500Principal().getEncoded()));
1546
attrList.add(new CK_ATTRIBUTE(CKA_ISSUER,
1547
cert.getIssuerX500Principal().getEncoded()));
1548
attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER,
1549
cert.getSerialNumber().toByteArray()));
1550
attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded()));
1551
1552
if (alias != null) {
1553
attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias));
1554
attrList.add(new CK_ATTRIBUTE(CKA_ID, alias));
1555
} else {
1556
// ibutton requires something to be set
1557
// - alias must be unique
1558
attrList.add(new CK_ATTRIBUTE(CKA_ID,
1559
getID(cert.getSubjectX500Principal().getName
1560
(X500Principal.CANONICAL), cert)));
1561
}
1562
1563
Session session = null;
1564
try {
1565
session = token.getOpSession();
1566
token.p11.C_CreateObject(session.id(),
1567
attrList.toArray(new CK_ATTRIBUTE[attrList.size()]));
1568
} finally {
1569
token.releaseSession(session);
1570
}
1571
}
1572
1573
private void storeChain(String alias, X509Certificate[] chain)
1574
throws PKCS11Exception, CertificateException {
1575
1576
// add new chain
1577
//
1578
// end cert has CKA_LABEL and CKA_ID set to alias.
1579
// other certs in chain have neither set.
1580
1581
storeCert(alias, chain[0]);
1582
storeCaCerts(chain, 1);
1583
}
1584
1585
private void storeCaCerts(X509Certificate[] chain, int start)
1586
throws PKCS11Exception, CertificateException {
1587
1588
// do not add duplicate CA cert if already in token
1589
//
1590
// XXX ibutton stores duplicate CA certs, NSS does not
1591
1592
Session session = null;
1593
HashSet<X509Certificate> cacerts = new HashSet<X509Certificate>();
1594
try {
1595
session = token.getOpSession();
1596
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1597
ATTR_TOKEN_TRUE,
1598
ATTR_CLASS_CERT };
1599
long[] handles = findObjects(session, attrs);
1600
1601
// load certs currently on the token
1602
for (long handle : handles) {
1603
cacerts.add(loadCert(session, handle));
1604
}
1605
} finally {
1606
token.releaseSession(session);
1607
}
1608
1609
for (int i = start; i < chain.length; i++) {
1610
if (!cacerts.contains(chain[i])) {
1611
storeCert(null, chain[i]);
1612
} else if (debug != null) {
1613
debug.println("ignoring duplicate CA cert for [" +
1614
chain[i].getSubjectX500Principal() +
1615
"]");
1616
}
1617
}
1618
}
1619
1620
private void storeSkey(String alias, KeyStore.SecretKeyEntry ske)
1621
throws PKCS11Exception, KeyStoreException {
1622
1623
SecretKey skey = ske.getSecretKey();
1624
// No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since
1625
// they are handled in P11SecretKeyFactory.createKey() method.
1626
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
1627
ATTR_SKEY_TOKEN_TRUE,
1628
ATTR_PRIVATE_TRUE,
1629
new CK_ATTRIBUTE(CKA_LABEL, alias),
1630
};
1631
try {
1632
P11SecretKeyFactory.convertKey(token, skey, null, attrs);
1633
} catch (InvalidKeyException ike) {
1634
// re-throw KeyStoreException to match javadoc
1635
throw new KeyStoreException("Cannot convert to PKCS11 keys", ike);
1636
}
1637
1638
// update global alias map
1639
aliasMap.put(alias, new AliasInfo(alias));
1640
1641
if (debug != null) {
1642
debug.println("storeSkey created token secret key for [" +
1643
alias + "]");
1644
}
1645
}
1646
1647
private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) {
1648
int n = attrs.length;
1649
CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1];
1650
System.arraycopy(attrs, 0, newAttrs, 0, n);
1651
newAttrs[n] = attr;
1652
return newAttrs;
1653
}
1654
1655
private void storePkey(String alias, KeyStore.PrivateKeyEntry pke)
1656
throws PKCS11Exception, CertificateException, KeyStoreException {
1657
1658
PrivateKey key = pke.getPrivateKey();
1659
CK_ATTRIBUTE[] attrs = null;
1660
1661
// If the key is a token object on this token, update it instead
1662
// of creating a duplicate key object.
1663
// Otherwise, treat a P11Key like any other key, if is is extractable.
1664
if (key instanceof P11Key) {
1665
P11Key p11Key = (P11Key)key;
1666
if (p11Key.tokenObject && (p11Key.token == this.token)) {
1667
updateP11Pkey(alias, null, p11Key);
1668
storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1669
return;
1670
}
1671
}
1672
1673
boolean useNDB = token.config.getNssNetscapeDbWorkaround();
1674
PublicKey publicKey = pke.getCertificate().getPublicKey();
1675
1676
if (key instanceof RSAPrivateKey) {
1677
1678
X509Certificate cert = (X509Certificate)pke.getCertificate();
1679
attrs = getRsaPrivKeyAttrs
1680
(alias, (RSAPrivateKey)key, cert.getSubjectX500Principal());
1681
1682
} else if (key instanceof DSAPrivateKey) {
1683
1684
DSAPrivateKey dsaKey = (DSAPrivateKey)key;
1685
1686
CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1687
if (idAttrs[0] == null) {
1688
idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1689
}
1690
1691
attrs = new CK_ATTRIBUTE[] {
1692
ATTR_TOKEN_TRUE,
1693
ATTR_CLASS_PKEY,
1694
ATTR_PRIVATE_TRUE,
1695
new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA),
1696
idAttrs[0],
1697
new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()),
1698
new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()),
1699
new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()),
1700
new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()),
1701
};
1702
if (idAttrs[1] != null) {
1703
attrs = addAttribute(attrs, idAttrs[1]);
1704
}
1705
1706
attrs = token.getAttributes
1707
(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs);
1708
1709
if (debug != null) {
1710
debug.println("storePkey created DSA template");
1711
}
1712
1713
} else if (key instanceof DHPrivateKey) {
1714
1715
DHPrivateKey dhKey = (DHPrivateKey)key;
1716
1717
CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1718
if (idAttrs[0] == null) {
1719
idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1720
}
1721
1722
attrs = new CK_ATTRIBUTE[] {
1723
ATTR_TOKEN_TRUE,
1724
ATTR_CLASS_PKEY,
1725
ATTR_PRIVATE_TRUE,
1726
new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH),
1727
idAttrs[0],
1728
new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()),
1729
new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()),
1730
new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()),
1731
};
1732
if (idAttrs[1] != null) {
1733
attrs = addAttribute(attrs, idAttrs[1]);
1734
}
1735
1736
attrs = token.getAttributes
1737
(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs);
1738
1739
} else if (key instanceof ECPrivateKey) {
1740
1741
ECPrivateKey ecKey = (ECPrivateKey)key;
1742
1743
CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB);
1744
if (idAttrs[0] == null) {
1745
idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias);
1746
}
1747
1748
byte[] encodedParams =
1749
ECUtil.encodeECParameterSpec(null, ecKey.getParams());
1750
attrs = new CK_ATTRIBUTE[] {
1751
ATTR_TOKEN_TRUE,
1752
ATTR_CLASS_PKEY,
1753
ATTR_PRIVATE_TRUE,
1754
new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC),
1755
idAttrs[0],
1756
new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()),
1757
new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams),
1758
};
1759
if (idAttrs[1] != null) {
1760
attrs = addAttribute(attrs, idAttrs[1]);
1761
}
1762
1763
attrs = token.getAttributes
1764
(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs);
1765
1766
if (debug != null) {
1767
debug.println("storePkey created EC template");
1768
}
1769
1770
} else if (key instanceof P11Key) {
1771
// sensitive/non-extractable P11Key
1772
P11Key p11Key = (P11Key)key;
1773
if (p11Key.token != this.token) {
1774
throw new KeyStoreException
1775
("Cannot move sensitive keys across tokens");
1776
}
1777
CK_ATTRIBUTE netscapeDB = null;
1778
if (useNDB) {
1779
// Note that this currently fails due to an NSS bug.
1780
// They do not allow the CKA_NETSCAPE_DB attribute to be
1781
// specified during C_CopyObject() and fail with
1782
// CKR_ATTRIBUTE_READ_ONLY.
1783
// But if we did not specify it, they would fail with
1784
// CKA_TEMPLATE_INCOMPLETE, so leave this code in here.
1785
CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true);
1786
netscapeDB = idAttrs[1];
1787
}
1788
// Update the key object.
1789
updateP11Pkey(alias, netscapeDB, p11Key);
1790
storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1791
return;
1792
1793
} else {
1794
throw new KeyStoreException("unsupported key type: " + key);
1795
}
1796
1797
Session session = null;
1798
try {
1799
session = token.getOpSession();
1800
1801
// create private key entry
1802
token.p11.C_CreateObject(session.id(), attrs);
1803
if (debug != null) {
1804
debug.println("storePkey created token key for [" +
1805
alias +
1806
"]");
1807
}
1808
} finally {
1809
token.releaseSession(session);
1810
}
1811
1812
storeChain(alias, (X509Certificate[])pke.getCertificateChain());
1813
}
1814
1815
private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias,
1816
RSAPrivateKey key,
1817
X500Principal subject) throws PKCS11Exception {
1818
1819
// subject is currently ignored - could be used to set CKA_SUBJECT
1820
1821
CK_ATTRIBUTE[] attrs = null;
1822
if (key instanceof RSAPrivateCrtKey) {
1823
1824
if (debug != null) {
1825
debug.println("creating RSAPrivateCrtKey attrs");
1826
}
1827
1828
RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key;
1829
1830
attrs = new CK_ATTRIBUTE[] {
1831
ATTR_TOKEN_TRUE,
1832
ATTR_CLASS_PKEY,
1833
ATTR_PRIVATE_TRUE,
1834
new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
1835
new CK_ATTRIBUTE(CKA_ID, alias),
1836
new CK_ATTRIBUTE(CKA_MODULUS,
1837
rsaKey.getModulus()),
1838
new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
1839
rsaKey.getPrivateExponent()),
1840
new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT,
1841
rsaKey.getPublicExponent()),
1842
new CK_ATTRIBUTE(CKA_PRIME_1,
1843
rsaKey.getPrimeP()),
1844
new CK_ATTRIBUTE(CKA_PRIME_2,
1845
rsaKey.getPrimeQ()),
1846
new CK_ATTRIBUTE(CKA_EXPONENT_1,
1847
rsaKey.getPrimeExponentP()),
1848
new CK_ATTRIBUTE(CKA_EXPONENT_2,
1849
rsaKey.getPrimeExponentQ()),
1850
new CK_ATTRIBUTE(CKA_COEFFICIENT,
1851
rsaKey.getCrtCoefficient()) };
1852
attrs = token.getAttributes
1853
(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
1854
1855
} else {
1856
1857
if (debug != null) {
1858
debug.println("creating RSAPrivateKey attrs");
1859
}
1860
1861
RSAPrivateKey rsaKey = key;
1862
1863
attrs = new CK_ATTRIBUTE[] {
1864
ATTR_TOKEN_TRUE,
1865
ATTR_CLASS_PKEY,
1866
ATTR_PRIVATE_TRUE,
1867
new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA),
1868
new CK_ATTRIBUTE(CKA_ID, alias),
1869
new CK_ATTRIBUTE(CKA_MODULUS,
1870
rsaKey.getModulus()),
1871
new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT,
1872
rsaKey.getPrivateExponent()) };
1873
attrs = token.getAttributes
1874
(TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs);
1875
}
1876
1877
return attrs;
1878
}
1879
1880
/**
1881
* Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be
1882
* used for this private key. It uses the same algorithm to calculate the
1883
* values as NSS. The public and private keys MUST match for the result to
1884
* be correct.
1885
*
1886
* It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB
1887
* at index 1. The boolean flags determine what is to be calculated.
1888
* If false or if we could not calculate the value, that element is null.
1889
*
1890
* NOTE that we currently do not use the CKA_ID value calculated by this
1891
* method.
1892
*/
1893
private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey,
1894
PublicKey publicKey, boolean id, boolean netscapeDb) {
1895
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2];
1896
if ((id || netscapeDb) == false) {
1897
return attrs;
1898
}
1899
String alg = privateKey.getAlgorithm();
1900
if (alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) {
1901
if (id) {
1902
BigInteger n = ((RSAPublicKey)publicKey).getModulus();
1903
attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n)));
1904
}
1905
// CKA_NETSCAPE_DB not needed for RSA public keys
1906
} else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) {
1907
BigInteger y = ((DSAPublicKey)publicKey).getY();
1908
if (id) {
1909
attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
1910
}
1911
if (netscapeDb) {
1912
attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
1913
}
1914
} else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) {
1915
BigInteger y = ((DHPublicKey)publicKey).getY();
1916
if (id) {
1917
attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y)));
1918
}
1919
if (netscapeDb) {
1920
attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y);
1921
}
1922
} else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) {
1923
ECPublicKey ecPub = (ECPublicKey)publicKey;
1924
ECPoint point = ecPub.getW();
1925
ECParameterSpec params = ecPub.getParams();
1926
byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve());
1927
if (id) {
1928
attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint));
1929
}
1930
if (netscapeDb) {
1931
attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint);
1932
}
1933
} else {
1934
throw new RuntimeException("Unknown key algorithm " + alg);
1935
}
1936
return attrs;
1937
}
1938
1939
/**
1940
* return true if cert destroyed
1941
*/
1942
private boolean destroyCert(byte[] cka_id)
1943
throws PKCS11Exception, KeyStoreException {
1944
Session session = null;
1945
try {
1946
session = token.getOpSession();
1947
THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1948
if (h.type != ATTR_CLASS_CERT) {
1949
return false;
1950
}
1951
1952
token.p11.C_DestroyObject(session.id(), h.handle);
1953
if (debug != null) {
1954
debug.println("destroyCert destroyed cert with CKA_ID [" +
1955
getID(cka_id) +
1956
"]");
1957
}
1958
return true;
1959
} finally {
1960
token.releaseSession(session);
1961
}
1962
}
1963
1964
/**
1965
* return true if chain destroyed
1966
*/
1967
private boolean destroyChain(byte[] cka_id)
1968
throws PKCS11Exception, CertificateException, KeyStoreException {
1969
1970
Session session = null;
1971
try {
1972
session = token.getOpSession();
1973
1974
THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null);
1975
if (h.type != ATTR_CLASS_CERT) {
1976
if (debug != null) {
1977
debug.println("destroyChain could not find " +
1978
"end entity cert with CKA_ID [0x" +
1979
Functions.toHexString(cka_id) +
1980
"]");
1981
}
1982
return false;
1983
}
1984
1985
X509Certificate endCert = loadCert(session, h.handle);
1986
token.p11.C_DestroyObject(session.id(), h.handle);
1987
if (debug != null) {
1988
debug.println("destroyChain destroyed end entity cert " +
1989
"with CKA_ID [" +
1990
getID(cka_id) +
1991
"]");
1992
}
1993
1994
// build chain following issuer->subject links
1995
1996
X509Certificate next = endCert;
1997
while (true) {
1998
1999
if (next.getSubjectX500Principal().equals
2000
(next.getIssuerX500Principal())) {
2001
// self signed - done
2002
break;
2003
}
2004
2005
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
2006
ATTR_TOKEN_TRUE,
2007
ATTR_CLASS_CERT,
2008
new CK_ATTRIBUTE(CKA_SUBJECT,
2009
next.getIssuerX500Principal().getEncoded()) };
2010
long[] ch = findObjects(session, attrs);
2011
2012
if (ch == null || ch.length == 0) {
2013
// done
2014
break;
2015
} else {
2016
// if more than one found, use first
2017
if (debug != null && ch.length > 1) {
2018
debug.println("destroyChain found " +
2019
ch.length +
2020
" certificate entries for subject [" +
2021
next.getIssuerX500Principal() +
2022
"] in token - using first entry");
2023
}
2024
2025
next = loadCert(session, ch[0]);
2026
2027
// only delete if not part of any other chain
2028
2029
attrs = new CK_ATTRIBUTE[] {
2030
ATTR_TOKEN_TRUE,
2031
ATTR_CLASS_CERT,
2032
new CK_ATTRIBUTE(CKA_ISSUER,
2033
next.getSubjectX500Principal().getEncoded()) };
2034
long[] issuers = findObjects(session, attrs);
2035
2036
boolean destroyIt = false;
2037
if (issuers == null || issuers.length == 0) {
2038
// no other certs with this issuer -
2039
// destroy it
2040
destroyIt = true;
2041
} else if (issuers.length == 1) {
2042
X509Certificate iCert = loadCert(session, issuers[0]);
2043
if (next.equals(iCert)) {
2044
// only cert with issuer is itself (self-signed) -
2045
// destroy it
2046
destroyIt = true;
2047
}
2048
}
2049
2050
if (destroyIt) {
2051
token.p11.C_DestroyObject(session.id(), ch[0]);
2052
if (debug != null) {
2053
debug.println
2054
("destroyChain destroyed cert in chain " +
2055
"with subject [" +
2056
next.getSubjectX500Principal() + "]");
2057
}
2058
} else {
2059
if (debug != null) {
2060
debug.println("destroyChain did not destroy " +
2061
"shared cert in chain with subject [" +
2062
next.getSubjectX500Principal() + "]");
2063
}
2064
}
2065
}
2066
}
2067
2068
return true;
2069
2070
} finally {
2071
token.releaseSession(session);
2072
}
2073
}
2074
2075
/**
2076
* return true if secret key destroyed
2077
*/
2078
private boolean destroySkey(String alias)
2079
throws PKCS11Exception, KeyStoreException {
2080
Session session = null;
2081
try {
2082
session = token.getOpSession();
2083
2084
THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias);
2085
if (h.type != ATTR_CLASS_SKEY) {
2086
if (debug != null) {
2087
debug.println("destroySkey did not find secret key " +
2088
"with CKA_LABEL [" +
2089
alias +
2090
"]");
2091
}
2092
return false;
2093
}
2094
token.p11.C_DestroyObject(session.id(), h.handle);
2095
return true;
2096
} finally {
2097
token.releaseSession(session);
2098
}
2099
}
2100
2101
/**
2102
* return true if private key destroyed
2103
*/
2104
private boolean destroyPkey(byte[] cka_id)
2105
throws PKCS11Exception, KeyStoreException {
2106
Session session = null;
2107
try {
2108
session = token.getOpSession();
2109
2110
THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null);
2111
if (h.type != ATTR_CLASS_PKEY) {
2112
if (debug != null) {
2113
debug.println
2114
("destroyPkey did not find private key with CKA_ID [" +
2115
getID(cka_id) +
2116
"]");
2117
}
2118
return false;
2119
}
2120
token.p11.C_DestroyObject(session.id(), h.handle);
2121
return true;
2122
} finally {
2123
token.releaseSession(session);
2124
}
2125
}
2126
2127
/**
2128
* build [alias + issuer + serialNumber] string from a cert
2129
*/
2130
private String getID(String alias, X509Certificate cert) {
2131
X500Principal issuer = cert.getIssuerX500Principal();
2132
BigInteger serialNum = cert.getSerialNumber();
2133
2134
return alias +
2135
ALIAS_SEP +
2136
issuer.getName(X500Principal.CANONICAL) +
2137
ALIAS_SEP +
2138
serialNum.toString();
2139
}
2140
2141
/**
2142
* build CKA_ID string from bytes
2143
*/
2144
private static String getID(byte[] bytes) {
2145
boolean printable = true;
2146
for (int i = 0; i < bytes.length; i++) {
2147
if (!DerValue.isPrintableStringChar((char)bytes[i])) {
2148
printable = false;
2149
break;
2150
}
2151
}
2152
2153
if (!printable) {
2154
return "0x" + Functions.toHexString(bytes);
2155
} else {
2156
try {
2157
return new String(bytes, "UTF-8");
2158
} catch (UnsupportedEncodingException uee) {
2159
return "0x" + Functions.toHexString(bytes);
2160
}
2161
}
2162
}
2163
2164
/**
2165
* find an object on the token
2166
*
2167
* @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY
2168
* @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY
2169
* @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY
2170
*/
2171
private THandle getTokenObject(Session session,
2172
CK_ATTRIBUTE type,
2173
byte[] cka_id,
2174
String cka_label)
2175
throws PKCS11Exception, KeyStoreException {
2176
2177
CK_ATTRIBUTE[] attrs;
2178
if (type == ATTR_CLASS_SKEY) {
2179
attrs = new CK_ATTRIBUTE[] {
2180
ATTR_SKEY_TOKEN_TRUE,
2181
new CK_ATTRIBUTE(CKA_LABEL, cka_label),
2182
type };
2183
} else {
2184
attrs = new CK_ATTRIBUTE[] {
2185
ATTR_TOKEN_TRUE,
2186
new CK_ATTRIBUTE(CKA_ID, cka_id),
2187
type };
2188
}
2189
long[] h = findObjects(session, attrs);
2190
if (h.length == 0) {
2191
if (debug != null) {
2192
if (type == ATTR_CLASS_SKEY) {
2193
debug.println("getTokenObject did not find secret key " +
2194
"with CKA_LABEL [" +
2195
cka_label +
2196
"]");
2197
} else if (type == ATTR_CLASS_CERT) {
2198
debug.println
2199
("getTokenObject did not find cert with CKA_ID [" +
2200
getID(cka_id) +
2201
"]");
2202
} else {
2203
debug.println("getTokenObject did not find private key " +
2204
"with CKA_ID [" +
2205
getID(cka_id) +
2206
"]");
2207
}
2208
}
2209
} else if (h.length == 1) {
2210
2211
// found object handle - return it
2212
return new THandle(h[0], type);
2213
2214
} else {
2215
2216
// found multiple object handles -
2217
// see if token ignored CKA_LABEL during search (e.g. NSS)
2218
2219
if (type == ATTR_CLASS_SKEY) {
2220
2221
ArrayList<THandle> list = new ArrayList<THandle>(h.length);
2222
for (int i = 0; i < h.length; i++) {
2223
2224
CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[]
2225
{ new CK_ATTRIBUTE(CKA_LABEL) };
2226
token.p11.C_GetAttributeValue(session.id(), h[i], label);
2227
if (label[0].pValue != null &&
2228
cka_label.equals(new String(label[0].getCharArray()))) {
2229
list.add(new THandle(h[i], ATTR_CLASS_SKEY));
2230
}
2231
}
2232
if (list.size() == 1) {
2233
// yes, there was only one CKA_LABEL that matched
2234
return list.get(0);
2235
} else {
2236
throw new KeyStoreException("invalid KeyStore state: " +
2237
"found " +
2238
list.size() +
2239
" secret keys sharing CKA_LABEL [" +
2240
cka_label +
2241
"]");
2242
}
2243
} else if (type == ATTR_CLASS_CERT) {
2244
throw new KeyStoreException("invalid KeyStore state: " +
2245
"found " +
2246
h.length +
2247
" certificates sharing CKA_ID " +
2248
getID(cka_id));
2249
} else {
2250
throw new KeyStoreException("invalid KeyStore state: " +
2251
"found " +
2252
h.length +
2253
" private keys sharing CKA_ID " +
2254
getID(cka_id));
2255
}
2256
}
2257
return new THandle(NO_HANDLE, null);
2258
}
2259
2260
/**
2261
* Create a mapping of all key pairs, trusted certs, and secret keys
2262
* on the token into logical KeyStore entries unambiguously
2263
* accessible via an alias.
2264
*
2265
* If the token is removed, the map may contain stale values.
2266
* KeyStore.load should be called to re-create the map.
2267
*
2268
* Assume all private keys and matching certs share a unique CKA_ID.
2269
*
2270
* Assume all secret keys have a unique CKA_LABEL.
2271
*
2272
* @return true if multiple certs found sharing the same CKA_LABEL
2273
* (if so, write capabilities are disabled)
2274
*/
2275
private boolean mapLabels() throws
2276
PKCS11Exception, CertificateException, KeyStoreException {
2277
2278
CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] {
2279
new CK_ATTRIBUTE(CKA_TRUSTED) };
2280
2281
Session session = null;
2282
try {
2283
session = token.getOpSession();
2284
2285
// get all private key CKA_IDs
2286
2287
ArrayList<byte[]> pkeyIDs = new ArrayList<byte[]>();
2288
CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] {
2289
ATTR_TOKEN_TRUE,
2290
ATTR_CLASS_PKEY,
2291
};
2292
long[] handles = findObjects(session, attrs);
2293
2294
for (long handle : handles) {
2295
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
2296
token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2297
2298
if (attrs[0].pValue != null) {
2299
pkeyIDs.add(attrs[0].getByteArray());
2300
}
2301
}
2302
2303
// Get all certificates
2304
//
2305
// If cert does not have a CKA_LABEL nor CKA_ID, it is ignored.
2306
//
2307
// Get the CKA_LABEL for each cert
2308
// (if the cert does not have a CKA_LABEL, use the CKA_ID).
2309
//
2310
// Map each cert to the its CKA_LABEL
2311
// (multiple certs may be mapped to a single CKA_LABEL)
2312
2313
HashMap<String, HashSet<AliasInfo>> certMap =
2314
new HashMap<String, HashSet<AliasInfo>>();
2315
2316
attrs = new CK_ATTRIBUTE[] {
2317
ATTR_TOKEN_TRUE,
2318
ATTR_CLASS_CERT,
2319
};
2320
handles = findObjects(session, attrs);
2321
2322
for (long handle : handles) {
2323
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
2324
2325
String cka_label = null;
2326
byte[] cka_id = null;
2327
try {
2328
token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2329
if (attrs[0].pValue != null) {
2330
// there is a CKA_LABEL
2331
cka_label = new String(attrs[0].getCharArray());
2332
}
2333
} catch (PKCS11Exception pe) {
2334
if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) {
2335
throw pe;
2336
}
2337
2338
// GetAttributeValue for CKA_LABEL not supported
2339
//
2340
// XXX SCA1000
2341
}
2342
2343
// get CKA_ID
2344
2345
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) };
2346
token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2347
if (attrs[0].pValue == null) {
2348
if (cka_label == null) {
2349
// no cka_label nor cka_id - ignore
2350
continue;
2351
}
2352
} else {
2353
if (cka_label == null) {
2354
// use CKA_ID as CKA_LABEL
2355
cka_label = getID(attrs[0].getByteArray());
2356
}
2357
cka_id = attrs[0].getByteArray();
2358
}
2359
2360
X509Certificate cert = loadCert(session, handle);
2361
2362
// get CKA_TRUSTED
2363
2364
boolean cka_trusted = false;
2365
2366
if (useSecmodTrust) {
2367
cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType);
2368
} else {
2369
if (CKA_TRUSTED_SUPPORTED) {
2370
try {
2371
token.p11.C_GetAttributeValue
2372
(session.id(), handle, trustedAttr);
2373
cka_trusted = trustedAttr[0].getBoolean();
2374
} catch (PKCS11Exception pe) {
2375
if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) {
2376
// XXX NSS, ibutton, sca1000
2377
CKA_TRUSTED_SUPPORTED = false;
2378
if (debug != null) {
2379
debug.println
2380
("CKA_TRUSTED attribute not supported");
2381
}
2382
}
2383
}
2384
}
2385
}
2386
2387
HashSet<AliasInfo> infoSet = certMap.get(cka_label);
2388
if (infoSet == null) {
2389
infoSet = new HashSet<AliasInfo>(2);
2390
certMap.put(cka_label, infoSet);
2391
}
2392
2393
// initially create private key entry AliasInfo entries -
2394
// these entries will get resolved into their true
2395
// entry types later
2396
2397
infoSet.add(new AliasInfo
2398
(cka_label,
2399
cka_id,
2400
cka_trusted,
2401
cert));
2402
}
2403
2404
// create list secret key CKA_LABELS -
2405
// if there are duplicates (either between secret keys,
2406
// or between a secret key and another object),
2407
// throw an exception
2408
HashMap<String, AliasInfo> sKeyMap =
2409
new HashMap<String, AliasInfo>();
2410
2411
attrs = new CK_ATTRIBUTE[] {
2412
ATTR_SKEY_TOKEN_TRUE,
2413
ATTR_CLASS_SKEY,
2414
};
2415
handles = findObjects(session, attrs);
2416
2417
for (long handle : handles) {
2418
attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) };
2419
token.p11.C_GetAttributeValue(session.id(), handle, attrs);
2420
if (attrs[0].pValue != null) {
2421
2422
// there is a CKA_LABEL
2423
String cka_label = new String(attrs[0].getCharArray());
2424
if (sKeyMap.get(cka_label) == null) {
2425
sKeyMap.put(cka_label, new AliasInfo(cka_label));
2426
} else {
2427
throw new KeyStoreException("invalid KeyStore state: " +
2428
"found multiple secret keys sharing same " +
2429
"CKA_LABEL [" +
2430
cka_label +
2431
"]");
2432
}
2433
}
2434
}
2435
2436
// update global aliasMap with alias mappings
2437
ArrayList<AliasInfo> matchedCerts =
2438
mapPrivateKeys(pkeyIDs, certMap);
2439
boolean sharedLabel = mapCerts(matchedCerts, certMap);
2440
mapSecretKeys(sKeyMap);
2441
2442
return sharedLabel;
2443
2444
} finally {
2445
token.releaseSession(session);
2446
}
2447
}
2448
2449
/**
2450
* for each private key CKA_ID, find corresponding cert with same CKA_ID.
2451
* if found cert, see if cert CKA_LABEL is unique.
2452
* if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL.
2453
* if CKA_LABEL not unique, map private key/cert alias to:
2454
* CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL
2455
* if cert not found, ignore private key
2456
* (don't support private key entries without a cert chain yet)
2457
*
2458
* @return a list of AliasInfo entries that represents all matches
2459
*/
2460
private ArrayList<AliasInfo> mapPrivateKeys(ArrayList<byte[]> pkeyIDs,
2461
HashMap<String, HashSet<AliasInfo>> certMap)
2462
throws PKCS11Exception, CertificateException {
2463
2464
// reset global alias map
2465
aliasMap = new HashMap<String, AliasInfo>();
2466
2467
// list of matched certs that we will return
2468
ArrayList<AliasInfo> matchedCerts = new ArrayList<AliasInfo>();
2469
2470
for (byte[] pkeyID : pkeyIDs) {
2471
2472
// try to find a matching CKA_ID in a certificate
2473
2474
boolean foundMatch = false;
2475
Set<String> certLabels = certMap.keySet();
2476
for (String certLabel : certLabels) {
2477
2478
// get cert CKA_IDs (if present) for each cert
2479
2480
HashSet<AliasInfo> infoSet = certMap.get(certLabel);
2481
for (AliasInfo aliasInfo : infoSet) {
2482
if (Arrays.equals(pkeyID, aliasInfo.id)) {
2483
2484
// found private key with matching cert
2485
2486
if (infoSet.size() == 1) {
2487
// unique CKA_LABEL - use certLabel as alias
2488
aliasInfo.matched = true;
2489
aliasMap.put(certLabel, aliasInfo);
2490
} else {
2491
// create new alias
2492
aliasInfo.matched = true;
2493
aliasMap.put(getID(certLabel, aliasInfo.cert),
2494
aliasInfo);
2495
}
2496
matchedCerts.add(aliasInfo);
2497
foundMatch = true;
2498
break;
2499
}
2500
}
2501
if (foundMatch) {
2502
break;
2503
}
2504
}
2505
2506
if (!foundMatch) {
2507
if (debug != null) {
2508
debug.println
2509
("did not find match for private key with CKA_ID [" +
2510
getID(pkeyID) +
2511
"] (ignoring entry)");
2512
}
2513
}
2514
}
2515
2516
return matchedCerts;
2517
}
2518
2519
/**
2520
* for each cert not matched with a private key but is CKA_TRUSTED:
2521
* if CKA_LABEL unique, map cert to CKA_LABEL.
2522
* if CKA_LABEL not unique, map cert to [label+issuer+serialNum]
2523
*
2524
* if CKA_TRUSTED not supported, treat all certs not part of a chain
2525
* as trusted
2526
*
2527
* @return true if multiple certs found sharing the same CKA_LABEL
2528
*/
2529
private boolean mapCerts(ArrayList<AliasInfo> matchedCerts,
2530
HashMap<String, HashSet<AliasInfo>> certMap)
2531
throws PKCS11Exception, CertificateException {
2532
2533
// load all cert chains
2534
for (AliasInfo aliasInfo : matchedCerts) {
2535
Session session = null;
2536
try {
2537
session = token.getOpSession();
2538
aliasInfo.chain = loadChain(session, aliasInfo.cert);
2539
} finally {
2540
token.releaseSession(session);
2541
}
2542
}
2543
2544
// find all certs in certMap not part of a cert chain
2545
// - these are trusted
2546
2547
boolean sharedLabel = false;
2548
2549
Set<String> certLabels = certMap.keySet();
2550
for (String certLabel : certLabels) {
2551
HashSet<AliasInfo> infoSet = certMap.get(certLabel);
2552
for (AliasInfo aliasInfo : infoSet) {
2553
2554
if (aliasInfo.matched == true) {
2555
// already found a private key match for this cert -
2556
// just continue
2557
aliasInfo.trusted = false;
2558
continue;
2559
}
2560
2561
// cert in this aliasInfo is not matched yet
2562
//
2563
// if CKA_TRUSTED_SUPPORTED == true,
2564
// then check if cert is trusted
2565
2566
if (CKA_TRUSTED_SUPPORTED) {
2567
if (aliasInfo.trusted) {
2568
// trusted certificate
2569
if (mapTrustedCert
2570
(certLabel, aliasInfo, infoSet) == true) {
2571
sharedLabel = true;
2572
}
2573
}
2574
continue;
2575
}
2576
2577
// CKA_TRUSTED_SUPPORTED == false
2578
//
2579
// XXX treat all certs not part of a chain as trusted
2580
// XXX
2581
// XXX Unsupported
2582
//
2583
// boolean partOfChain = false;
2584
// for (AliasInfo matchedInfo : matchedCerts) {
2585
// for (int i = 0; i < matchedInfo.chain.length; i++) {
2586
// if (matchedInfo.chain[i].equals(aliasInfo.cert)) {
2587
// partOfChain = true;
2588
// break;
2589
// }
2590
// }
2591
// if (partOfChain) {
2592
// break;
2593
// }
2594
// }
2595
//
2596
// if (!partOfChain) {
2597
// if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){
2598
// sharedLabel = true;
2599
// }
2600
// } else {
2601
// if (debug != null) {
2602
// debug.println("ignoring unmatched/untrusted cert " +
2603
// "that is part of cert chain - cert subject is [" +
2604
// aliasInfo.cert.getSubjectX500Principal().getName
2605
// (X500Principal.CANONICAL) +
2606
// "]");
2607
// }
2608
// }
2609
}
2610
}
2611
2612
return sharedLabel;
2613
}
2614
2615
private boolean mapTrustedCert(String certLabel,
2616
AliasInfo aliasInfo,
2617
HashSet<AliasInfo> infoSet) {
2618
2619
boolean sharedLabel = false;
2620
2621
aliasInfo.type = ATTR_CLASS_CERT;
2622
aliasInfo.trusted = true;
2623
if (infoSet.size() == 1) {
2624
// unique CKA_LABEL - use certLabel as alias
2625
aliasMap.put(certLabel, aliasInfo);
2626
} else {
2627
// create new alias
2628
sharedLabel = true;
2629
aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo);
2630
}
2631
2632
return sharedLabel;
2633
}
2634
2635
/**
2636
* If the secret key shares a CKA_LABEL with another entry,
2637
* throw an exception
2638
*/
2639
private void mapSecretKeys(HashMap<String, AliasInfo> sKeyMap)
2640
throws KeyStoreException {
2641
for (String label : sKeyMap.keySet()) {
2642
if (aliasMap.containsKey(label)) {
2643
throw new KeyStoreException("invalid KeyStore state: " +
2644
"found secret key sharing CKA_LABEL [" +
2645
label +
2646
"] with another token object");
2647
}
2648
}
2649
aliasMap.putAll(sKeyMap);
2650
}
2651
2652
private void dumpTokenMap() {
2653
Set<String> aliases = aliasMap.keySet();
2654
System.out.println("Token Alias Map:");
2655
if (aliases.isEmpty()) {
2656
System.out.println(" [empty]");
2657
} else {
2658
for (String s : aliases) {
2659
System.out.println(" " + s + aliasMap.get(s));
2660
}
2661
}
2662
}
2663
2664
private void checkWrite() throws KeyStoreException {
2665
if (writeDisabled) {
2666
throw new KeyStoreException
2667
("This PKCS11KeyStore does not support write capabilities");
2668
}
2669
}
2670
2671
private final static long[] LONG0 = new long[0];
2672
2673
private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs)
2674
throws PKCS11Exception {
2675
Token token = session.token;
2676
long[] handles = LONG0;
2677
token.p11.C_FindObjectsInit(session.id(), attrs);
2678
while (true) {
2679
long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX);
2680
if (h.length == 0) {
2681
break;
2682
}
2683
handles = P11Util.concat(handles, h);
2684
}
2685
token.p11.C_FindObjectsFinal(session.id());
2686
return handles;
2687
}
2688
2689
}
2690
2691