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