Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/classes/apple/security/KeychainStore.java
38829 views
1
/*
2
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package apple.security;
27
28
import java.io.*;
29
import java.security.*;
30
import java.security.cert.*;
31
import java.security.cert.Certificate;
32
import java.security.spec.*;
33
import java.util.*;
34
35
import javax.crypto.*;
36
import javax.crypto.spec.*;
37
import javax.security.auth.x500.*;
38
39
import sun.security.pkcs.*;
40
import sun.security.pkcs.EncryptedPrivateKeyInfo;
41
import sun.security.util.*;
42
import sun.security.x509.*;
43
44
/**
45
* This class provides the keystore implementation referred to as "KeychainStore".
46
* It uses the current user's keychain as its backing storage, and does NOT support
47
* a file-based implementation.
48
*/
49
50
public final class KeychainStore extends KeyStoreSpi {
51
52
// Private keys and their supporting certificate chains
53
// If a key came from the keychain it has a SecKeyRef and one or more
54
// SecCertificateRef. When we delete the key we have to delete all of the corresponding
55
// native objects.
56
class KeyEntry {
57
Date date; // the creation date of this entry
58
byte[] protectedPrivKey;
59
char[] password;
60
long keyRef; // SecKeyRef for this key
61
Certificate chain[];
62
long chainRefs[]; // SecCertificateRefs for this key's chain.
63
};
64
65
// Trusted certificates
66
class TrustedCertEntry {
67
Date date; // the creation date of this entry
68
69
Certificate cert;
70
long certRef; // SecCertificateRef for this key
71
};
72
73
/**
74
* Entries that have been deleted. When something calls engineStore we'll
75
* remove them from the keychain.
76
*/
77
private Hashtable deletedEntries = new Hashtable();
78
79
/**
80
* Entries that have been added. When something calls engineStore we'll
81
* add them to the keychain.
82
*/
83
private Hashtable addedEntries = new Hashtable();
84
85
/**
86
* Private keys and certificates are stored in a hashtable.
87
* Hash entries are keyed by alias names.
88
*/
89
private Hashtable entries = new Hashtable();
90
91
/**
92
* Algorithm identifiers and corresponding OIDs for the contents of the PKCS12 bag we get from the Keychain.
93
*/
94
private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2};
95
private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = {1, 2, 840, 113549, 1, 12, 1, 3};
96
private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;
97
private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;
98
99
/**
100
* Constnats used in PBE decryption.
101
*/
102
private static final int iterationCount = 1024;
103
private static final int SALT_LEN = 20;
104
105
private static final Debug debug = Debug.getInstance("keystore");
106
107
static {
108
AccessController.doPrivileged(
109
new PrivilegedAction<Void>() {
110
public Void run() {
111
System.loadLibrary("osx");
112
return null;
113
}
114
});
115
try {
116
PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
117
pbeWithSHAAnd3KeyTripleDESCBC_OID = new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);
118
} catch (IOException ioe) {
119
// should not happen
120
}
121
}
122
123
private static void permissionCheck() {
124
SecurityManager sec = System.getSecurityManager();
125
126
if (sec != null) {
127
sec.checkPermission(new RuntimePermission("useKeychainStore"));
128
}
129
}
130
131
132
/**
133
* Verify the Apple provider in the constructor.
134
*
135
* @exception SecurityException if fails to verify
136
* its own integrity
137
*/
138
public KeychainStore() { }
139
140
/**
141
* Returns the key associated with the given alias, using the given
142
* password to recover it.
143
*
144
* @param alias the alias name
145
* @param password the password for recovering the key. This password is
146
* used internally as the key is exported in a PKCS12 format.
147
*
148
* @return the requested key, or null if the given alias does not exist
149
* or does not identify a <i>key entry</i>.
150
*
151
* @exception NoSuchAlgorithmException if the algorithm for recovering the
152
* key cannot be found
153
* @exception UnrecoverableKeyException if the key cannot be recovered
154
* (e.g., the given password is wrong).
155
*/
156
public Key engineGetKey(String alias, char[] password)
157
throws NoSuchAlgorithmException, UnrecoverableKeyException
158
{
159
permissionCheck();
160
161
// An empty password is rejected by MacOS API, no private key data
162
// is exported. If no password is passed (as is the case when
163
// this implementation is used as browser keystore in various
164
// deployment scenarios like Webstart, JFX and applets), create
165
// a dummy password so MacOS API is happy.
166
if (password == null || password.length == 0) {
167
// Must not be a char array with only a 0, as this is an empty
168
// string.
169
if (random == null) {
170
random = new SecureRandom();
171
}
172
password = Long.toString(random.nextLong()).toCharArray();
173
}
174
175
Object entry = entries.get(alias.toLowerCase());
176
177
if (entry == null || !(entry instanceof KeyEntry)) {
178
return null;
179
}
180
181
// This call gives us a PKCS12 bag, with the key inside it.
182
byte[] exportedKeyInfo = _getEncodedKeyData(((KeyEntry)entry).keyRef, password);
183
if (exportedKeyInfo == null) {
184
return null;
185
}
186
187
PrivateKey returnValue = null;
188
189
try {
190
byte[] pkcs8KeyData = fetchPrivateKeyFromBag(exportedKeyInfo);
191
byte[] encryptedKey;
192
AlgorithmParameters algParams;
193
ObjectIdentifier algOid;
194
try {
195
// get the encrypted private key
196
EncryptedPrivateKeyInfo encrInfo = new EncryptedPrivateKeyInfo(pkcs8KeyData);
197
encryptedKey = encrInfo.getEncryptedData();
198
199
// parse Algorithm parameters
200
DerValue val = new DerValue(encrInfo.getAlgorithm().encode());
201
DerInputStream in = val.toDerInputStream();
202
algOid = in.getOID();
203
algParams = parseAlgParameters(in);
204
205
} catch (IOException ioe) {
206
UnrecoverableKeyException uke =
207
new UnrecoverableKeyException("Private key not stored as "
208
+ "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
209
uke.initCause(ioe);
210
throw uke;
211
}
212
213
// Use JCE to decrypt the data using the supplied password.
214
SecretKey skey = getPBEKey(password);
215
Cipher cipher = Cipher.getInstance(algOid.toString());
216
cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
217
byte[] decryptedPrivateKey = cipher.doFinal(encryptedKey);
218
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(decryptedPrivateKey);
219
220
// Parse the key algorithm and then use a JCA key factory to create the private key.
221
DerValue val = new DerValue(decryptedPrivateKey);
222
DerInputStream in = val.toDerInputStream();
223
224
// Ignore this -- version should be 0.
225
int i = in.getInteger();
226
227
// Get the Algorithm ID next
228
DerValue[] value = in.getSequence(2);
229
if (value.length < 1 || value.length > 2) {
230
throw new IOException("Invalid length for AlgorithmIdentifier");
231
}
232
AlgorithmId algId = new AlgorithmId(value[0].getOID());
233
String algName = algId.getName();
234
235
// Get a key factory for this algorithm. It's likely to be 'RSA'.
236
KeyFactory kfac = KeyFactory.getInstance(algName);
237
returnValue = kfac.generatePrivate(kspec);
238
} catch (Exception e) {
239
UnrecoverableKeyException uke =
240
new UnrecoverableKeyException("Get Key failed: " +
241
e.getMessage());
242
uke.initCause(e);
243
throw uke;
244
}
245
246
return returnValue;
247
}
248
249
private native byte[] _getEncodedKeyData(long secKeyRef, char[] password);
250
251
/**
252
* Returns the certificate chain associated with the given alias.
253
*
254
* @param alias the alias name
255
*
256
* @return the certificate chain (ordered with the user's certificate first
257
* and the root certificate authority last), or null if the given alias
258
* does not exist or does not contain a certificate chain (i.e., the given
259
* alias identifies either a <i>trusted certificate entry</i> or a
260
* <i>key entry</i> without a certificate chain).
261
*/
262
public Certificate[] engineGetCertificateChain(String alias) {
263
permissionCheck();
264
265
Object entry = entries.get(alias.toLowerCase());
266
267
if (entry != null && entry instanceof KeyEntry) {
268
if (((KeyEntry)entry).chain == null) {
269
return null;
270
} else {
271
return (Certificate[])((KeyEntry)entry).chain.clone();
272
}
273
} else {
274
return null;
275
}
276
}
277
278
/**
279
* Returns the certificate associated with the given alias.
280
*
281
* <p>If the given alias name identifies a
282
* <i>trusted certificate entry</i>, the certificate associated with that
283
* entry is returned. If the given alias name identifies a
284
* <i>key entry</i>, the first element of the certificate chain of that
285
* entry is returned, or null if that entry does not have a certificate
286
* chain.
287
*
288
* @param alias the alias name
289
*
290
* @return the certificate, or null if the given alias does not exist or
291
* does not contain a certificate.
292
*/
293
public Certificate engineGetCertificate(String alias) {
294
permissionCheck();
295
296
Object entry = entries.get(alias.toLowerCase());
297
298
if (entry != null) {
299
if (entry instanceof TrustedCertEntry) {
300
return ((TrustedCertEntry)entry).cert;
301
} else {
302
if (((KeyEntry)entry).chain == null) {
303
return null;
304
} else {
305
return ((KeyEntry)entry).chain[0];
306
}
307
}
308
} else {
309
return null;
310
}
311
}
312
313
/**
314
* Returns the creation date of the entry identified by the given alias.
315
*
316
* @param alias the alias name
317
*
318
* @return the creation date of this entry, or null if the given alias does
319
* not exist
320
*/
321
public Date engineGetCreationDate(String alias) {
322
permissionCheck();
323
324
Object entry = entries.get(alias.toLowerCase());
325
326
if (entry != null) {
327
if (entry instanceof TrustedCertEntry) {
328
return new Date(((TrustedCertEntry)entry).date.getTime());
329
} else {
330
return new Date(((KeyEntry)entry).date.getTime());
331
}
332
} else {
333
return null;
334
}
335
}
336
337
/**
338
* Assigns the given key to the given alias, protecting it with the given
339
* password.
340
*
341
* <p>If the given key is of type <code>java.security.PrivateKey</code>,
342
* it must be accompanied by a certificate chain certifying the
343
* corresponding public key.
344
*
345
* <p>If the given alias already exists, the keystore information
346
* associated with it is overridden by the given key (and possibly
347
* certificate chain).
348
*
349
* @param alias the alias name
350
* @param key the key to be associated with the alias
351
* @param password the password to protect the key
352
* @param chain the certificate chain for the corresponding public
353
* key (only required if the given key is of type
354
* <code>java.security.PrivateKey</code>).
355
*
356
* @exception KeyStoreException if the given key cannot be protected, or
357
* this operation fails for some other reason
358
*/
359
public void engineSetKeyEntry(String alias, Key key, char[] password,
360
Certificate[] chain)
361
throws KeyStoreException
362
{
363
permissionCheck();
364
365
synchronized(entries) {
366
try {
367
KeyEntry entry = new KeyEntry();
368
entry.date = new Date();
369
370
if (key instanceof PrivateKey) {
371
if ((key.getFormat().equals("PKCS#8")) ||
372
(key.getFormat().equals("PKCS8"))) {
373
entry.protectedPrivKey = encryptPrivateKey(key.getEncoded(), password);
374
entry.password = password.clone();
375
} else {
376
throw new KeyStoreException("Private key is not encoded as PKCS#8");
377
}
378
} else {
379
throw new KeyStoreException("Key is not a PrivateKey");
380
}
381
382
// clone the chain
383
if (chain != null) {
384
if ((chain.length > 1) && !validateChain(chain)) {
385
throw new KeyStoreException("Certificate chain does not validate");
386
}
387
388
entry.chain = (Certificate[])chain.clone();
389
entry.chainRefs = new long[entry.chain.length];
390
}
391
392
String lowerAlias = alias.toLowerCase();
393
if (entries.get(lowerAlias) != null) {
394
deletedEntries.put(lowerAlias, entries.get(lowerAlias));
395
}
396
397
entries.put(lowerAlias, entry);
398
addedEntries.put(lowerAlias, entry);
399
} catch (Exception nsae) {
400
KeyStoreException ke = new KeyStoreException("Key protection algorithm not found: " + nsae);
401
ke.initCause(nsae);
402
throw ke;
403
}
404
}
405
}
406
407
/**
408
* Assigns the given key (that has already been protected) to the given
409
* alias.
410
*
411
* <p>If the protected key is of type
412
* <code>java.security.PrivateKey</code>, it must be accompanied by a
413
* certificate chain certifying the corresponding public key. If the
414
* underlying keystore implementation is of type <code>jks</code>,
415
* <code>key</code> must be encoded as an
416
* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
417
*
418
* <p>If the given alias already exists, the keystore information
419
* associated with it is overridden by the given key (and possibly
420
* certificate chain).
421
*
422
* @param alias the alias name
423
* @param key the key (in protected format) to be associated with the alias
424
* @param chain the certificate chain for the corresponding public
425
* key (only useful if the protected key is of type
426
* <code>java.security.PrivateKey</code>).
427
*
428
* @exception KeyStoreException if this operation fails.
429
*/
430
public void engineSetKeyEntry(String alias, byte[] key,
431
Certificate[] chain)
432
throws KeyStoreException
433
{
434
permissionCheck();
435
436
synchronized(entries) {
437
// key must be encoded as EncryptedPrivateKeyInfo as defined in
438
// PKCS#8
439
KeyEntry entry = new KeyEntry();
440
try {
441
EncryptedPrivateKeyInfo privateKey = new EncryptedPrivateKeyInfo(key);
442
entry.protectedPrivKey = privateKey.getEncoded();
443
} catch (IOException ioe) {
444
throw new KeyStoreException("key is not encoded as "
445
+ "EncryptedPrivateKeyInfo");
446
}
447
448
entry.date = new Date();
449
450
if ((chain != null) &&
451
(chain.length != 0)) {
452
entry.chain = (Certificate[])chain.clone();
453
entry.chainRefs = new long[entry.chain.length];
454
}
455
456
String lowerAlias = alias.toLowerCase();
457
if (entries.get(lowerAlias) != null) {
458
deletedEntries.put(lowerAlias, entries.get(alias));
459
}
460
entries.put(lowerAlias, entry);
461
addedEntries.put(lowerAlias, entry);
462
}
463
}
464
465
/**
466
* Assigns the given certificate to the given alias.
467
*
468
* <p>If the given alias already exists in this keystore and identifies a
469
* <i>trusted certificate entry</i>, the certificate associated with it is
470
* overridden by the given certificate.
471
*
472
* @param alias the alias name
473
* @param cert the certificate
474
*
475
* @exception KeyStoreException if the given alias already exists and does
476
* not identify a <i>trusted certificate entry</i>, or this operation
477
* fails for some other reason.
478
*/
479
public void engineSetCertificateEntry(String alias, Certificate cert)
480
throws KeyStoreException
481
{
482
permissionCheck();
483
484
synchronized(entries) {
485
486
Object entry = entries.get(alias.toLowerCase());
487
if ((entry != null) && (entry instanceof KeyEntry)) {
488
throw new KeyStoreException
489
("Cannot overwrite key entry with certificate");
490
}
491
492
// This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry.
493
// Security framework doesn't support the same certificate twice in a keychain.
494
Collection allValues = entries.values();
495
496
for (Object value : allValues) {
497
if (value instanceof TrustedCertEntry) {
498
TrustedCertEntry tce = (TrustedCertEntry)value;
499
if (tce.cert.equals(cert)) {
500
throw new KeyStoreException("Keychain does not support mulitple copies of same certificate.");
501
}
502
}
503
}
504
505
TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
506
trustedCertEntry.cert = cert;
507
trustedCertEntry.date = new Date();
508
String lowerAlias = alias.toLowerCase();
509
if (entries.get(lowerAlias) != null) {
510
deletedEntries.put(lowerAlias, entries.get(lowerAlias));
511
}
512
entries.put(lowerAlias, trustedCertEntry);
513
addedEntries.put(lowerAlias, trustedCertEntry);
514
}
515
}
516
517
/**
518
* Deletes the entry identified by the given alias from this keystore.
519
*
520
* @param alias the alias name
521
*
522
* @exception KeyStoreException if the entry cannot be removed.
523
*/
524
public void engineDeleteEntry(String alias)
525
throws KeyStoreException
526
{
527
permissionCheck();
528
529
synchronized(entries) {
530
Object entry = entries.remove(alias.toLowerCase());
531
deletedEntries.put(alias.toLowerCase(), entry);
532
}
533
}
534
535
/**
536
* Lists all the alias names of this keystore.
537
*
538
* @return enumeration of the alias names
539
*/
540
public Enumeration engineAliases() {
541
permissionCheck();
542
return entries.keys();
543
}
544
545
/**
546
* Checks if the given alias exists in this keystore.
547
*
548
* @param alias the alias name
549
*
550
* @return true if the alias exists, false otherwise
551
*/
552
public boolean engineContainsAlias(String alias) {
553
permissionCheck();
554
return entries.containsKey(alias.toLowerCase());
555
}
556
557
/**
558
* Retrieves the number of entries in this keystore.
559
*
560
* @return the number of entries in this keystore
561
*/
562
public int engineSize() {
563
permissionCheck();
564
return entries.size();
565
}
566
567
/**
568
* Returns true if the entry identified by the given alias is a
569
* <i>key entry</i>, and false otherwise.
570
*
571
* @return true if the entry identified by the given alias is a
572
* <i>key entry</i>, false otherwise.
573
*/
574
public boolean engineIsKeyEntry(String alias) {
575
permissionCheck();
576
Object entry = entries.get(alias.toLowerCase());
577
if ((entry != null) && (entry instanceof KeyEntry)) {
578
return true;
579
} else {
580
return false;
581
}
582
}
583
584
/**
585
* Returns true if the entry identified by the given alias is a
586
* <i>trusted certificate entry</i>, and false otherwise.
587
*
588
* @return true if the entry identified by the given alias is a
589
* <i>trusted certificate entry</i>, false otherwise.
590
*/
591
public boolean engineIsCertificateEntry(String alias) {
592
permissionCheck();
593
Object entry = entries.get(alias.toLowerCase());
594
if ((entry != null) && (entry instanceof TrustedCertEntry)) {
595
return true;
596
} else {
597
return false;
598
}
599
}
600
601
/**
602
* Returns the (alias) name of the first keystore entry whose certificate
603
* matches the given certificate.
604
*
605
* <p>This method attempts to match the given certificate with each
606
* keystore entry. If the entry being considered
607
* is a <i>trusted certificate entry</i>, the given certificate is
608
* compared to that entry's certificate. If the entry being considered is
609
* a <i>key entry</i>, the given certificate is compared to the first
610
* element of that entry's certificate chain (if a chain exists).
611
*
612
* @param cert the certificate to match with.
613
*
614
* @return the (alias) name of the first entry with matching certificate,
615
* or null if no such entry exists in this keystore.
616
*/
617
public String engineGetCertificateAlias(Certificate cert) {
618
permissionCheck();
619
Certificate certElem;
620
621
for (Enumeration e = entries.keys(); e.hasMoreElements(); ) {
622
String alias = (String)e.nextElement();
623
Object entry = entries.get(alias);
624
if (entry instanceof TrustedCertEntry) {
625
certElem = ((TrustedCertEntry)entry).cert;
626
} else if (((KeyEntry)entry).chain != null) {
627
certElem = ((KeyEntry)entry).chain[0];
628
} else {
629
continue;
630
}
631
if (certElem.equals(cert)) {
632
return alias;
633
}
634
}
635
return null;
636
}
637
638
/**
639
* Stores this keystore to the given output stream, and protects its
640
* integrity with the given password.
641
*
642
* @param stream Ignored. the output stream to which this keystore is written.
643
* @param password the password to generate the keystore integrity check
644
*
645
* @exception IOException if there was an I/O problem with data
646
* @exception NoSuchAlgorithmException if the appropriate data integrity
647
* algorithm could not be found
648
* @exception CertificateException if any of the certificates included in
649
* the keystore data could not be stored
650
*/
651
public void engineStore(OutputStream stream, char[] password)
652
throws IOException, NoSuchAlgorithmException, CertificateException
653
{
654
permissionCheck();
655
656
// Delete items that do have a keychain item ref.
657
for (Enumeration e = deletedEntries.keys(); e.hasMoreElements(); ) {
658
String alias = (String)e.nextElement();
659
Object entry = deletedEntries.get(alias);
660
if (entry instanceof TrustedCertEntry) {
661
if (((TrustedCertEntry)entry).certRef != 0) {
662
_removeItemFromKeychain(((TrustedCertEntry)entry).certRef);
663
_releaseKeychainItemRef(((TrustedCertEntry)entry).certRef);
664
}
665
} else {
666
Certificate certElem;
667
KeyEntry keyEntry = (KeyEntry)entry;
668
669
if (keyEntry.chain != null) {
670
for (int i = 0; i < keyEntry.chain.length; i++) {
671
if (keyEntry.chainRefs[i] != 0) {
672
_removeItemFromKeychain(keyEntry.chainRefs[i]);
673
_releaseKeychainItemRef(keyEntry.chainRefs[i]);
674
}
675
}
676
677
if (keyEntry.keyRef != 0) {
678
_removeItemFromKeychain(keyEntry.keyRef);
679
_releaseKeychainItemRef(keyEntry.keyRef);
680
}
681
}
682
}
683
}
684
685
// Add all of the certs or keys in the added entries.
686
// No need to check for 0 refs, as they are in the added list.
687
for (Enumeration e = addedEntries.keys(); e.hasMoreElements(); ) {
688
String alias = (String)e.nextElement();
689
Object entry = addedEntries.get(alias);
690
if (entry instanceof TrustedCertEntry) {
691
TrustedCertEntry tce = (TrustedCertEntry)entry;
692
Certificate certElem;
693
certElem = tce.cert;
694
tce.certRef = addCertificateToKeychain(alias, certElem);
695
} else {
696
KeyEntry keyEntry = (KeyEntry)entry;
697
698
if (keyEntry.chain != null) {
699
for (int i = 0; i < keyEntry.chain.length; i++) {
700
keyEntry.chainRefs[i] = addCertificateToKeychain(alias, keyEntry.chain[i]);
701
}
702
703
keyEntry.keyRef = _addItemToKeychain(alias, false, keyEntry.protectedPrivKey, keyEntry.password);
704
}
705
}
706
}
707
708
// Clear the added and deletedEntries hashtables here, now that we're done with the updates.
709
// For the deleted entries, we freed up the native references above.
710
deletedEntries.clear();
711
addedEntries.clear();
712
}
713
714
private long addCertificateToKeychain(String alias, Certificate cert) {
715
byte[] certblob = null;
716
long returnValue = 0;
717
718
try {
719
certblob = cert.getEncoded();
720
returnValue = _addItemToKeychain(alias, true, certblob, null);
721
} catch (Exception e) {
722
e.printStackTrace();
723
}
724
725
return returnValue;
726
}
727
728
private native long _addItemToKeychain(String alias, boolean isCertificate, byte[] datablob, char[] password);
729
private native int _removeItemFromKeychain(long certRef);
730
private native void _releaseKeychainItemRef(long keychainItemRef);
731
732
/**
733
* Loads the keystore from the Keychain.
734
*
735
* @param stream Ignored - here for API compatibility.
736
* @param password Ignored - if user needs to unlock keychain Security
737
* framework will post any dialogs.
738
*
739
* @exception IOException if there is an I/O or format problem with the
740
* keystore data
741
* @exception NoSuchAlgorithmException if the algorithm used to check
742
* the integrity of the keystore cannot be found
743
* @exception CertificateException if any of the certificates in the
744
* keystore could not be loaded
745
*/
746
public void engineLoad(InputStream stream, char[] password)
747
throws IOException, NoSuchAlgorithmException, CertificateException
748
{
749
permissionCheck();
750
751
// Release any stray keychain references before clearing out the entries.
752
synchronized(entries) {
753
for (Enumeration e = entries.keys(); e.hasMoreElements(); ) {
754
String alias = (String)e.nextElement();
755
Object entry = entries.get(alias);
756
if (entry instanceof TrustedCertEntry) {
757
if (((TrustedCertEntry)entry).certRef != 0) {
758
_releaseKeychainItemRef(((TrustedCertEntry)entry).certRef);
759
}
760
} else {
761
KeyEntry keyEntry = (KeyEntry)entry;
762
763
if (keyEntry.chain != null) {
764
for (int i = 0; i < keyEntry.chain.length; i++) {
765
if (keyEntry.chainRefs[i] != 0) {
766
_releaseKeychainItemRef(keyEntry.chainRefs[i]);
767
}
768
}
769
770
if (keyEntry.keyRef != 0) {
771
_releaseKeychainItemRef(keyEntry.keyRef);
772
}
773
}
774
}
775
}
776
777
entries.clear();
778
_scanKeychain();
779
if (debug != null) {
780
debug.println("KeychainStore load entry count: " +
781
entries.size());
782
}
783
}
784
}
785
786
private native void _scanKeychain();
787
788
/**
789
* Callback method from _scanKeychain. If a trusted certificate is found, this method will be called.
790
*/
791
private void createTrustedCertEntry(String alias, long keychainItemRef, long creationDate, byte[] derStream) {
792
TrustedCertEntry tce = new TrustedCertEntry();
793
794
try {
795
CertificateFactory cf = CertificateFactory.getInstance("X.509");
796
InputStream input = new ByteArrayInputStream(derStream);
797
X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
798
input.close();
799
tce.cert = cert;
800
tce.certRef = keychainItemRef;
801
802
// Make a creation date.
803
if (creationDate != 0)
804
tce.date = new Date(creationDate);
805
else
806
tce.date = new Date();
807
808
int uniqueVal = 1;
809
String originalAlias = alias;
810
811
while (entries.containsKey(alias.toLowerCase())) {
812
alias = originalAlias + " " + uniqueVal;
813
uniqueVal++;
814
}
815
816
entries.put(alias.toLowerCase(), tce);
817
} catch (Exception e) {
818
// The certificate will be skipped.
819
System.err.println("KeychainStore Ignored Exception: " + e);
820
}
821
}
822
823
/**
824
* Callback method from _scanKeychain. If an identity is found, this method will be called to create Java certificate
825
* and private key objects from the keychain data.
826
*/
827
private void createKeyEntry(String alias, long creationDate, long secKeyRef, long[] secCertificateRefs, byte[][] rawCertData)
828
throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
829
KeyEntry ke = new KeyEntry();
830
831
// First, store off the private key information. This is the easy part.
832
ke.protectedPrivKey = null;
833
ke.keyRef = secKeyRef;
834
835
// Make a creation date.
836
if (creationDate != 0)
837
ke.date = new Date(creationDate);
838
else
839
ke.date = new Date();
840
841
// Next, create X.509 Certificate objects from the raw data. This is complicated
842
// because a certificate's public key may be too long for Java's default encryption strength.
843
List createdCerts = new ArrayList();
844
845
try {
846
CertificateFactory cf = CertificateFactory.getInstance("X.509");
847
848
for (int i = 0; i < rawCertData.length; i++) {
849
try {
850
InputStream input = new ByteArrayInputStream(rawCertData[i]);
851
X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
852
input.close();
853
854
// We successfully created the certificate, so track it and its corresponding SecCertificateRef.
855
createdCerts.add(new CertKeychainItemPair(secCertificateRefs[i], cert));
856
} catch (CertificateException e) {
857
// The certificate will be skipped.
858
System.err.println("KeychainStore Ignored Exception: " + e);
859
}
860
}
861
} catch (CertificateException e) {
862
e.printStackTrace();
863
} catch (IOException ioe) {
864
ioe.printStackTrace(); // How would this happen?
865
}
866
867
// We have our certificates in the List, so now extract them into an array of
868
// Certificates and SecCertificateRefs.
869
Object[] objArray = createdCerts.toArray();
870
Certificate[] certArray = new Certificate[objArray.length];
871
long[] certRefArray = new long[objArray.length];
872
873
for (int i = 0; i < objArray.length; i++) {
874
CertKeychainItemPair addedItem = (CertKeychainItemPair)objArray[i];
875
certArray[i] = addedItem.mCert;
876
certRefArray[i] = addedItem.mCertificateRef;
877
}
878
879
ke.chain = certArray;
880
ke.chainRefs = certRefArray;
881
882
// If we don't have already have an item with this item's alias
883
// create a new one for it.
884
int uniqueVal = 1;
885
String originalAlias = alias;
886
887
while (entries.containsKey(alias.toLowerCase())) {
888
alias = originalAlias + " " + uniqueVal;
889
uniqueVal++;
890
}
891
892
entries.put(alias.toLowerCase(), ke);
893
}
894
895
private class CertKeychainItemPair {
896
long mCertificateRef;
897
Certificate mCert;
898
899
CertKeychainItemPair(long inCertRef, Certificate cert) {
900
mCertificateRef = inCertRef;
901
mCert = cert;
902
}
903
}
904
905
/*
906
* Validate Certificate Chain
907
*/
908
private boolean validateChain(Certificate[] certChain)
909
{
910
for (int i = 0; i < certChain.length-1; i++) {
911
X500Principal issuerDN =
912
((X509Certificate)certChain[i]).getIssuerX500Principal();
913
X500Principal subjectDN =
914
((X509Certificate)certChain[i+1]).getSubjectX500Principal();
915
if (!(issuerDN.equals(subjectDN)))
916
return false;
917
}
918
return true;
919
}
920
921
private byte[] fetchPrivateKeyFromBag(byte[] privateKeyInfo) throws IOException, NoSuchAlgorithmException, CertificateException
922
{
923
byte[] returnValue = null;
924
DerValue val = new DerValue(new ByteArrayInputStream(privateKeyInfo));
925
DerInputStream s = val.toDerInputStream();
926
int version = s.getInteger();
927
928
if (version != 3) {
929
throw new IOException("PKCS12 keystore not in version 3 format");
930
}
931
932
/*
933
* Read the authSafe.
934
*/
935
byte[] authSafeData;
936
ContentInfo authSafe = new ContentInfo(s);
937
ObjectIdentifier contentType = authSafe.getContentType();
938
939
if (contentType.equals(ContentInfo.DATA_OID)) {
940
authSafeData = authSafe.getData();
941
} else /* signed data */ {
942
throw new IOException("public key protected PKCS12 not supported");
943
}
944
945
DerInputStream as = new DerInputStream(authSafeData);
946
DerValue[] safeContentsArray = as.getSequence(2);
947
int count = safeContentsArray.length;
948
949
/*
950
* Spin over the ContentInfos.
951
*/
952
for (int i = 0; i < count; i++) {
953
byte[] safeContentsData;
954
ContentInfo safeContents;
955
DerInputStream sci;
956
byte[] eAlgId = null;
957
958
sci = new DerInputStream(safeContentsArray[i].toByteArray());
959
safeContents = new ContentInfo(sci);
960
contentType = safeContents.getContentType();
961
safeContentsData = null;
962
963
if (contentType.equals(ContentInfo.DATA_OID)) {
964
safeContentsData = safeContents.getData();
965
} else if (contentType.equals(ContentInfo.ENCRYPTED_DATA_OID)) {
966
// The password was used to export the private key from the keychain.
967
// The Keychain won't export the key with encrypted data, so we don't need
968
// to worry about it.
969
continue;
970
} else {
971
throw new IOException("public key protected PKCS12" +
972
" not supported");
973
}
974
DerInputStream sc = new DerInputStream(safeContentsData);
975
returnValue = extractKeyData(sc);
976
}
977
978
return returnValue;
979
}
980
981
private byte[] extractKeyData(DerInputStream stream)
982
throws IOException, NoSuchAlgorithmException, CertificateException
983
{
984
byte[] returnValue = null;
985
DerValue[] safeBags = stream.getSequence(2);
986
int count = safeBags.length;
987
988
/*
989
* Spin over the SafeBags.
990
*/
991
for (int i = 0; i < count; i++) {
992
ObjectIdentifier bagId;
993
DerInputStream sbi;
994
DerValue bagValue;
995
Object bagItem = null;
996
997
sbi = safeBags[i].toDerInputStream();
998
bagId = sbi.getOID();
999
bagValue = sbi.getDerValue();
1000
if (!bagValue.isContextSpecific((byte)0)) {
1001
throw new IOException("unsupported PKCS12 bag value type "
1002
+ bagValue.tag);
1003
}
1004
bagValue = bagValue.data.getDerValue();
1005
if (bagId.equals(PKCS8ShroudedKeyBag_OID)) {
1006
// got what we were looking for. Return it.
1007
returnValue = bagValue.toByteArray();
1008
} else {
1009
// log error message for "unsupported PKCS12 bag type"
1010
System.out.println("Unsupported bag type '" + bagId + "'");
1011
}
1012
}
1013
1014
return returnValue;
1015
}
1016
1017
/*
1018
* Generate PBE Algorithm Parameters
1019
*/
1020
private AlgorithmParameters getAlgorithmParameters(String algorithm)
1021
throws IOException
1022
{
1023
AlgorithmParameters algParams = null;
1024
1025
// create PBE parameters from salt and iteration count
1026
PBEParameterSpec paramSpec =
1027
new PBEParameterSpec(getSalt(), iterationCount);
1028
try {
1029
algParams = AlgorithmParameters.getInstance(algorithm);
1030
algParams.init(paramSpec);
1031
} catch (Exception e) {
1032
IOException ioe =
1033
new IOException("getAlgorithmParameters failed: " +
1034
e.getMessage());
1035
ioe.initCause(e);
1036
throw ioe;
1037
}
1038
return algParams;
1039
}
1040
1041
// the source of randomness
1042
private SecureRandom random;
1043
1044
/*
1045
* Generate random salt
1046
*/
1047
private byte[] getSalt()
1048
{
1049
// Generate a random salt.
1050
byte[] salt = new byte[SALT_LEN];
1051
if (random == null) {
1052
random = new SecureRandom();
1053
}
1054
salt = random.generateSeed(SALT_LEN);
1055
return salt;
1056
}
1057
1058
/*
1059
* parse Algorithm Parameters
1060
*/
1061
private AlgorithmParameters parseAlgParameters(DerInputStream in)
1062
throws IOException
1063
{
1064
AlgorithmParameters algParams = null;
1065
try {
1066
DerValue params;
1067
if (in.available() == 0) {
1068
params = null;
1069
} else {
1070
params = in.getDerValue();
1071
if (params.tag == DerValue.tag_Null) {
1072
params = null;
1073
}
1074
}
1075
if (params != null) {
1076
algParams = AlgorithmParameters.getInstance("PBE");
1077
algParams.init(params.toByteArray());
1078
}
1079
} catch (Exception e) {
1080
IOException ioe =
1081
new IOException("parseAlgParameters failed: " +
1082
e.getMessage());
1083
ioe.initCause(e);
1084
throw ioe;
1085
}
1086
return algParams;
1087
}
1088
1089
/*
1090
* Generate PBE key
1091
*/
1092
private SecretKey getPBEKey(char[] password) throws IOException
1093
{
1094
SecretKey skey = null;
1095
1096
try {
1097
PBEKeySpec keySpec = new PBEKeySpec(password);
1098
SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
1099
skey = skFac.generateSecret(keySpec);
1100
} catch (Exception e) {
1101
IOException ioe = new IOException("getSecretKey failed: " +
1102
e.getMessage());
1103
ioe.initCause(e);
1104
throw ioe;
1105
}
1106
return skey;
1107
}
1108
1109
/*
1110
* Encrypt private key using Password-based encryption (PBE)
1111
* as defined in PKCS#5.
1112
*
1113
* NOTE: Currently pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
1114
* used to derive the key and IV.
1115
*
1116
* @return encrypted private key encoded as EncryptedPrivateKeyInfo
1117
*/
1118
private byte[] encryptPrivateKey(byte[] data, char[] password)
1119
throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
1120
{
1121
byte[] key = null;
1122
1123
try {
1124
// create AlgorithmParameters
1125
AlgorithmParameters algParams =
1126
getAlgorithmParameters("PBEWithSHA1AndDESede");
1127
1128
// Use JCE
1129
SecretKey skey = getPBEKey(password);
1130
Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDESede");
1131
cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
1132
byte[] encryptedKey = cipher.doFinal(data);
1133
1134
// wrap encrypted private key in EncryptedPrivateKeyInfo
1135
// as defined in PKCS#8
1136
AlgorithmId algid =
1137
new AlgorithmId(pbeWithSHAAnd3KeyTripleDESCBC_OID, algParams);
1138
EncryptedPrivateKeyInfo encrInfo =
1139
new EncryptedPrivateKeyInfo(algid, encryptedKey);
1140
key = encrInfo.getEncoded();
1141
} catch (Exception e) {
1142
UnrecoverableKeyException uke =
1143
new UnrecoverableKeyException("Encrypt Private Key failed: "
1144
+ e.getMessage());
1145
uke.initCause(e);
1146
throw uke;
1147
}
1148
1149
return ((byte[])key);
1150
}
1151
1152
1153
}
1154
1155
1156