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