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/pkcs12/PKCS12KeyStore.java
38919 views
1
/*
2
* Copyright (c) 1999, 2020, 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.pkcs12;
27
28
import java.io.*;
29
import java.security.AccessController;
30
import java.security.MessageDigest;
31
import java.security.NoSuchAlgorithmException;
32
import java.security.Key;
33
import java.security.KeyFactory;
34
import java.security.KeyStore;
35
import java.security.KeyStoreSpi;
36
import java.security.KeyStoreException;
37
import java.security.PKCS12Attribute;
38
import java.security.PrivateKey;
39
import java.security.PrivilegedAction;
40
import java.security.UnrecoverableEntryException;
41
import java.security.UnrecoverableKeyException;
42
import java.security.SecureRandom;
43
import java.security.Security;
44
import java.security.cert.Certificate;
45
import java.security.cert.CertificateFactory;
46
import java.security.cert.X509Certificate;
47
import java.security.cert.CertificateException;
48
import java.security.spec.AlgorithmParameterSpec;
49
import java.security.spec.InvalidParameterSpecException;
50
import java.security.spec.KeySpec;
51
import java.security.spec.PKCS8EncodedKeySpec;
52
import java.util.*;
53
54
import java.security.AlgorithmParameters;
55
import java.security.InvalidAlgorithmParameterException;
56
import javax.crypto.spec.PBEParameterSpec;
57
import javax.crypto.spec.PBEKeySpec;
58
import javax.crypto.spec.SecretKeySpec;
59
import javax.crypto.SecretKeyFactory;
60
import javax.crypto.SecretKey;
61
import javax.crypto.Cipher;
62
import javax.crypto.Mac;
63
import javax.security.auth.DestroyFailedException;
64
import javax.security.auth.x500.X500Principal;
65
66
import sun.security.util.Debug;
67
import sun.security.util.DerInputStream;
68
import sun.security.util.DerOutputStream;
69
import sun.security.util.DerValue;
70
import sun.security.util.ObjectIdentifier;
71
import sun.security.pkcs.ContentInfo;
72
import sun.security.x509.AlgorithmId;
73
import sun.security.pkcs.EncryptedPrivateKeyInfo;
74
75
76
/**
77
* This class provides the keystore implementation referred to as "PKCS12".
78
* Implements the PKCS#12 PFX protected using the Password privacy mode.
79
* The contents are protected using Password integrity mode.
80
*
81
* Currently we support following PBE algorithms:
82
* - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys
83
* - pbeWithSHAAnd40BitRC2CBC to encrypt certificates
84
*
85
* Supported encryption of various implementations :
86
*
87
* Software and mode. Certificate encryption Private key encryption
88
* ---------------------------------------------------------------------
89
* MSIE4 (domestic 40 bit RC2. 40 bit RC2
90
* and xport versions)
91
* PKCS#12 export.
92
*
93
* MSIE4, 5 (domestic 40 bit RC2, 40 bit RC2,
94
* and export versions) 3 key triple DES 3 key triple DES
95
* PKCS#12 import.
96
*
97
* MSIE5 40 bit RC2 3 key triple DES,
98
* PKCS#12 export. with SHA1 (168 bits)
99
*
100
* Netscape Communicator 40 bit RC2 3 key triple DES,
101
* (domestic and export with SHA1 (168 bits)
102
* versions) PKCS#12 export
103
*
104
* Netscape Communicator 40 bit ciphers only All.
105
* (export version)
106
* PKCS#12 import.
107
*
108
* Netscape Communicator All. All.
109
* (domestic or fortified
110
* version) PKCS#12 import.
111
*
112
* OpenSSL PKCS#12 code. All. All.
113
* ---------------------------------------------------------------------
114
*
115
* NOTE: PKCS12 KeyStore supports PrivateKeyEntry and TrustedCertficateEntry.
116
* PKCS#12 is mainly used to deliver private keys with their associated
117
* certificate chain and aliases. In a PKCS12 keystore, entries are
118
* identified by the alias, and a localKeyId is required to match the
119
* private key with the certificate. Trusted certificate entries are identified
120
* by the presence of an trustedKeyUsage attribute.
121
*
122
* @author Seema Malkani
123
* @author Jeff Nisewanger
124
* @author Jan Luehe
125
*
126
* @see KeyProtector
127
* @see java.security.KeyStoreSpi
128
* @see KeyTool
129
*
130
*
131
*/
132
public final class PKCS12KeyStore extends KeyStoreSpi {
133
134
public static final int VERSION_3 = 3;
135
136
private static final String[] KEY_PROTECTION_ALGORITHM = {
137
"keystore.pkcs12.keyProtectionAlgorithm",
138
"keystore.PKCS12.keyProtectionAlgorithm"
139
};
140
141
private static final int MAX_ITERATION_COUNT = 5000000;
142
private static final int PBE_ITERATION_COUNT = 50000; // default
143
private static final int MAC_ITERATION_COUNT = 100000; // default
144
private static final int SALT_LEN = 20;
145
146
// friendlyName, localKeyId, trustedKeyUsage
147
private static final String[] CORE_ATTRIBUTES = {
148
"1.2.840.113549.1.9.20",
149
"1.2.840.113549.1.9.21",
150
"2.16.840.1.113894.746875.1.1"
151
};
152
153
private static final Debug debug = Debug.getInstance("pkcs12");
154
155
private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2};
156
private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3};
157
private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5};
158
159
private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20};
160
private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21};
161
162
private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1};
163
164
private static final int pbeWithSHAAnd40BitRC2CBC[] =
165
{1, 2, 840, 113549, 1, 12, 1, 6};
166
private static final int pbeWithSHAAnd3KeyTripleDESCBC[] =
167
{1, 2, 840, 113549, 1, 12, 1, 3};
168
private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13};
169
// TODO: temporary Oracle OID
170
/*
171
* { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894)
172
* jdk(746875) crypto(1) id-at-trustedKeyUsage(1) }
173
*/
174
private static final int TrustedKeyUsage[] =
175
{2, 16, 840, 1, 113894, 746875, 1, 1};
176
private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0};
177
178
private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;
179
private static ObjectIdentifier CertBag_OID;
180
private static ObjectIdentifier SecretBag_OID;
181
private static ObjectIdentifier PKCS9FriendlyName_OID;
182
private static ObjectIdentifier PKCS9LocalKeyId_OID;
183
private static ObjectIdentifier PKCS9CertType_OID;
184
private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID;
185
private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;
186
private static ObjectIdentifier pbes2_OID;
187
private static ObjectIdentifier TrustedKeyUsage_OID;
188
private static ObjectIdentifier[] AnyUsage;
189
190
private int counter = 0;
191
192
// private key count
193
// Note: This is a workaround to allow null localKeyID attribute
194
// in pkcs12 with one private key entry and associated cert-chain
195
private int privateKeyCount = 0;
196
197
// secret key count
198
private int secretKeyCount = 0;
199
200
// certificate count
201
private int certificateCount = 0;
202
203
// the source of randomness
204
private SecureRandom random;
205
206
static {
207
try {
208
PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
209
CertBag_OID = new ObjectIdentifier(certBag);
210
SecretBag_OID = new ObjectIdentifier(secretBag);
211
PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name);
212
PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId);
213
PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType);
214
pbeWithSHAAnd40BitRC2CBC_OID =
215
new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC);
216
pbeWithSHAAnd3KeyTripleDESCBC_OID =
217
new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);
218
pbes2_OID = new ObjectIdentifier(pbes2);
219
TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage);
220
AnyUsage = new ObjectIdentifier[]{
221
new ObjectIdentifier(AnyExtendedKeyUsage)};
222
} catch (IOException ioe) {
223
// should not happen
224
}
225
}
226
227
// A keystore entry and associated attributes
228
private static class Entry {
229
Date date; // the creation date of this entry
230
String alias;
231
byte[] keyId;
232
Set<KeyStore.Entry.Attribute> attributes;
233
}
234
235
// A key entry
236
private static class KeyEntry extends Entry {
237
}
238
239
// A private key entry and its supporting certificate chain
240
private static class PrivateKeyEntry extends KeyEntry {
241
byte[] protectedPrivKey;
242
Certificate chain[];
243
};
244
245
// A secret key
246
private static class SecretKeyEntry extends KeyEntry {
247
byte[] protectedSecretKey;
248
};
249
250
// A certificate entry
251
private static class CertEntry extends Entry {
252
final X509Certificate cert;
253
ObjectIdentifier[] trustedKeyUsage;
254
255
CertEntry(X509Certificate cert, byte[] keyId, String alias) {
256
this(cert, keyId, alias, null, null);
257
}
258
259
CertEntry(X509Certificate cert, byte[] keyId, String alias,
260
ObjectIdentifier[] trustedKeyUsage,
261
Set<? extends KeyStore.Entry.Attribute> attributes) {
262
this.date = new Date();
263
this.cert = cert;
264
this.keyId = keyId;
265
this.alias = alias;
266
this.trustedKeyUsage = trustedKeyUsage;
267
this.attributes = new HashSet<>();
268
if (attributes != null) {
269
this.attributes.addAll(attributes);
270
}
271
}
272
}
273
274
/**
275
* Private keys and certificates are stored in a map.
276
* Map entries are keyed by alias names.
277
*/
278
private Map<String, Entry> entries =
279
Collections.synchronizedMap(new LinkedHashMap<String, Entry>());
280
281
private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>();
282
private LinkedHashMap<X500Principal, X509Certificate> certsMap =
283
new LinkedHashMap<X500Principal, X509Certificate>();
284
private ArrayList<CertEntry> certEntries = new ArrayList<CertEntry>();
285
286
/**
287
* Returns the key associated with the given alias, using the given
288
* password to recover it.
289
*
290
* @param alias the alias name
291
* @param password the password for recovering the key
292
*
293
* @return the requested key, or null if the given alias does not exist
294
* or does not identify a <i>key entry</i>.
295
*
296
* @exception NoSuchAlgorithmException if the algorithm for recovering the
297
* key cannot be found
298
* @exception UnrecoverableKeyException if the key cannot be recovered
299
* (e.g., the given password is wrong).
300
*/
301
public Key engineGetKey(String alias, char[] password)
302
throws NoSuchAlgorithmException, UnrecoverableKeyException
303
{
304
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
305
Key key = null;
306
307
if (entry == null || (!(entry instanceof KeyEntry))) {
308
return null;
309
}
310
311
// get the encoded private key or secret key
312
byte[] encrBytes = null;
313
if (entry instanceof PrivateKeyEntry) {
314
encrBytes = ((PrivateKeyEntry) entry).protectedPrivKey;
315
} else if (entry instanceof SecretKeyEntry) {
316
encrBytes = ((SecretKeyEntry) entry).protectedSecretKey;
317
} else {
318
throw new UnrecoverableKeyException("Error locating key");
319
}
320
321
byte[] encryptedKey;
322
AlgorithmParameters algParams;
323
ObjectIdentifier algOid;
324
325
try {
326
// get the encrypted private key
327
EncryptedPrivateKeyInfo encrInfo =
328
new EncryptedPrivateKeyInfo(encrBytes);
329
encryptedKey = encrInfo.getEncryptedData();
330
331
// parse Algorithm parameters
332
DerValue val = new DerValue(encrInfo.getAlgorithm().encode());
333
DerInputStream in = val.toDerInputStream();
334
algOid = in.getOID();
335
algParams = parseAlgParameters(algOid, in);
336
337
} catch (IOException ioe) {
338
UnrecoverableKeyException uke =
339
new UnrecoverableKeyException("Private key not stored as "
340
+ "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
341
uke.initCause(ioe);
342
throw uke;
343
}
344
345
try {
346
PBEParameterSpec pbeSpec;
347
int ic = 0;
348
349
if (algParams != null) {
350
try {
351
pbeSpec =
352
algParams.getParameterSpec(PBEParameterSpec.class);
353
} catch (InvalidParameterSpecException ipse) {
354
throw new IOException("Invalid PBE algorithm parameters");
355
}
356
ic = pbeSpec.getIterationCount();
357
358
if (ic > MAX_ITERATION_COUNT) {
359
throw new IOException("PBE iteration count too large");
360
}
361
}
362
363
byte[] keyInfo;
364
while (true) {
365
try {
366
// Use JCE
367
SecretKey skey = getPBEKey(password);
368
Cipher cipher = Cipher.getInstance(
369
mapPBEParamsToAlgorithm(algOid, algParams));
370
cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
371
keyInfo = cipher.doFinal(encryptedKey);
372
break;
373
} catch (Exception e) {
374
if (password.length == 0) {
375
// Retry using an empty password
376
// without a NULL terminator.
377
password = new char[1];
378
continue;
379
}
380
throw e;
381
}
382
}
383
384
/*
385
* Parse the key algorithm and then use a JCA key factory
386
* to re-create the key.
387
*/
388
DerValue val = new DerValue(keyInfo);
389
DerInputStream in = val.toDerInputStream();
390
int i = in.getInteger();
391
DerValue[] value = in.getSequence(2);
392
if (value.length < 1 || value.length > 2) {
393
throw new IOException("Invalid length for AlgorithmIdentifier");
394
}
395
AlgorithmId algId = new AlgorithmId(value[0].getOID());
396
String keyAlgo = algId.getName();
397
398
// decode private key
399
if (entry instanceof PrivateKeyEntry) {
400
KeyFactory kfac = KeyFactory.getInstance(keyAlgo);
401
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo);
402
key = kfac.generatePrivate(kspec);
403
404
if (debug != null) {
405
debug.println("Retrieved a protected private key at alias" +
406
" '" + alias + "' (" +
407
new AlgorithmId(algOid).getName() +
408
" iterations: " + ic + ")");
409
}
410
411
// decode secret key
412
} else {
413
byte[] keyBytes = in.getOctetString();
414
SecretKeySpec secretKeySpec =
415
new SecretKeySpec(keyBytes, keyAlgo);
416
417
// Special handling required for PBE: needs a PBEKeySpec
418
if (keyAlgo.startsWith("PBE")) {
419
SecretKeyFactory sKeyFactory =
420
SecretKeyFactory.getInstance(keyAlgo);
421
KeySpec pbeKeySpec =
422
sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class);
423
key = sKeyFactory.generateSecret(pbeKeySpec);
424
} else {
425
key = secretKeySpec;
426
}
427
428
if (debug != null) {
429
debug.println("Retrieved a protected secret key at alias " +
430
"'" + alias + "' (" +
431
new AlgorithmId(algOid).getName() +
432
" iterations: " + ic + ")");
433
}
434
}
435
} catch (Exception e) {
436
UnrecoverableKeyException uke =
437
new UnrecoverableKeyException("Get Key failed: " +
438
e.getMessage());
439
uke.initCause(e);
440
throw uke;
441
}
442
return key;
443
}
444
445
/**
446
* Returns the certificate chain associated with the given alias.
447
*
448
* @param alias the alias name
449
*
450
* @return the certificate chain (ordered with the user's certificate first
451
* and the root certificate authority last), or null if the given alias
452
* does not exist or does not contain a certificate chain (i.e., the given
453
* alias identifies either a <i>trusted certificate entry</i> or a
454
* <i>key entry</i> without a certificate chain).
455
*/
456
public Certificate[] engineGetCertificateChain(String alias) {
457
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
458
if (entry != null && entry instanceof PrivateKeyEntry) {
459
if (((PrivateKeyEntry) entry).chain == null) {
460
return null;
461
} else {
462
463
if (debug != null) {
464
debug.println("Retrieved a " +
465
((PrivateKeyEntry) entry).chain.length +
466
"-certificate chain at alias '" + alias + "'");
467
}
468
469
return ((PrivateKeyEntry) entry).chain.clone();
470
}
471
} else {
472
return null;
473
}
474
}
475
476
/**
477
* Returns the certificate associated with the given alias.
478
*
479
* <p>If the given alias name identifies a
480
* <i>trusted certificate entry</i>, the certificate associated with that
481
* entry is returned. If the given alias name identifies a
482
* <i>key entry</i>, the first element of the certificate chain of that
483
* entry is returned, or null if that entry does not have a certificate
484
* chain.
485
*
486
* @param alias the alias name
487
*
488
* @return the certificate, or null if the given alias does not exist or
489
* does not contain a certificate.
490
*/
491
public Certificate engineGetCertificate(String alias) {
492
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
493
if (entry == null) {
494
return null;
495
}
496
if (entry instanceof CertEntry &&
497
((CertEntry) entry).trustedKeyUsage != null) {
498
499
if (debug != null) {
500
if (Arrays.equals(AnyUsage,
501
((CertEntry) entry).trustedKeyUsage)) {
502
debug.println("Retrieved a certificate at alias '" + alias +
503
"' (trusted for any purpose)");
504
} else {
505
debug.println("Retrieved a certificate at alias '" + alias +
506
"' (trusted for limited purposes)");
507
}
508
}
509
510
return ((CertEntry) entry).cert;
511
512
} else if (entry instanceof PrivateKeyEntry) {
513
if (((PrivateKeyEntry) entry).chain == null) {
514
return null;
515
} else {
516
517
if (debug != null) {
518
debug.println("Retrieved a certificate at alias '" + alias +
519
"'");
520
}
521
522
return ((PrivateKeyEntry) entry).chain[0];
523
}
524
525
} else {
526
return null;
527
}
528
}
529
530
/**
531
* Returns the creation date of the entry identified by the given alias.
532
*
533
* @param alias the alias name
534
*
535
* @return the creation date of this entry, or null if the given alias does
536
* not exist
537
*/
538
public Date engineGetCreationDate(String alias) {
539
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
540
if (entry != null) {
541
return new Date(entry.date.getTime());
542
} else {
543
return null;
544
}
545
}
546
547
/**
548
* Assigns the given key to the given alias, protecting it with the given
549
* password.
550
*
551
* <p>If the given key is of type <code>java.security.PrivateKey</code>,
552
* it must be accompanied by a certificate chain certifying the
553
* corresponding public key.
554
*
555
* <p>If the given alias already exists, the keystore information
556
* associated with it is overridden by the given key (and possibly
557
* certificate chain).
558
*
559
* @param alias the alias name
560
* @param key the key to be associated with the alias
561
* @param password the password to protect the key
562
* @param chain the certificate chain for the corresponding public
563
* key (only required if the given key is of type
564
* <code>java.security.PrivateKey</code>).
565
*
566
* @exception KeyStoreException if the given key cannot be protected, or
567
* this operation fails for some other reason
568
*/
569
public synchronized void engineSetKeyEntry(String alias, Key key,
570
char[] password, Certificate[] chain)
571
throws KeyStoreException
572
{
573
KeyStore.PasswordProtection passwordProtection =
574
new KeyStore.PasswordProtection(password);
575
576
try {
577
setKeyEntry(alias, key, passwordProtection, chain, null);
578
579
} finally {
580
try {
581
passwordProtection.destroy();
582
} catch (DestroyFailedException dfe) {
583
// ignore
584
}
585
}
586
}
587
588
/*
589
* Sets a key entry (with attributes, when present)
590
*/
591
private void setKeyEntry(String alias, Key key,
592
KeyStore.PasswordProtection passwordProtection, Certificate[] chain,
593
Set<KeyStore.Entry.Attribute> attributes)
594
throws KeyStoreException
595
{
596
try {
597
Entry entry;
598
599
if (key instanceof PrivateKey) {
600
PrivateKeyEntry keyEntry = new PrivateKeyEntry();
601
keyEntry.date = new Date();
602
603
if ((key.getFormat().equals("PKCS#8")) ||
604
(key.getFormat().equals("PKCS8"))) {
605
606
if (debug != null) {
607
debug.println(
608
"Setting a protected private key at alias '" +
609
alias + "'");
610
}
611
612
// Encrypt the private key
613
keyEntry.protectedPrivKey =
614
encryptPrivateKey(key.getEncoded(), passwordProtection);
615
} else {
616
throw new KeyStoreException("Private key is not encoded" +
617
"as PKCS#8");
618
}
619
620
// clone the chain
621
if (chain != null) {
622
// validate cert-chain
623
if ((chain.length > 1) && (!validateChain(chain)))
624
throw new KeyStoreException("Certificate chain is " +
625
"not valid");
626
keyEntry.chain = chain.clone();
627
certificateCount += chain.length;
628
629
if (debug != null) {
630
debug.println("Setting a " + chain.length +
631
"-certificate chain at alias '" + alias + "'");
632
}
633
}
634
privateKeyCount++;
635
entry = keyEntry;
636
637
} else if (key instanceof SecretKey) {
638
SecretKeyEntry keyEntry = new SecretKeyEntry();
639
keyEntry.date = new Date();
640
641
// Encode secret key in a PKCS#8
642
DerOutputStream pkcs8 = new DerOutputStream();
643
DerOutputStream secretKeyInfo = new DerOutputStream();
644
secretKeyInfo.putInteger(0);
645
AlgorithmId algId = AlgorithmId.get(key.getAlgorithm());
646
algId.encode(secretKeyInfo);
647
secretKeyInfo.putOctetString(key.getEncoded());
648
pkcs8.write(DerValue.tag_Sequence, secretKeyInfo);
649
650
// Encrypt the secret key (using same PBE as for private keys)
651
keyEntry.protectedSecretKey =
652
encryptPrivateKey(pkcs8.toByteArray(), passwordProtection);
653
654
if (debug != null) {
655
debug.println("Setting a protected secret key at alias '" +
656
alias + "'");
657
}
658
secretKeyCount++;
659
entry = keyEntry;
660
661
} else {
662
throw new KeyStoreException("Unsupported Key type");
663
}
664
665
entry.attributes = new HashSet<>();
666
if (attributes != null) {
667
entry.attributes.addAll(attributes);
668
}
669
// set the keyId to current date
670
entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
671
// set the alias
672
entry.alias = alias.toLowerCase(Locale.ENGLISH);
673
// add the entry
674
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
675
676
} catch (Exception nsae) {
677
throw new KeyStoreException("Key protection " +
678
" algorithm not found: " + nsae, nsae);
679
}
680
}
681
682
/**
683
* Assigns the given key (that has already been protected) to the given
684
* alias.
685
*
686
* <p>If the protected key is of type
687
* <code>java.security.PrivateKey</code>, it must be accompanied by a
688
* certificate chain certifying the corresponding public key. If the
689
* underlying keystore implementation is of type <code>jks</code>,
690
* <code>key</code> must be encoded as an
691
* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
692
*
693
* <p>If the given alias already exists, the keystore information
694
* associated with it is overridden by the given key (and possibly
695
* certificate chain).
696
*
697
* @param alias the alias name
698
* @param key the key (in protected format) to be associated with the alias
699
* @param chain the certificate chain for the corresponding public
700
* key (only useful if the protected key is of type
701
* <code>java.security.PrivateKey</code>).
702
*
703
* @exception KeyStoreException if this operation fails.
704
*/
705
public synchronized void engineSetKeyEntry(String alias, byte[] key,
706
Certificate[] chain)
707
throws KeyStoreException
708
{
709
// Private key must be encoded as EncryptedPrivateKeyInfo
710
// as defined in PKCS#8
711
try {
712
new EncryptedPrivateKeyInfo(key);
713
} catch (IOException ioe) {
714
throw new KeyStoreException("Private key is not stored"
715
+ " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe);
716
}
717
718
PrivateKeyEntry entry = new PrivateKeyEntry();
719
entry.date = new Date();
720
721
if (debug != null) {
722
debug.println("Setting a protected private key at alias '" +
723
alias + "'");
724
}
725
726
try {
727
// set the keyId to current date
728
entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
729
} catch (UnsupportedEncodingException ex) {
730
// Won't happen
731
}
732
// set the alias
733
entry.alias = alias.toLowerCase(Locale.ENGLISH);
734
735
entry.protectedPrivKey = key.clone();
736
if (chain != null) {
737
// validate cert-chain
738
if ((chain.length > 1) && (!validateChain(chain))) {
739
throw new KeyStoreException("Certificate chain is "
740
+ "not valid");
741
}
742
entry.chain = chain.clone();
743
certificateCount += chain.length;
744
745
if (debug != null) {
746
debug.println("Setting a " + entry.chain.length +
747
"-certificate chain at alias '" + alias + "'");
748
}
749
}
750
751
// add the entry
752
privateKeyCount++;
753
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
754
}
755
756
757
/*
758
* Generate random salt
759
*/
760
private byte[] getSalt()
761
{
762
// Generate a random salt.
763
byte[] salt = new byte[SALT_LEN];
764
if (random == null) {
765
random = new SecureRandom();
766
}
767
random.nextBytes(salt);
768
return salt;
769
}
770
771
/*
772
* Generate PBE Algorithm Parameters
773
*/
774
private AlgorithmParameters getPBEAlgorithmParameters(String algorithm)
775
throws IOException
776
{
777
AlgorithmParameters algParams = null;
778
779
// create PBE parameters from salt and iteration count
780
PBEParameterSpec paramSpec =
781
new PBEParameterSpec(getSalt(), PBE_ITERATION_COUNT);
782
try {
783
algParams = AlgorithmParameters.getInstance(algorithm);
784
algParams.init(paramSpec);
785
} catch (Exception e) {
786
throw new IOException("getPBEAlgorithmParameters failed: " +
787
e.getMessage(), e);
788
}
789
return algParams;
790
}
791
792
/*
793
* parse Algorithm Parameters
794
*/
795
private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm,
796
DerInputStream in) throws IOException
797
{
798
AlgorithmParameters algParams = null;
799
try {
800
DerValue params;
801
if (in.available() == 0) {
802
params = null;
803
} else {
804
params = in.getDerValue();
805
if (params.tag == DerValue.tag_Null) {
806
params = null;
807
}
808
}
809
if (params != null) {
810
if (algorithm.equals((Object)pbes2_OID)) {
811
algParams = AlgorithmParameters.getInstance("PBES2");
812
} else {
813
algParams = AlgorithmParameters.getInstance("PBE");
814
}
815
algParams.init(params.toByteArray());
816
}
817
} catch (Exception e) {
818
throw new IOException("parseAlgParameters failed: " +
819
e.getMessage(), e);
820
}
821
return algParams;
822
}
823
824
/*
825
* Generate PBE key
826
*/
827
private SecretKey getPBEKey(char[] password) throws IOException
828
{
829
SecretKey skey = null;
830
831
try {
832
PBEKeySpec keySpec = new PBEKeySpec(password);
833
SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
834
skey = skFac.generateSecret(keySpec);
835
keySpec.clearPassword();
836
} catch (Exception e) {
837
throw new IOException("getSecretKey failed: " +
838
e.getMessage(), e);
839
}
840
return skey;
841
}
842
843
/*
844
* Encrypt private key using Password-based encryption (PBE)
845
* as defined in PKCS#5.
846
*
847
* NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
848
* used to derive the key and IV.
849
*
850
* @return encrypted private key encoded as EncryptedPrivateKeyInfo
851
*/
852
private byte[] encryptPrivateKey(byte[] data,
853
KeyStore.PasswordProtection passwordProtection)
854
throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
855
{
856
byte[] key = null;
857
858
try {
859
String algorithm;
860
AlgorithmParameters algParams;
861
AlgorithmId algid;
862
863
// Initialize PBE algorithm and parameters
864
algorithm = passwordProtection.getProtectionAlgorithm();
865
if (algorithm != null) {
866
AlgorithmParameterSpec algParamSpec =
867
passwordProtection.getProtectionParameters();
868
if (algParamSpec != null) {
869
algParams = AlgorithmParameters.getInstance(algorithm);
870
algParams.init(algParamSpec);
871
} else {
872
algParams = getPBEAlgorithmParameters(algorithm);
873
}
874
} else {
875
// Check default key protection algorithm for PKCS12 keystores
876
algorithm = AccessController.doPrivileged(
877
new PrivilegedAction<String>() {
878
public String run() {
879
String prop =
880
Security.getProperty(
881
KEY_PROTECTION_ALGORITHM[0]);
882
if (prop == null) {
883
prop = Security.getProperty(
884
KEY_PROTECTION_ALGORITHM[1]);
885
}
886
return prop;
887
}
888
});
889
if (algorithm == null || algorithm.isEmpty()) {
890
algorithm = "PBEWithSHA1AndDESede";
891
}
892
algParams = getPBEAlgorithmParameters(algorithm);
893
}
894
895
ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm);
896
if (pbeOID == null) {
897
throw new IOException("PBE algorithm '" + algorithm +
898
" 'is not supported for key entry protection");
899
}
900
901
// Use JCE
902
SecretKey skey = getPBEKey(passwordProtection.getPassword());
903
Cipher cipher = Cipher.getInstance(algorithm);
904
cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
905
byte[] encryptedKey = cipher.doFinal(data);
906
algid = new AlgorithmId(pbeOID, cipher.getParameters());
907
908
if (debug != null) {
909
debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() +
910
")");
911
}
912
913
// wrap encrypted private key in EncryptedPrivateKeyInfo
914
// as defined in PKCS#8
915
EncryptedPrivateKeyInfo encrInfo =
916
new EncryptedPrivateKeyInfo(algid, encryptedKey);
917
key = encrInfo.getEncoded();
918
} catch (Exception e) {
919
UnrecoverableKeyException uke =
920
new UnrecoverableKeyException("Encrypt Private Key failed: "
921
+ e.getMessage());
922
uke.initCause(e);
923
throw uke;
924
}
925
926
return key;
927
}
928
929
/*
930
* Map a PBE algorithm name onto its object identifier
931
*/
932
private static ObjectIdentifier mapPBEAlgorithmToOID(String algorithm)
933
throws NoSuchAlgorithmException {
934
// Check for PBES2 algorithms
935
if (algorithm.toLowerCase(Locale.ENGLISH).startsWith("pbewithhmacsha")) {
936
return pbes2_OID;
937
}
938
return AlgorithmId.get(algorithm).getOID();
939
}
940
941
/*
942
* Map a PBE algorithm parameters onto its algorithm name
943
*/
944
private static String mapPBEParamsToAlgorithm(ObjectIdentifier algorithm,
945
AlgorithmParameters algParams) throws NoSuchAlgorithmException {
946
// Check for PBES2 algorithms
947
if (algorithm.equals((Object)pbes2_OID) && algParams != null) {
948
return algParams.toString();
949
}
950
return algorithm.toString();
951
}
952
953
/**
954
* Assigns the given certificate to the given alias.
955
*
956
* <p>If the given alias already exists in this keystore and identifies a
957
* <i>trusted certificate entry</i>, the certificate associated with it is
958
* overridden by the given certificate.
959
*
960
* @param alias the alias name
961
* @param cert the certificate
962
*
963
* @exception KeyStoreException if the given alias already exists and does
964
* not identify a <i>trusted certificate entry</i>, or this operation fails
965
* for some other reason.
966
*/
967
public synchronized void engineSetCertificateEntry(String alias,
968
Certificate cert) throws KeyStoreException
969
{
970
setCertEntry(alias, cert, null);
971
}
972
973
/*
974
* Sets a trusted cert entry (with attributes, when present)
975
*/
976
private void setCertEntry(String alias, Certificate cert,
977
Set<KeyStore.Entry.Attribute> attributes) throws KeyStoreException {
978
979
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
980
if (entry != null && entry instanceof KeyEntry) {
981
throw new KeyStoreException("Cannot overwrite own certificate");
982
}
983
984
CertEntry certEntry =
985
new CertEntry((X509Certificate) cert, null, alias, AnyUsage,
986
attributes);
987
certificateCount++;
988
entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
989
990
if (debug != null) {
991
debug.println("Setting a trusted certificate at alias '" + alias +
992
"'");
993
}
994
}
995
996
/**
997
* Deletes the entry identified by the given alias from this keystore.
998
*
999
* @param alias the alias name
1000
*
1001
* @exception KeyStoreException if the entry cannot be removed.
1002
*/
1003
public synchronized void engineDeleteEntry(String alias)
1004
throws KeyStoreException
1005
{
1006
if (debug != null) {
1007
debug.println("Removing entry at alias '" + alias + "'");
1008
}
1009
1010
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
1011
if (entry instanceof PrivateKeyEntry) {
1012
PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
1013
if (keyEntry.chain != null) {
1014
certificateCount -= keyEntry.chain.length;
1015
}
1016
privateKeyCount--;
1017
} else if (entry instanceof CertEntry) {
1018
certificateCount--;
1019
} else if (entry instanceof SecretKeyEntry) {
1020
secretKeyCount--;
1021
}
1022
entries.remove(alias.toLowerCase(Locale.ENGLISH));
1023
}
1024
1025
/**
1026
* Lists all the alias names of this keystore.
1027
*
1028
* @return enumeration of the alias names
1029
*/
1030
public Enumeration<String> engineAliases() {
1031
return Collections.enumeration(entries.keySet());
1032
}
1033
1034
/**
1035
* Checks if the given alias exists in this keystore.
1036
*
1037
* @param alias the alias name
1038
*
1039
* @return true if the alias exists, false otherwise
1040
*/
1041
public boolean engineContainsAlias(String alias) {
1042
return entries.containsKey(alias.toLowerCase(Locale.ENGLISH));
1043
}
1044
1045
/**
1046
* Retrieves the number of entries in this keystore.
1047
*
1048
* @return the number of entries in this keystore
1049
*/
1050
public int engineSize() {
1051
return entries.size();
1052
}
1053
1054
/**
1055
* Returns true if the entry identified by the given alias is a
1056
* <i>key entry</i>, and false otherwise.
1057
*
1058
* @return true if the entry identified by the given alias is a
1059
* <i>key entry</i>, false otherwise.
1060
*/
1061
public boolean engineIsKeyEntry(String alias) {
1062
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
1063
if (entry != null && entry instanceof KeyEntry) {
1064
return true;
1065
} else {
1066
return false;
1067
}
1068
}
1069
1070
/**
1071
* Returns true if the entry identified by the given alias is a
1072
* <i>trusted certificate entry</i>, and false otherwise.
1073
*
1074
* @return true if the entry identified by the given alias is a
1075
* <i>trusted certificate entry</i>, false otherwise.
1076
*/
1077
public boolean engineIsCertificateEntry(String alias) {
1078
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
1079
if (entry != null && entry instanceof CertEntry &&
1080
((CertEntry) entry).trustedKeyUsage != null) {
1081
return true;
1082
} else {
1083
return false;
1084
}
1085
}
1086
1087
/**
1088
* Determines if the keystore {@code Entry} for the specified
1089
* {@code alias} is an instance or subclass of the specified
1090
* {@code entryClass}.
1091
*
1092
* @param alias the alias name
1093
* @param entryClass the entry class
1094
*
1095
* @return true if the keystore {@code Entry} for the specified
1096
* {@code alias} is an instance or subclass of the
1097
* specified {@code entryClass}, false otherwise
1098
*
1099
* @since 1.5
1100
*/
1101
@Override
1102
public boolean
1103
engineEntryInstanceOf(String alias,
1104
Class<? extends KeyStore.Entry> entryClass)
1105
{
1106
if (entryClass == KeyStore.TrustedCertificateEntry.class) {
1107
return engineIsCertificateEntry(alias);
1108
}
1109
1110
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
1111
if (entryClass == KeyStore.PrivateKeyEntry.class) {
1112
return (entry != null && entry instanceof PrivateKeyEntry);
1113
}
1114
if (entryClass == KeyStore.SecretKeyEntry.class) {
1115
return (entry != null && entry instanceof SecretKeyEntry);
1116
}
1117
return false;
1118
}
1119
1120
/**
1121
* Returns the (alias) name of the first keystore entry whose certificate
1122
* matches the given certificate.
1123
*
1124
* <p>This method attempts to match the given certificate with each
1125
* keystore entry. If the entry being considered
1126
* is a <i>trusted certificate entry</i>, the given certificate is
1127
* compared to that entry's certificate. If the entry being considered is
1128
* a <i>key entry</i>, the given certificate is compared to the first
1129
* element of that entry's certificate chain (if a chain exists).
1130
*
1131
* @param cert the certificate to match with.
1132
*
1133
* @return the (alias) name of the first entry with matching certificate,
1134
* or null if no such entry exists in this keystore.
1135
*/
1136
public String engineGetCertificateAlias(Certificate cert) {
1137
Certificate certElem = null;
1138
1139
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1140
String alias = e.nextElement();
1141
Entry entry = entries.get(alias);
1142
if (entry instanceof PrivateKeyEntry) {
1143
if (((PrivateKeyEntry) entry).chain != null) {
1144
certElem = ((PrivateKeyEntry) entry).chain[0];
1145
}
1146
} else if (entry instanceof CertEntry &&
1147
((CertEntry) entry).trustedKeyUsage != null) {
1148
certElem = ((CertEntry) entry).cert;
1149
} else {
1150
continue;
1151
}
1152
if (certElem != null && certElem.equals(cert)) {
1153
return alias;
1154
}
1155
}
1156
return null;
1157
}
1158
1159
/**
1160
* Stores this keystore to the given output stream, and protects its
1161
* integrity with the given password.
1162
*
1163
* @param stream the output stream to which this keystore is written.
1164
* @param password the password to generate the keystore integrity check
1165
*
1166
* @exception IOException if there was an I/O problem with data
1167
* @exception NoSuchAlgorithmException if the appropriate data integrity
1168
* algorithm could not be found
1169
* @exception CertificateException if any of the certificates included in
1170
* the keystore data could not be stored
1171
*/
1172
public synchronized void engineStore(OutputStream stream, char[] password)
1173
throws IOException, NoSuchAlgorithmException, CertificateException
1174
{
1175
// password is mandatory when storing
1176
if (password == null) {
1177
throw new IllegalArgumentException("password can't be null");
1178
}
1179
1180
// -- Create PFX
1181
DerOutputStream pfx = new DerOutputStream();
1182
1183
// PFX version (always write the latest version)
1184
DerOutputStream version = new DerOutputStream();
1185
version.putInteger(VERSION_3);
1186
byte[] pfxVersion = version.toByteArray();
1187
pfx.write(pfxVersion);
1188
1189
// -- Create AuthSafe
1190
DerOutputStream authSafe = new DerOutputStream();
1191
1192
// -- Create ContentInfos
1193
DerOutputStream authSafeContentInfo = new DerOutputStream();
1194
1195
// -- create safeContent Data ContentInfo
1196
if (privateKeyCount > 0 || secretKeyCount > 0) {
1197
1198
if (debug != null) {
1199
debug.println("Storing " + (privateKeyCount + secretKeyCount) +
1200
" protected key(s) in a PKCS#7 data");
1201
}
1202
1203
byte[] safeContentData = createSafeContent();
1204
ContentInfo dataContentInfo = new ContentInfo(safeContentData);
1205
dataContentInfo.encode(authSafeContentInfo);
1206
}
1207
1208
// -- create EncryptedContentInfo
1209
if (certificateCount > 0) {
1210
1211
if (debug != null) {
1212
debug.println("Storing " + certificateCount +
1213
" certificate(s) in a PKCS#7 encryptedData");
1214
}
1215
1216
byte[] encrData = createEncryptedData(password);
1217
ContentInfo encrContentInfo =
1218
new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
1219
new DerValue(encrData));
1220
encrContentInfo.encode(authSafeContentInfo);
1221
}
1222
1223
// wrap as SequenceOf ContentInfos
1224
DerOutputStream cInfo = new DerOutputStream();
1225
cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
1226
byte[] authenticatedSafe = cInfo.toByteArray();
1227
1228
// Create Encapsulated ContentInfo
1229
ContentInfo contentInfo = new ContentInfo(authenticatedSafe);
1230
contentInfo.encode(authSafe);
1231
byte[] authSafeData = authSafe.toByteArray();
1232
pfx.write(authSafeData);
1233
1234
// -- MAC
1235
byte[] macData = calculateMac(password, authenticatedSafe);
1236
pfx.write(macData);
1237
1238
// write PFX to output stream
1239
DerOutputStream pfxout = new DerOutputStream();
1240
pfxout.write(DerValue.tag_Sequence, pfx);
1241
byte[] pfxData = pfxout.toByteArray();
1242
stream.write(pfxData);
1243
stream.flush();
1244
}
1245
1246
/**
1247
* Gets a <code>KeyStore.Entry</code> for the specified alias
1248
* with the specified protection parameter.
1249
*
1250
* @param alias get the <code>KeyStore.Entry</code> for this alias
1251
* @param protParam the <code>ProtectionParameter</code>
1252
* used to protect the <code>Entry</code>,
1253
* which may be <code>null</code>
1254
*
1255
* @return the <code>KeyStore.Entry</code> for the specified alias,
1256
* or <code>null</code> if there is no such entry
1257
*
1258
* @exception KeyStoreException if the operation failed
1259
* @exception NoSuchAlgorithmException if the algorithm for recovering the
1260
* entry cannot be found
1261
* @exception UnrecoverableEntryException if the specified
1262
* <code>protParam</code> were insufficient or invalid
1263
* @exception UnrecoverableKeyException if the entry is a
1264
* <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>
1265
* and the specified <code>protParam</code> does not contain
1266
* the information needed to recover the key (e.g. wrong password)
1267
*
1268
* @since 1.5
1269
*/
1270
@Override
1271
public KeyStore.Entry engineGetEntry(String alias,
1272
KeyStore.ProtectionParameter protParam)
1273
throws KeyStoreException, NoSuchAlgorithmException,
1274
UnrecoverableEntryException {
1275
1276
if (!engineContainsAlias(alias)) {
1277
return null;
1278
}
1279
1280
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
1281
if (protParam == null) {
1282
if (engineIsCertificateEntry(alias)) {
1283
if (entry instanceof CertEntry &&
1284
((CertEntry) entry).trustedKeyUsage != null) {
1285
1286
if (debug != null) {
1287
debug.println("Retrieved a trusted certificate at " +
1288
"alias '" + alias + "'");
1289
}
1290
1291
return new KeyStore.TrustedCertificateEntry(
1292
((CertEntry)entry).cert, getAttributes(entry));
1293
}
1294
} else {
1295
throw new UnrecoverableKeyException
1296
("requested entry requires a password");
1297
}
1298
}
1299
1300
if (protParam instanceof KeyStore.PasswordProtection) {
1301
if (engineIsCertificateEntry(alias)) {
1302
throw new UnsupportedOperationException
1303
("trusted certificate entries are not password-protected");
1304
} else if (engineIsKeyEntry(alias)) {
1305
KeyStore.PasswordProtection pp =
1306
(KeyStore.PasswordProtection)protParam;
1307
char[] password = pp.getPassword();
1308
1309
Key key = engineGetKey(alias, password);
1310
if (key instanceof PrivateKey) {
1311
Certificate[] chain = engineGetCertificateChain(alias);
1312
1313
return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain,
1314
getAttributes(entry));
1315
1316
} else if (key instanceof SecretKey) {
1317
1318
return new KeyStore.SecretKeyEntry((SecretKey)key,
1319
getAttributes(entry));
1320
}
1321
} else if (!engineIsKeyEntry(alias)) {
1322
throw new UnsupportedOperationException
1323
("untrusted certificate entries are not " +
1324
"password-protected");
1325
}
1326
}
1327
1328
throw new UnsupportedOperationException();
1329
}
1330
1331
/**
1332
* Saves a <code>KeyStore.Entry</code> under the specified alias.
1333
* The specified protection parameter is used to protect the
1334
* <code>Entry</code>.
1335
*
1336
* <p> If an entry already exists for the specified alias,
1337
* it is overridden.
1338
*
1339
* @param alias save the <code>KeyStore.Entry</code> under this alias
1340
* @param entry the <code>Entry</code> to save
1341
* @param protParam the <code>ProtectionParameter</code>
1342
* used to protect the <code>Entry</code>,
1343
* which may be <code>null</code>
1344
*
1345
* @exception KeyStoreException if this operation fails
1346
*
1347
* @since 1.5
1348
*/
1349
@Override
1350
public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
1351
KeyStore.ProtectionParameter protParam) throws KeyStoreException {
1352
1353
// get password
1354
if (protParam != null &&
1355
!(protParam instanceof KeyStore.PasswordProtection)) {
1356
throw new KeyStoreException("unsupported protection parameter");
1357
}
1358
KeyStore.PasswordProtection pProtect = null;
1359
if (protParam != null) {
1360
pProtect = (KeyStore.PasswordProtection)protParam;
1361
}
1362
1363
// set entry
1364
if (entry instanceof KeyStore.TrustedCertificateEntry) {
1365
if (protParam != null && pProtect.getPassword() != null) {
1366
// pre-1.5 style setCertificateEntry did not allow password
1367
throw new KeyStoreException
1368
("trusted certificate entries are not password-protected");
1369
} else {
1370
KeyStore.TrustedCertificateEntry tce =
1371
(KeyStore.TrustedCertificateEntry)entry;
1372
setCertEntry(alias, tce.getTrustedCertificate(),
1373
tce.getAttributes());
1374
1375
return;
1376
}
1377
} else if (entry instanceof KeyStore.PrivateKeyEntry) {
1378
if (pProtect == null || pProtect.getPassword() == null) {
1379
// pre-1.5 style setKeyEntry required password
1380
throw new KeyStoreException
1381
("non-null password required to create PrivateKeyEntry");
1382
} else {
1383
KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry;
1384
setKeyEntry(alias, pke.getPrivateKey(), pProtect,
1385
pke.getCertificateChain(), pke.getAttributes());
1386
1387
return;
1388
}
1389
} else if (entry instanceof KeyStore.SecretKeyEntry) {
1390
if (pProtect == null || pProtect.getPassword() == null) {
1391
// pre-1.5 style setKeyEntry required password
1392
throw new KeyStoreException
1393
("non-null password required to create SecretKeyEntry");
1394
} else {
1395
KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;
1396
setKeyEntry(alias, ske.getSecretKey(), pProtect,
1397
(Certificate[])null, ske.getAttributes());
1398
1399
return;
1400
}
1401
}
1402
1403
throw new KeyStoreException
1404
("unsupported entry type: " + entry.getClass().getName());
1405
}
1406
1407
/*
1408
* Assemble the entry attributes
1409
*/
1410
private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) {
1411
1412
if (entry.attributes == null) {
1413
entry.attributes = new HashSet<>();
1414
}
1415
1416
// friendlyName
1417
entry.attributes.add(new PKCS12Attribute(
1418
PKCS9FriendlyName_OID.toString(), entry.alias));
1419
1420
// localKeyID
1421
byte[] keyIdValue = entry.keyId;
1422
if (keyIdValue != null) {
1423
entry.attributes.add(new PKCS12Attribute(
1424
PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue)));
1425
}
1426
1427
// trustedKeyUsage
1428
if (entry instanceof CertEntry) {
1429
ObjectIdentifier[] trustedKeyUsageValue =
1430
((CertEntry) entry).trustedKeyUsage;
1431
if (trustedKeyUsageValue != null) {
1432
if (trustedKeyUsageValue.length == 1) { // omit brackets
1433
entry.attributes.add(new PKCS12Attribute(
1434
TrustedKeyUsage_OID.toString(),
1435
trustedKeyUsageValue[0].toString()));
1436
} else { // multi-valued
1437
entry.attributes.add(new PKCS12Attribute(
1438
TrustedKeyUsage_OID.toString(),
1439
Arrays.toString(trustedKeyUsageValue)));
1440
}
1441
}
1442
}
1443
1444
return entry.attributes;
1445
}
1446
1447
/*
1448
* Generate Hash.
1449
*/
1450
private byte[] generateHash(byte[] data) throws IOException
1451
{
1452
byte[] digest = null;
1453
1454
try {
1455
MessageDigest md = MessageDigest.getInstance("SHA1");
1456
md.update(data);
1457
digest = md.digest();
1458
} catch (Exception e) {
1459
throw new IOException("generateHash failed: " + e, e);
1460
}
1461
return digest;
1462
}
1463
1464
1465
/*
1466
* Calculate MAC using HMAC algorithm (required for password integrity)
1467
*
1468
* Hash-based MAC algorithm combines secret key with message digest to
1469
* create a message authentication code (MAC)
1470
*/
1471
private byte[] calculateMac(char[] passwd, byte[] data)
1472
throws IOException
1473
{
1474
byte[] mData = null;
1475
String algName = "SHA1";
1476
1477
try {
1478
// Generate a random salt.
1479
byte[] salt = getSalt();
1480
1481
// generate MAC (MAC key is generated within JCE)
1482
Mac m = Mac.getInstance("HmacPBESHA1");
1483
PBEParameterSpec params =
1484
new PBEParameterSpec(salt, MAC_ITERATION_COUNT);
1485
SecretKey key = getPBEKey(passwd);
1486
m.init(key, params);
1487
m.update(data);
1488
byte[] macResult = m.doFinal();
1489
1490
// encode as MacData
1491
MacData macData = new MacData(algName, macResult, salt,
1492
MAC_ITERATION_COUNT);
1493
DerOutputStream bytes = new DerOutputStream();
1494
bytes.write(macData.getEncoded());
1495
mData = bytes.toByteArray();
1496
} catch (Exception e) {
1497
throw new IOException("calculateMac failed: " + e, e);
1498
}
1499
return mData;
1500
}
1501
1502
1503
/*
1504
* Validate Certificate Chain
1505
*/
1506
private boolean validateChain(Certificate[] certChain)
1507
{
1508
for (int i = 0; i < certChain.length-1; i++) {
1509
X500Principal issuerDN =
1510
((X509Certificate)certChain[i]).getIssuerX500Principal();
1511
X500Principal subjectDN =
1512
((X509Certificate)certChain[i+1]).getSubjectX500Principal();
1513
if (!(issuerDN.equals(subjectDN)))
1514
return false;
1515
}
1516
1517
// Check for loops in the chain. If there are repeated certs,
1518
// the Set of certs in the chain will contain fewer certs than
1519
// the chain
1520
Set<Certificate> set = new HashSet<>(Arrays.asList(certChain));
1521
return set.size() == certChain.length;
1522
}
1523
1524
1525
/*
1526
* Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage.
1527
*
1528
* Although attributes are optional, they could be required.
1529
* For e.g. localKeyId attribute is required to match the
1530
* private key with the associated end-entity certificate.
1531
* The trustedKeyUsage attribute is used to denote a trusted certificate.
1532
*
1533
* PKCS8ShroudedKeyBags include unique localKeyID and friendlyName.
1534
* CertBags may or may not include attributes depending on the type
1535
* of Certificate. In end-entity certificates, localKeyID should be
1536
* unique, and the corresponding private key should have the same
1537
* localKeyID. For trusted CA certs in the cert-chain, localKeyID
1538
* attribute is not required, hence most vendors don't include it.
1539
* NSS/Netscape require it to be unique or null, where as IE/OpenSSL
1540
* ignore it.
1541
*
1542
* Here is a list of pkcs12 attribute values in CertBags.
1543
*
1544
* PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE
1545
* --------------------------------------------------------------
1546
* LocalKeyId
1547
* (In EE cert only,
1548
* NULL in CA certs) true true true true
1549
*
1550
* friendlyName unique same/ same/ unique
1551
* unique unique/
1552
* null
1553
* trustedKeyUsage - - - true
1554
*
1555
* Note: OpenSSL adds friendlyName for end-entity cert only, and
1556
* removes the localKeyID and friendlyName for CA certs.
1557
* If the CertBag did not have a friendlyName, most vendors will
1558
* add it, and assign it to the DN of the cert.
1559
*/
1560
private byte[] getBagAttributes(String alias, byte[] keyId,
1561
Set<KeyStore.Entry.Attribute> attributes) throws IOException {
1562
return getBagAttributes(alias, keyId, null, attributes);
1563
}
1564
1565
private byte[] getBagAttributes(String alias, byte[] keyId,
1566
ObjectIdentifier[] trustedUsage,
1567
Set<KeyStore.Entry.Attribute> attributes) throws IOException {
1568
1569
byte[] localKeyID = null;
1570
byte[] friendlyName = null;
1571
byte[] trustedKeyUsage = null;
1572
1573
// return null if all three attributes are null
1574
if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) {
1575
return null;
1576
}
1577
1578
// SafeBag Attributes
1579
DerOutputStream bagAttrs = new DerOutputStream();
1580
1581
// Encode the friendlyname oid.
1582
if (alias != null) {
1583
DerOutputStream bagAttr1 = new DerOutputStream();
1584
bagAttr1.putOID(PKCS9FriendlyName_OID);
1585
DerOutputStream bagAttrContent1 = new DerOutputStream();
1586
DerOutputStream bagAttrValue1 = new DerOutputStream();
1587
bagAttrContent1.putBMPString(alias);
1588
bagAttr1.write(DerValue.tag_Set, bagAttrContent1);
1589
bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1);
1590
friendlyName = bagAttrValue1.toByteArray();
1591
}
1592
1593
// Encode the localkeyId oid.
1594
if (keyId != null) {
1595
DerOutputStream bagAttr2 = new DerOutputStream();
1596
bagAttr2.putOID(PKCS9LocalKeyId_OID);
1597
DerOutputStream bagAttrContent2 = new DerOutputStream();
1598
DerOutputStream bagAttrValue2 = new DerOutputStream();
1599
bagAttrContent2.putOctetString(keyId);
1600
bagAttr2.write(DerValue.tag_Set, bagAttrContent2);
1601
bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2);
1602
localKeyID = bagAttrValue2.toByteArray();
1603
}
1604
1605
// Encode the trustedKeyUsage oid.
1606
if (trustedUsage != null) {
1607
DerOutputStream bagAttr3 = new DerOutputStream();
1608
bagAttr3.putOID(TrustedKeyUsage_OID);
1609
DerOutputStream bagAttrContent3 = new DerOutputStream();
1610
DerOutputStream bagAttrValue3 = new DerOutputStream();
1611
for (ObjectIdentifier usage : trustedUsage) {
1612
bagAttrContent3.putOID(usage);
1613
}
1614
bagAttr3.write(DerValue.tag_Set, bagAttrContent3);
1615
bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3);
1616
trustedKeyUsage = bagAttrValue3.toByteArray();
1617
}
1618
1619
DerOutputStream attrs = new DerOutputStream();
1620
if (friendlyName != null) {
1621
attrs.write(friendlyName);
1622
}
1623
if (localKeyID != null) {
1624
attrs.write(localKeyID);
1625
}
1626
if (trustedKeyUsage != null) {
1627
attrs.write(trustedKeyUsage);
1628
}
1629
1630
if (attributes != null) {
1631
for (KeyStore.Entry.Attribute attribute : attributes) {
1632
String attributeName = attribute.getName();
1633
// skip friendlyName, localKeyId and trustedKeyUsage
1634
if (CORE_ATTRIBUTES[0].equals(attributeName) ||
1635
CORE_ATTRIBUTES[1].equals(attributeName) ||
1636
CORE_ATTRIBUTES[2].equals(attributeName)) {
1637
continue;
1638
}
1639
attrs.write(((PKCS12Attribute) attribute).getEncoded());
1640
}
1641
}
1642
1643
bagAttrs.write(DerValue.tag_Set, attrs);
1644
return bagAttrs.toByteArray();
1645
}
1646
1647
/*
1648
* Create EncryptedData content type, that contains EncryptedContentInfo.
1649
* Includes certificates in individual SafeBags of type CertBag.
1650
* Each CertBag may include pkcs12 attributes
1651
* (see comments in getBagAttributes)
1652
*/
1653
private byte[] createEncryptedData(char[] password)
1654
throws CertificateException, IOException
1655
{
1656
DerOutputStream out = new DerOutputStream();
1657
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1658
1659
String alias = e.nextElement();
1660
Entry entry = entries.get(alias);
1661
1662
// certificate chain
1663
Certificate[] certs;
1664
1665
if (entry instanceof PrivateKeyEntry) {
1666
PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
1667
if (keyEntry.chain != null) {
1668
certs = keyEntry.chain;
1669
} else {
1670
certs = new Certificate[0];
1671
}
1672
} else if (entry instanceof CertEntry) {
1673
certs = new Certificate[]{((CertEntry) entry).cert};
1674
} else {
1675
certs = new Certificate[0];
1676
}
1677
1678
for (int i = 0; i < certs.length; i++) {
1679
// create SafeBag of Type CertBag
1680
DerOutputStream safeBag = new DerOutputStream();
1681
safeBag.putOID(CertBag_OID);
1682
1683
// create a CertBag
1684
DerOutputStream certBag = new DerOutputStream();
1685
certBag.putOID(PKCS9CertType_OID);
1686
1687
// write encoded certs in a context-specific tag
1688
DerOutputStream certValue = new DerOutputStream();
1689
X509Certificate cert = (X509Certificate) certs[i];
1690
certValue.putOctetString(cert.getEncoded());
1691
certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1692
true, (byte) 0), certValue);
1693
1694
// wrap CertBag in a Sequence
1695
DerOutputStream certout = new DerOutputStream();
1696
certout.write(DerValue.tag_Sequence, certBag);
1697
byte[] certBagValue = certout.toByteArray();
1698
1699
// Wrap the CertBag encoding in a context-specific tag.
1700
DerOutputStream bagValue = new DerOutputStream();
1701
bagValue.write(certBagValue);
1702
// write SafeBag Value
1703
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1704
true, (byte) 0), bagValue);
1705
1706
// write SafeBag Attributes
1707
// All Certs should have a unique friendlyName.
1708
// This change is made to meet NSS requirements.
1709
byte[] bagAttrs = null;
1710
if (i == 0) {
1711
// Only End-Entity Cert should have a localKeyId.
1712
if (entry instanceof KeyEntry) {
1713
KeyEntry keyEntry = (KeyEntry) entry;
1714
bagAttrs =
1715
getBagAttributes(keyEntry.alias, keyEntry.keyId,
1716
keyEntry.attributes);
1717
} else {
1718
CertEntry certEntry = (CertEntry) entry;
1719
bagAttrs =
1720
getBagAttributes(certEntry.alias, certEntry.keyId,
1721
certEntry.trustedKeyUsage,
1722
certEntry.attributes);
1723
}
1724
} else {
1725
// Trusted root CA certs and Intermediate CA certs do not
1726
// need to have a localKeyId, and hence localKeyId is null
1727
// This change is made to meet NSS/Netscape requirements.
1728
// NSS pkcs12 library requires trusted CA certs in the
1729
// certificate chain to have unique or null localKeyID.
1730
// However, IE/OpenSSL do not impose this restriction.
1731
bagAttrs = getBagAttributes(
1732
cert.getSubjectX500Principal().getName(), null,
1733
entry.attributes);
1734
}
1735
if (bagAttrs != null) {
1736
safeBag.write(bagAttrs);
1737
}
1738
1739
// wrap as Sequence
1740
out.write(DerValue.tag_Sequence, safeBag);
1741
} // for cert-chain
1742
}
1743
1744
// wrap as SequenceOf SafeBag
1745
DerOutputStream safeBagValue = new DerOutputStream();
1746
safeBagValue.write(DerValue.tag_SequenceOf, out);
1747
byte[] safeBagData = safeBagValue.toByteArray();
1748
1749
// encrypt the content (EncryptedContentInfo)
1750
byte[] encrContentInfo = encryptContent(safeBagData, password);
1751
1752
// -- SEQUENCE of EncryptedData
1753
DerOutputStream encrData = new DerOutputStream();
1754
DerOutputStream encrDataContent = new DerOutputStream();
1755
encrData.putInteger(0);
1756
encrData.write(encrContentInfo);
1757
encrDataContent.write(DerValue.tag_Sequence, encrData);
1758
return encrDataContent.toByteArray();
1759
}
1760
1761
/*
1762
* Create SafeContent Data content type.
1763
* Includes encrypted secret key in a SafeBag of type SecretBag.
1764
* Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.
1765
* Each PKCS8ShroudedKeyBag includes pkcs12 attributes
1766
* (see comments in getBagAttributes)
1767
*/
1768
private byte[] createSafeContent()
1769
throws CertificateException, IOException {
1770
1771
DerOutputStream out = new DerOutputStream();
1772
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1773
1774
String alias = e.nextElement();
1775
Entry entry = entries.get(alias);
1776
if (entry == null || (!(entry instanceof KeyEntry))) {
1777
continue;
1778
}
1779
DerOutputStream safeBag = new DerOutputStream();
1780
KeyEntry keyEntry = (KeyEntry) entry;
1781
1782
// DER encode the private key
1783
if (keyEntry instanceof PrivateKeyEntry) {
1784
// Create SafeBag of type pkcs8ShroudedKeyBag
1785
safeBag.putOID(PKCS8ShroudedKeyBag_OID);
1786
1787
// get the encrypted private key
1788
byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey;
1789
EncryptedPrivateKeyInfo encrInfo = null;
1790
try {
1791
encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
1792
1793
} catch (IOException ioe) {
1794
throw new IOException("Private key not stored as "
1795
+ "PKCS#8 EncryptedPrivateKeyInfo"
1796
+ ioe.getMessage());
1797
}
1798
1799
// Wrap the EncryptedPrivateKeyInfo in a context-specific tag.
1800
DerOutputStream bagValue = new DerOutputStream();
1801
bagValue.write(encrInfo.getEncoded());
1802
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1803
true, (byte) 0), bagValue);
1804
1805
// DER encode the secret key
1806
} else if (keyEntry instanceof SecretKeyEntry) {
1807
// Create SafeBag of type SecretBag
1808
safeBag.putOID(SecretBag_OID);
1809
1810
// Create a SecretBag
1811
DerOutputStream secretBag = new DerOutputStream();
1812
secretBag.putOID(PKCS8ShroudedKeyBag_OID);
1813
1814
// Write secret key in a context-specific tag
1815
DerOutputStream secretKeyValue = new DerOutputStream();
1816
secretKeyValue.putOctetString(
1817
((SecretKeyEntry) keyEntry).protectedSecretKey);
1818
secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1819
true, (byte) 0), secretKeyValue);
1820
1821
// Wrap SecretBag in a Sequence
1822
DerOutputStream secretBagSeq = new DerOutputStream();
1823
secretBagSeq.write(DerValue.tag_Sequence, secretBag);
1824
byte[] secretBagValue = secretBagSeq.toByteArray();
1825
1826
// Wrap the secret bag in a context-specific tag.
1827
DerOutputStream bagValue = new DerOutputStream();
1828
bagValue.write(secretBagValue);
1829
1830
// Write SafeBag value
1831
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1832
true, (byte) 0), bagValue);
1833
} else {
1834
continue; // skip this entry
1835
}
1836
1837
// write SafeBag Attributes
1838
byte[] bagAttrs =
1839
getBagAttributes(alias, entry.keyId, entry.attributes);
1840
safeBag.write(bagAttrs);
1841
1842
// wrap as Sequence
1843
out.write(DerValue.tag_Sequence, safeBag);
1844
}
1845
1846
// wrap as Sequence
1847
DerOutputStream safeBagValue = new DerOutputStream();
1848
safeBagValue.write(DerValue.tag_Sequence, out);
1849
return safeBagValue.toByteArray();
1850
}
1851
1852
1853
/*
1854
* Encrypt the contents using Password-based (PBE) encryption
1855
* as defined in PKCS #5.
1856
*
1857
* NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used
1858
* to derive the key and IV.
1859
*
1860
* @return encrypted contents encoded as EncryptedContentInfo
1861
*/
1862
private byte[] encryptContent(byte[] data, char[] password)
1863
throws IOException {
1864
1865
byte[] encryptedData = null;
1866
1867
// create AlgorithmParameters
1868
AlgorithmParameters algParams =
1869
getPBEAlgorithmParameters("PBEWithSHA1AndRC2_40");
1870
DerOutputStream bytes = new DerOutputStream();
1871
AlgorithmId algId =
1872
new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);
1873
algId.encode(bytes);
1874
byte[] encodedAlgId = bytes.toByteArray();
1875
1876
try {
1877
// Use JCE
1878
SecretKey skey = getPBEKey(password);
1879
Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");
1880
cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
1881
encryptedData = cipher.doFinal(data);
1882
1883
if (debug != null) {
1884
debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() +
1885
")");
1886
}
1887
1888
} catch (Exception e) {
1889
throw new IOException("Failed to encrypt" +
1890
" safe contents entry: " + e, e);
1891
}
1892
1893
// create EncryptedContentInfo
1894
DerOutputStream bytes2 = new DerOutputStream();
1895
bytes2.putOID(ContentInfo.DATA_OID);
1896
bytes2.write(encodedAlgId);
1897
1898
// Wrap encrypted data in a context-specific tag.
1899
DerOutputStream tmpout2 = new DerOutputStream();
1900
tmpout2.putOctetString(encryptedData);
1901
bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
1902
false, (byte)0), tmpout2);
1903
1904
// wrap EncryptedContentInfo in a Sequence
1905
DerOutputStream out = new DerOutputStream();
1906
out.write(DerValue.tag_Sequence, bytes2);
1907
return out.toByteArray();
1908
}
1909
1910
/**
1911
* Loads the keystore from the given input stream.
1912
*
1913
* <p>If a password is given, it is used to check the integrity of the
1914
* keystore data. Otherwise, the integrity of the keystore is not checked.
1915
*
1916
* @param stream the input stream from which the keystore is loaded
1917
* @param password the (optional) password used to check the integrity of
1918
* the keystore.
1919
*
1920
* @exception IOException if there is an I/O or format problem with the
1921
* keystore data
1922
* @exception NoSuchAlgorithmException if the algorithm used to check
1923
* the integrity of the keystore cannot be found
1924
* @exception CertificateException if any of the certificates in the
1925
* keystore could not be loaded
1926
*/
1927
public synchronized void engineLoad(InputStream stream, char[] password)
1928
throws IOException, NoSuchAlgorithmException, CertificateException
1929
{
1930
DataInputStream dis;
1931
CertificateFactory cf = null;
1932
ByteArrayInputStream bais = null;
1933
byte[] encoded = null;
1934
1935
if (stream == null)
1936
return;
1937
1938
// reset the counter
1939
counter = 0;
1940
1941
DerValue val = new DerValue(stream);
1942
DerInputStream s = val.toDerInputStream();
1943
int version = s.getInteger();
1944
1945
if (version != VERSION_3) {
1946
throw new IOException("PKCS12 keystore not in version 3 format");
1947
}
1948
1949
entries.clear();
1950
1951
/*
1952
* Read the authSafe.
1953
*/
1954
byte[] authSafeData;
1955
ContentInfo authSafe = new ContentInfo(s);
1956
ObjectIdentifier contentType = authSafe.getContentType();
1957
1958
if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1959
authSafeData = authSafe.getData();
1960
} else /* signed data */ {
1961
throw new IOException("public key protected PKCS12 not supported");
1962
}
1963
1964
DerInputStream as = new DerInputStream(authSafeData);
1965
DerValue[] safeContentsArray = as.getSequence(2);
1966
int count = safeContentsArray.length;
1967
1968
// reset the counters at the start
1969
privateKeyCount = 0;
1970
secretKeyCount = 0;
1971
certificateCount = 0;
1972
1973
/*
1974
* Spin over the ContentInfos.
1975
*/
1976
for (int i = 0; i < count; i++) {
1977
byte[] safeContentsData;
1978
ContentInfo safeContents;
1979
DerInputStream sci;
1980
byte[] eAlgId = null;
1981
1982
sci = new DerInputStream(safeContentsArray[i].toByteArray());
1983
safeContents = new ContentInfo(sci);
1984
contentType = safeContents.getContentType();
1985
safeContentsData = null;
1986
if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1987
1988
if (debug != null) {
1989
debug.println("Loading PKCS#7 data");
1990
}
1991
1992
safeContentsData = safeContents.getData();
1993
} else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) {
1994
if (password == null) {
1995
1996
if (debug != null) {
1997
debug.println("Warning: skipping PKCS#7 encryptedData" +
1998
" - no password was supplied");
1999
}
2000
continue;
2001
}
2002
2003
DerInputStream edi =
2004
safeContents.getContent().toDerInputStream();
2005
int edVersion = edi.getInteger();
2006
DerValue[] seq = edi.getSequence(3);
2007
if (seq.length != 3) {
2008
// We require the encryptedContent field, even though
2009
// it is optional
2010
throw new IOException("Invalid length for EncryptedContentInfo");
2011
}
2012
ObjectIdentifier edContentType = seq[0].getOID();
2013
eAlgId = seq[1].toByteArray();
2014
if (!seq[2].isContextSpecific((byte)0)) {
2015
throw new IOException("unsupported encrypted content type "
2016
+ seq[2].tag);
2017
}
2018
byte newTag = DerValue.tag_OctetString;
2019
if (seq[2].isConstructed())
2020
newTag |= 0x20;
2021
seq[2].resetTag(newTag);
2022
safeContentsData = seq[2].getOctetString();
2023
2024
// parse Algorithm parameters
2025
DerInputStream in = seq[1].toDerInputStream();
2026
ObjectIdentifier algOid = in.getOID();
2027
AlgorithmParameters algParams = parseAlgParameters(algOid, in);
2028
2029
PBEParameterSpec pbeSpec;
2030
int ic = 0;
2031
2032
if (algParams != null) {
2033
try {
2034
pbeSpec =
2035
algParams.getParameterSpec(PBEParameterSpec.class);
2036
} catch (InvalidParameterSpecException ipse) {
2037
throw new IOException(
2038
"Invalid PBE algorithm parameters");
2039
}
2040
ic = pbeSpec.getIterationCount();
2041
2042
if (ic > MAX_ITERATION_COUNT) {
2043
throw new IOException("PBE iteration count too large");
2044
}
2045
}
2046
2047
if (debug != null) {
2048
debug.println("Loading PKCS#7 encryptedData " +
2049
"(" + new AlgorithmId(algOid).getName() +
2050
" iterations: " + ic + ")");
2051
}
2052
2053
while (true) {
2054
try {
2055
// Use JCE
2056
SecretKey skey = getPBEKey(password);
2057
Cipher cipher = Cipher.getInstance(algOid.toString());
2058
cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
2059
safeContentsData = cipher.doFinal(safeContentsData);
2060
break;
2061
} catch (Exception e) {
2062
if (password.length == 0) {
2063
// Retry using an empty password
2064
// without a NULL terminator.
2065
password = new char[1];
2066
continue;
2067
}
2068
throw new IOException("keystore password was incorrect",
2069
new UnrecoverableKeyException(
2070
"failed to decrypt safe contents entry: " + e));
2071
}
2072
}
2073
} else {
2074
throw new IOException("public key protected PKCS12" +
2075
" not supported");
2076
}
2077
DerInputStream sc = new DerInputStream(safeContentsData);
2078
loadSafeContents(sc, password);
2079
}
2080
2081
// The MacData is optional.
2082
if (password != null && s.available() > 0) {
2083
MacData macData = new MacData(s);
2084
int ic = macData.getIterations();
2085
2086
try {
2087
if (ic > MAX_ITERATION_COUNT) {
2088
throw new InvalidAlgorithmParameterException(
2089
"MAC iteration count too large: " + ic);
2090
}
2091
2092
String algName =
2093
macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
2094
2095
// Change SHA-1 to SHA1
2096
algName = algName.replace("-", "");
2097
2098
// generate MAC (MAC key is created within JCE)
2099
Mac m = Mac.getInstance("HmacPBE" + algName);
2100
PBEParameterSpec params =
2101
new PBEParameterSpec(macData.getSalt(), ic);
2102
SecretKey key = getPBEKey(password);
2103
m.init(key, params);
2104
m.update(authSafeData);
2105
byte[] macResult = m.doFinal();
2106
2107
if (debug != null) {
2108
debug.println("Checking keystore integrity " +
2109
"(" + m.getAlgorithm() + " iterations: " + ic + ")");
2110
}
2111
2112
if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
2113
throw new UnrecoverableKeyException("Failed PKCS12" +
2114
" integrity checking");
2115
}
2116
} catch (Exception e) {
2117
throw new IOException("Integrity check failed: " + e, e);
2118
}
2119
}
2120
2121
/*
2122
* Match up private keys with certificate chains.
2123
*/
2124
PrivateKeyEntry[] list =
2125
keyList.toArray(new PrivateKeyEntry[keyList.size()]);
2126
for (int m = 0; m < list.length; m++) {
2127
PrivateKeyEntry entry = list[m];
2128
if (entry.keyId != null) {
2129
ArrayList<X509Certificate> chain =
2130
new ArrayList<X509Certificate>();
2131
X509Certificate cert = findMatchedCertificate(entry);
2132
2133
mainloop:
2134
while (cert != null) {
2135
// Check for loops in the certificate chain
2136
if (!chain.isEmpty()) {
2137
for (X509Certificate chainCert : chain) {
2138
if (cert.equals(chainCert)) {
2139
if (debug != null) {
2140
debug.println("Loop detected in " +
2141
"certificate chain. Skip adding " +
2142
"repeated cert to chain. Subject: " +
2143
cert.getSubjectX500Principal()
2144
.toString());
2145
}
2146
break mainloop;
2147
}
2148
}
2149
}
2150
chain.add(cert);
2151
X500Principal issuerDN = cert.getIssuerX500Principal();
2152
if (issuerDN.equals(cert.getSubjectX500Principal())) {
2153
break;
2154
}
2155
cert = certsMap.get(issuerDN);
2156
}
2157
/* Update existing KeyEntry in entries table */
2158
if (chain.size() > 0)
2159
entry.chain = chain.toArray(new Certificate[chain.size()]);
2160
}
2161
}
2162
2163
if (debug != null) {
2164
debug.println("PKCS12KeyStore load: private key count: " +
2165
privateKeyCount + ". secret key count: " + secretKeyCount +
2166
". certificate count: " + certificateCount);
2167
}
2168
2169
certEntries.clear();
2170
certsMap.clear();
2171
keyList.clear();
2172
}
2173
2174
/**
2175
* Locates a matched CertEntry from certEntries, and returns its cert.
2176
* @param entry the KeyEntry to match
2177
* @return a certificate, null if not found
2178
*/
2179
private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
2180
CertEntry keyIdMatch = null;
2181
CertEntry aliasMatch = null;
2182
for (CertEntry ce: certEntries) {
2183
if (Arrays.equals(entry.keyId, ce.keyId)) {
2184
keyIdMatch = ce;
2185
if (entry.alias.equalsIgnoreCase(ce.alias)) {
2186
// Full match!
2187
return ce.cert;
2188
}
2189
} else if (entry.alias.equalsIgnoreCase(ce.alias)) {
2190
aliasMatch = ce;
2191
}
2192
}
2193
// keyId match first, for compatibility
2194
if (keyIdMatch != null) return keyIdMatch.cert;
2195
else if (aliasMatch != null) return aliasMatch.cert;
2196
else return null;
2197
}
2198
2199
private void loadSafeContents(DerInputStream stream, char[] password)
2200
throws IOException, NoSuchAlgorithmException, CertificateException
2201
{
2202
DerValue[] safeBags = stream.getSequence(2);
2203
int count = safeBags.length;
2204
2205
/*
2206
* Spin over the SafeBags.
2207
*/
2208
for (int i = 0; i < count; i++) {
2209
ObjectIdentifier bagId;
2210
DerInputStream sbi;
2211
DerValue bagValue;
2212
Object bagItem = null;
2213
2214
sbi = safeBags[i].toDerInputStream();
2215
bagId = sbi.getOID();
2216
bagValue = sbi.getDerValue();
2217
if (!bagValue.isContextSpecific((byte)0)) {
2218
throw new IOException("unsupported PKCS12 bag value type "
2219
+ bagValue.tag);
2220
}
2221
bagValue = bagValue.data.getDerValue();
2222
if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) {
2223
PrivateKeyEntry kEntry = new PrivateKeyEntry();
2224
kEntry.protectedPrivKey = bagValue.toByteArray();
2225
bagItem = kEntry;
2226
privateKeyCount++;
2227
} else if (bagId.equals((Object)CertBag_OID)) {
2228
DerInputStream cs = new DerInputStream(bagValue.toByteArray());
2229
DerValue[] certValues = cs.getSequence(2);
2230
if (certValues.length != 2) {
2231
throw new IOException("Invalid length for CertBag");
2232
}
2233
ObjectIdentifier certId = certValues[0].getOID();
2234
if (!certValues[1].isContextSpecific((byte)0)) {
2235
throw new IOException("unsupported PKCS12 cert value type "
2236
+ certValues[1].tag);
2237
}
2238
DerValue certValue = certValues[1].data.getDerValue();
2239
CertificateFactory cf = CertificateFactory.getInstance("X509");
2240
X509Certificate cert;
2241
cert = (X509Certificate)cf.generateCertificate
2242
(new ByteArrayInputStream(certValue.getOctetString()));
2243
bagItem = cert;
2244
certificateCount++;
2245
} else if (bagId.equals((Object)SecretBag_OID)) {
2246
DerInputStream ss = new DerInputStream(bagValue.toByteArray());
2247
DerValue[] secretValues = ss.getSequence(2);
2248
if (secretValues.length != 2) {
2249
throw new IOException("Invalid length for SecretBag");
2250
}
2251
ObjectIdentifier secretId = secretValues[0].getOID();
2252
if (!secretValues[1].isContextSpecific((byte)0)) {
2253
throw new IOException(
2254
"unsupported PKCS12 secret value type "
2255
+ secretValues[1].tag);
2256
}
2257
DerValue secretValue = secretValues[1].data.getDerValue();
2258
SecretKeyEntry kEntry = new SecretKeyEntry();
2259
kEntry.protectedSecretKey = secretValue.getOctetString();
2260
bagItem = kEntry;
2261
secretKeyCount++;
2262
} else {
2263
2264
if (debug != null) {
2265
debug.println("Unsupported PKCS12 bag type: " + bagId);
2266
}
2267
}
2268
2269
DerValue[] attrSet;
2270
try {
2271
attrSet = sbi.getSet(3);
2272
} catch (IOException e) {
2273
// entry does not have attributes
2274
// Note: CA certs can have no attributes
2275
// OpenSSL generates pkcs12 with no attr for CA certs.
2276
attrSet = null;
2277
}
2278
2279
String alias = null;
2280
byte[] keyId = null;
2281
ObjectIdentifier[] trustedKeyUsage = null;
2282
Set<PKCS12Attribute> attributes = new HashSet<>();
2283
2284
if (attrSet != null) {
2285
for (int j = 0; j < attrSet.length; j++) {
2286
byte[] encoded = attrSet[j].toByteArray();
2287
DerInputStream as = new DerInputStream(encoded);
2288
DerValue[] attrSeq = as.getSequence(2);
2289
if (attrSeq.length != 2) {
2290
throw new IOException("Invalid length for Attribute");
2291
}
2292
ObjectIdentifier attrId = attrSeq[0].getOID();
2293
DerInputStream vs =
2294
new DerInputStream(attrSeq[1].toByteArray());
2295
DerValue[] valSet;
2296
try {
2297
valSet = vs.getSet(1);
2298
} catch (IOException e) {
2299
throw new IOException("Attribute " + attrId +
2300
" should have a value " + e.getMessage());
2301
}
2302
if (attrId.equals((Object)PKCS9FriendlyName_OID)) {
2303
alias = valSet[0].getBMPString();
2304
} else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) {
2305
keyId = valSet[0].getOctetString();
2306
} else if
2307
(attrId.equals((Object)TrustedKeyUsage_OID)) {
2308
trustedKeyUsage = new ObjectIdentifier[valSet.length];
2309
for (int k = 0; k < valSet.length; k++) {
2310
trustedKeyUsage[k] = valSet[k].getOID();
2311
}
2312
} else {
2313
attributes.add(new PKCS12Attribute(encoded));
2314
}
2315
}
2316
}
2317
2318
/*
2319
* As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId)
2320
* are optional PKCS12 bagAttributes. But entries in the keyStore
2321
* are identified by their alias. Hence we need to have an
2322
* Unfriendlyname in the alias, if alias is null. The keyId
2323
* attribute is required to match the private key with the
2324
* certificate. If we get a bagItem of type KeyEntry with a
2325
* null keyId, we should skip it entirely.
2326
*/
2327
if (bagItem instanceof KeyEntry) {
2328
KeyEntry entry = (KeyEntry)bagItem;
2329
2330
if (bagItem instanceof PrivateKeyEntry) {
2331
if (keyId == null) {
2332
// Insert a localKeyID for the privateKey
2333
// Note: This is a workaround to allow null localKeyID
2334
// attribute in pkcs12 with one private key entry and
2335
// associated cert-chain
2336
if (privateKeyCount == 1) {
2337
keyId = "01".getBytes("UTF8");
2338
} else {
2339
continue;
2340
}
2341
}
2342
}
2343
entry.keyId = keyId;
2344
// restore date if it exists
2345
String keyIdStr = new String(keyId, "UTF8");
2346
Date date = null;
2347
if (keyIdStr.startsWith("Time ")) {
2348
try {
2349
date = new Date(
2350
Long.parseLong(keyIdStr.substring(5)));
2351
} catch (Exception e) {
2352
date = null;
2353
}
2354
}
2355
if (date == null) {
2356
date = new Date();
2357
}
2358
entry.date = date;
2359
2360
if (bagItem instanceof PrivateKeyEntry) {
2361
keyList.add((PrivateKeyEntry) entry);
2362
}
2363
if (entry.attributes == null) {
2364
entry.attributes = new HashSet<>();
2365
}
2366
entry.attributes.addAll(attributes);
2367
if (alias == null) {
2368
alias = getUnfriendlyName();
2369
}
2370
entry.alias = alias;
2371
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
2372
2373
} else if (bagItem instanceof X509Certificate) {
2374
X509Certificate cert = (X509Certificate)bagItem;
2375
// Insert a localKeyID for the corresponding cert
2376
// Note: This is a workaround to allow null localKeyID
2377
// attribute in pkcs12 with one private key entry and
2378
// associated cert-chain
2379
if ((keyId == null) && (privateKeyCount == 1)) {
2380
// insert localKeyID only for EE cert or self-signed cert
2381
if (i == 0) {
2382
keyId = "01".getBytes("UTF8");
2383
}
2384
}
2385
// Trusted certificate
2386
if (trustedKeyUsage != null) {
2387
if (alias == null) {
2388
alias = getUnfriendlyName();
2389
}
2390
CertEntry certEntry =
2391
new CertEntry(cert, keyId, alias, trustedKeyUsage,
2392
attributes);
2393
entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
2394
} else {
2395
certEntries.add(new CertEntry(cert, keyId, alias));
2396
}
2397
X500Principal subjectDN = cert.getSubjectX500Principal();
2398
if (subjectDN != null) {
2399
if (!certsMap.containsKey(subjectDN)) {
2400
certsMap.put(subjectDN, cert);
2401
}
2402
}
2403
}
2404
}
2405
}
2406
2407
private String getUnfriendlyName() {
2408
counter++;
2409
return (String.valueOf(counter));
2410
}
2411
}
2412
2413