History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/pkcs12/PKCS12KeyStore.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/pkcs12/PKCS12KeyStore.java+1536−0
@@ -0,0 +1,1536 @@
1/*
2 * Copyright (c) 1999, 2010, 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
26package sun.security.pkcs12;
27
28import java.io.*;
29import java.security.MessageDigest;
30import java.security.NoSuchAlgorithmException;
31import java.security.Key;
32import java.security.KeyFactory;
33import java.security.PrivateKey;
34import java.security.KeyStoreSpi;
35import java.security.KeyStoreException;
36import java.security.UnrecoverableKeyException;
37import java.security.SecureRandom;
38import java.security.cert.Certificate;
39import java.security.cert.CertificateFactory;
40import java.security.cert.X509Certificate;
41import java.security.cert.CertificateException;
42import java.security.spec.PKCS8EncodedKeySpec;
43import java.util.*;
44import java.math.*;
45
46import java.security.AlgorithmParameters;
47import java.security.spec.AlgorithmParameterSpec;
48import javax.crypto.spec.PBEParameterSpec;
49import javax.crypto.spec.PBEKeySpec;
50import javax.crypto.spec.SecretKeySpec;
51import javax.crypto.SecretKeyFactory;
52import javax.crypto.SecretKey;
53import javax.crypto.Cipher;
54import javax.crypto.Mac;
55import javax.security.auth.x500.X500Principal;
56
57import sun.security.util.DerInputStream;
58import sun.security.util.DerOutputStream;
59import sun.security.util.DerValue;
60import sun.security.util.ObjectIdentifier;
61import sun.security.pkcs.ContentInfo;
62import sun.security.x509.AlgorithmId;
63import sun.security.pkcs.EncryptedPrivateKeyInfo;
64
65
66/**
67 * This class provides the keystore implementation referred to as "PKCS12".
68 * Implements the PKCS#12 PFX protected using the Password privacy mode.
69 * The contents are protected using Password integrity mode.
70 *
71 * Currently we support following PBE algorithms:
72 * - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys
73 * - pbeWithSHAAnd40BitRC2CBC to encrypt certificates
74 *
75 * Supported encryption of various implementations :
76 *
77 * Software and mode. Certificate encryption Private key encryption
78 * ---------------------------------------------------------------------
79 * MSIE4 (domestic 40 bit RC2. 40 bit RC2
80 * and xport versions)
81 * PKCS#12 export.
82 *
83 * MSIE4, 5 (domestic 40 bit RC2, 40 bit RC2,
84 * and export versions) 3 key triple DES 3 key triple DES
85 * PKCS#12 import.
86 *
87 * MSIE5 40 bit RC2 3 key triple DES,
88 * PKCS#12 export. with SHA1 (168 bits)
89 *
90 * Netscape Communicator 40 bit RC2 3 key triple DES,
91 * (domestic and export with SHA1 (168 bits)
92 * versions) PKCS#12 export
93 *
94 * Netscape Communicator 40 bit ciphers only All.
95 * (export version)
96 * PKCS#12 import.
97 *
98 * Netscape Communicator All. All.
99 * (domestic or fortified
100 * version) PKCS#12 import.
101 *
102 * OpenSSL PKCS#12 code. All. All.
103 * ---------------------------------------------------------------------
104 *
105 * NOTE: Currently PKCS12 KeyStore does not support TrustedCertEntries.
106 * PKCS#12 is mainly used to deliver private keys with their associated
107 * certificate chain and aliases. In a PKCS12 keystore, entries are
108 * identified by the alias, and a localKeyId is required to match the
109 * private key with the certificate.
110 *
111 * @author Seema Malkani
112 * @author Jeff Nisewanger
113 * @author Jan Luehe
114 *
115 * @see KeyProtector
116 * @see java.security.KeyStoreSpi
117 * @see KeyTool
118 *
119 *
120 */
121public final class PKCS12KeyStore extends KeyStoreSpi {
122
123 public static final int VERSION_3 = 3;
124
125 private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2};
126 private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3};
127
128 private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20};
129 private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21};
130
131 private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1};
132
133 private static final int pbeWithSHAAnd40BitRC2CBC[] =
134 {1, 2, 840, 113549, 1, 12, 1, 6};
135 private static final int pbeWithSHAAnd3KeyTripleDESCBC[] =
136 {1, 2, 840, 113549, 1, 12, 1, 3};
137
138 private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;
139 private static ObjectIdentifier CertBag_OID;
140 private static ObjectIdentifier PKCS9FriendlyName_OID;
141 private static ObjectIdentifier PKCS9LocalKeyId_OID;
142 private static ObjectIdentifier PKCS9CertType_OID;
143 private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID;
144 private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;
145
146 private int counter = 0;
147 private static final int iterationCount = 1024;
148 private static final int SALT_LEN = 20;
149
150 // private key count
151 // Note: This is a workaround to allow null localKeyID attribute
152 // in pkcs12 with one private key entry and associated cert-chain
153 private int privateKeyCount = 0;
154
155 // the source of randomness
156 private SecureRandom random;
157
158 static {
159 try {
160 PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
161 CertBag_OID = new ObjectIdentifier(certBag);
162 PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name);
163 PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId);
164 PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType);
165 pbeWithSHAAnd40BitRC2CBC_OID =
166 new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC);
167 pbeWithSHAAnd3KeyTripleDESCBC_OID =
168 new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);
169 } catch (IOException ioe) {
170 // should not happen
171 }
172 }
173
174 // Private keys and their supporting certificate chains
175 private static class KeyEntry {
176 Date date; // the creation date of this entry
177 byte[] protectedPrivKey;
178 Certificate chain[];
179 byte[] keyId;
180 String alias;
181 };
182
183 // A certificate with its PKCS #9 attributes
184 private static class CertEntry {
185 final X509Certificate cert;
186 final byte[] keyId;
187 final String alias;
188 CertEntry(X509Certificate cert, byte[] keyId, String alias) {
189 this.cert = cert;
190 this.keyId = keyId;
191 this.alias = alias;
192 }
193 }
194
195 /**
196 * Private keys and certificates are stored in a hashtable.
197 * Hash entries are keyed by alias names.
198 */
199 private Hashtable<String, KeyEntry> entries =
200 new Hashtable<String, KeyEntry>();
201
202 private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>();
203 private LinkedHashMap<X500Principal, X509Certificate> certsMap =
204 new LinkedHashMap<X500Principal, X509Certificate>();
205 private ArrayList<CertEntry> certEntries = new ArrayList<CertEntry>();
206
207 /**
208 * Returns the key associated with the given alias, using the given
209 * password to recover it.
210 *
211 * @param alias the alias name
212 * @param password the password for recovering the key
213 *
214 * @return the requested key, or null if the given alias does not exist
215 * or does not identify a <i>key entry</i>.
216 *
217 * @exception NoSuchAlgorithmException if the algorithm for recovering the
218 * key cannot be found
219 * @exception UnrecoverableKeyException if the key cannot be recovered
220 * (e.g., the given password is wrong).
221 */
222 public Key engineGetKey(String alias, char[] password)
223 throws NoSuchAlgorithmException, UnrecoverableKeyException
224 {
225 KeyEntry entry = entries.get(alias.toLowerCase());
226 Key key = null;
227
228 if (entry == null) {
229 return null;
230 }
231
232 // get the encoded private key
233 byte[] encrBytes = entry.protectedPrivKey;
234
235 byte[] encryptedKey;
236 AlgorithmParameters algParams;
237 ObjectIdentifier algOid;
238 try {
239 // get the encrypted private key
240 EncryptedPrivateKeyInfo encrInfo =
241 new EncryptedPrivateKeyInfo(encrBytes);
242 encryptedKey = encrInfo.getEncryptedData();
243
244 // parse Algorithm parameters
245 DerValue val = new DerValue(encrInfo.getAlgorithm().encode());
246 DerInputStream in = val.toDerInputStream();
247 algOid = in.getOID();
248 algParams = parseAlgParameters(in);
249
250 } catch (IOException ioe) {
251 UnrecoverableKeyException uke =
252 new UnrecoverableKeyException("Private key not stored as "
253 + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
254 uke.initCause(ioe);
255 throw uke;
256 }
257
258 try {
259 // Use JCE
260 SecretKey skey = getPBEKey(password);
261 Cipher cipher = Cipher.getInstance(algOid.toString());
262 cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
263 byte[] privateKeyInfo = cipher.doFinal(encryptedKey);
264
265 PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(privateKeyInfo);
266
267 /*
268 * Parse the key algorithm and then use a JCA key factory
269 * to create the private key.
270 */
271 DerValue val = new DerValue(privateKeyInfo);
272 DerInputStream in = val.toDerInputStream();
273 int i = in.getInteger();
274 DerValue[] value = in.getSequence(2);
275 AlgorithmId algId = new AlgorithmId(value[0].getOID());
276 String algName = algId.getName();
277
278 KeyFactory kfac = KeyFactory.getInstance(algName);
279 key = kfac.generatePrivate(kspec);
280 } catch (Exception e) {
281 UnrecoverableKeyException uke =
282 new UnrecoverableKeyException("Get Key failed: " +
283 e.getMessage());
284 uke.initCause(e);
285 throw uke;
286 }
287 return key;
288 }
289
290 /**
291 * Returns the certificate chain associated with the given alias.
292 *
293 * @param alias the alias name
294 *
295 * @return the certificate chain (ordered with the user's certificate first
296 * and the root certificate authority last), or null if the given alias
297 * does not exist or does not contain a certificate chain (i.e., the given
298 * alias identifies either a <i>trusted certificate entry</i> or a
299 * <i>key entry</i> without a certificate chain).
300 */
301 public Certificate[] engineGetCertificateChain(String alias) {
302 KeyEntry entry = entries.get(alias.toLowerCase());
303 if (entry != null) {
304 if (entry.chain == null) {
305 return null;
306 } else {
307 return entry.chain.clone();
308 }
309 } else {
310 return null;
311 }
312 }
313
314 /**
315 * Returns the certificate associated with the given alias.
316 *
317 * <p>If the given alias name identifies a
318 * <i>trusted certificate entry</i>, the certificate associated with that
319 * entry is returned. If the given alias name identifies a
320 * <i>key entry</i>, the first element of the certificate chain of that
321 * entry is returned, or null if that entry does not have a certificate
322 * chain.
323 *
324 * @param alias the alias name
325 *
326 * @return the certificate, or null if the given alias does not exist or
327 * does not contain a certificate.
328 */
329 public Certificate engineGetCertificate(String alias) {
330 KeyEntry entry = entries.get(alias.toLowerCase());
331 if (entry != null) {
332 if (entry.chain == null) {
333 return null;
334 } else {
335 return entry.chain[0];
336 }
337 } else {
338 return null;
339 }
340 }
341
342 /**
343 * Returns the creation date of the entry identified by the given alias.
344 *
345 * @param alias the alias name
346 *
347 * @return the creation date of this entry, or null if the given alias does
348 * not exist
349 */
350 public Date engineGetCreationDate(String alias) {
351 KeyEntry entry = entries.get(alias.toLowerCase());
352 if (entry != null) {
353 return new Date(entry.date.getTime());
354 } else {
355 return null;
356 }
357 }
358
359 /**
360 * Assigns the given key to the given alias, protecting it with the given
361 * password.
362 *
363 * <p>If the given key is of type <code>java.security.PrivateKey</code>,
364 * it must be accompanied by a certificate chain certifying the
365 * corresponding public key.
366 *
367 * <p>If the given alias already exists, the keystore information
368 * associated with it is overridden by the given key (and possibly
369 * certificate chain).
370 *
371 * @param alias the alias name
372 * @param key the key to be associated with the alias
373 * @param password the password to protect the key
374 * @param chain the certificate chain for the corresponding public
375 * key (only required if the given key is of type
376 * <code>java.security.PrivateKey</code>).
377 *
378 * @exception KeyStoreException if the given key cannot be protected, or
379 * this operation fails for some other reason
380 */
381 public synchronized void engineSetKeyEntry(String alias, Key key,
382 char[] password, Certificate[] chain)
383 throws KeyStoreException
384 {
385 try {
386 KeyEntry entry = new KeyEntry();
387 entry.date = new Date();
388
389 if (key instanceof PrivateKey) {
390 if ((key.getFormat().equals("PKCS#8")) ||
391 (key.getFormat().equals("PKCS8"))) {
392 // Encrypt the private key
393 entry.protectedPrivKey =
394 encryptPrivateKey(key.getEncoded(), password);
395 } else {
396 throw new KeyStoreException("Private key is not encoded" +
397 "as PKCS#8");
398 }
399 } else {
400 throw new KeyStoreException("Key is not a PrivateKey");
401 }
402
403 // clone the chain
404 if (chain != null) {
405 // validate cert-chain
406 if ((chain.length > 1) && (!validateChain(chain)))
407 throw new KeyStoreException("Certificate chain is " +
408 "not validate");
409 entry.chain = chain.clone();
410 }
411
412 // set the keyId to current date
413 entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
414 // set the alias
415 entry.alias = alias.toLowerCase();
416
417 // add the entry
418 entries.put(alias.toLowerCase(), entry);
419 } catch (Exception nsae) {
420 KeyStoreException ke = new KeyStoreException("Key protection " +
421 " algorithm not found: " + nsae);
422 ke.initCause(nsae);
423 throw ke;
424 }
425 }
426
427 /**
428 * Assigns the given key (that has already been protected) to the given
429 * alias.
430 *
431 * <p>If the protected key is of type
432 * <code>java.security.PrivateKey</code>, it must be accompanied by a
433 * certificate chain certifying the corresponding public key. If the
434 * underlying keystore implementation is of type <code>jks</code>,
435 * <code>key</code> must be encoded as an
436 * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
437 *
438 * <p>If the given alias already exists, the keystore information
439 * associated with it is overridden by the given key (and possibly
440 * certificate chain).
441 *
442 * @param alias the alias name
443 * @param key the key (in protected format) to be associated with the alias
444 * @param chain the certificate chain for the corresponding public
445 * key (only useful if the protected key is of type
446 * <code>java.security.PrivateKey</code>).
447 *
448 * @exception KeyStoreException if this operation fails.
449 */
450 public synchronized void engineSetKeyEntry(String alias, byte[] key,
451 Certificate[] chain)
452 throws KeyStoreException
453 {
454 // key must be encoded as EncryptedPrivateKeyInfo
455 // as defined in PKCS#8
456 try {
457 new EncryptedPrivateKeyInfo(key);
458 } catch (IOException ioe) {
459 KeyStoreException ke = new KeyStoreException("Private key is not"
460 + " stored as PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
461 ke.initCause(ioe);
462 throw ke;
463 }
464
465 KeyEntry entry = new KeyEntry();
466 entry.date = new Date();
467
468 try {
469 // set the keyId to current date
470 entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
471 } catch (UnsupportedEncodingException ex) {
472 // Won't happen
473 }
474 // set the alias
475 entry.alias = alias.toLowerCase();
476
477 entry.protectedPrivKey = key.clone();
478 if (chain != null) {
479 entry.chain = chain.clone();
480 }
481
482 // add the entry
483 entries.put(alias.toLowerCase(), entry);
484 }
485
486
487 /*
488 * Generate random salt
489 */
490 private byte[] getSalt()
491 {
492 // Generate a random salt.
493 byte[] salt = new byte[SALT_LEN];
494 if (random == null) {
495 random = new SecureRandom();
496 }
497 random.nextBytes(salt);
498 return salt;
499 }
500
501 /*
502 * Generate PBE Algorithm Parameters
503 */
504 private AlgorithmParameters getAlgorithmParameters(String algorithm)
505 throws IOException
506 {
507 AlgorithmParameters algParams = null;
508
509 // create PBE parameters from salt and iteration count
510 PBEParameterSpec paramSpec =
511 new PBEParameterSpec(getSalt(), iterationCount);
512 try {
513 algParams = AlgorithmParameters.getInstance(algorithm);
514 algParams.init(paramSpec);
515 } catch (Exception e) {
516 IOException ioe =
517 new IOException("getAlgorithmParameters failed: " +
518 e.getMessage());
519 ioe.initCause(e);
520 throw ioe;
521 }
522 return algParams;
523 }
524
525 /*
526 * parse Algorithm Parameters
527 */
528 private AlgorithmParameters parseAlgParameters(DerInputStream in)
529 throws IOException
530 {
531 AlgorithmParameters algParams = null;
532 try {
533 DerValue params;
534 if (in.available() == 0) {
535 params = null;
536 } else {
537 params = in.getDerValue();
538 if (params.tag == DerValue.tag_Null) {
539 params = null;
540 }
541 }
542 if (params != null) {
543 algParams = AlgorithmParameters.getInstance("PBE");
544 algParams.init(params.toByteArray());
545 }
546 } catch (Exception e) {
547 IOException ioe =
548 new IOException("parseAlgParameters failed: " +
549 e.getMessage());
550 ioe.initCause(e);
551 throw ioe;
552 }
553 return algParams;
554 }
555
556 /*
557 * Generate PBE key
558 */
559 private SecretKey getPBEKey(char[] password) throws IOException
560 {
561 SecretKey skey = null;
562
563 try {
564 PBEKeySpec keySpec = new PBEKeySpec(password);
565 SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
566 skey = skFac.generateSecret(keySpec);
567 } catch (Exception e) {
568 IOException ioe = new IOException("getSecretKey failed: " +
569 e.getMessage());
570 ioe.initCause(e);
571 throw ioe;
572 }
573 return skey;
574 }
575
576 /*
577 * Encrypt private key using Password-based encryption (PBE)
578 * as defined in PKCS#5.
579 *
580 * NOTE: Currently pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
581 * used to derive the key and IV.
582 *
583 * @return encrypted private key encoded as EncryptedPrivateKeyInfo
584 */
585 private byte[] encryptPrivateKey(byte[] data, char[] password)
586 throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
587 {
588 byte[] key = null;
589
590 try {
591 // create AlgorithmParameters
592 AlgorithmParameters algParams =
593 getAlgorithmParameters("PBEWithSHA1AndDESede");
594
595 // Use JCE
596 SecretKey skey = getPBEKey(password);
597 Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDESede");
598 cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
599 byte[] encryptedKey = cipher.doFinal(data);
600
601 // wrap encrypted private key in EncryptedPrivateKeyInfo
602 // as defined in PKCS#8
603 AlgorithmId algid =
604 new AlgorithmId(pbeWithSHAAnd3KeyTripleDESCBC_OID, algParams);
605 EncryptedPrivateKeyInfo encrInfo =
606 new EncryptedPrivateKeyInfo(algid, encryptedKey);
607 key = encrInfo.getEncoded();
608 } catch (Exception e) {
609 UnrecoverableKeyException uke =
610 new UnrecoverableKeyException("Encrypt Private Key failed: "
611 + e.getMessage());
612 uke.initCause(e);
613 throw uke;
614 }
615
616 return key;
617 }
618
619 /**
620 * Assigns the given certificate to the given alias.
621 *
622 * <p>If the given alias already exists in this keystore and identifies a
623 * <i>trusted certificate entry</i>, the certificate associated with it is
624 * overridden by the given certificate.
625 *
626 * @param alias the alias name
627 * @param cert the certificate
628 *
629 * @exception KeyStoreException if the given alias already exists and does
630 * identify a <i>key entry</i>, or on an attempt to create a
631 * <i>trusted cert entry</i> which is currently not supported.
632 */
633 public synchronized void engineSetCertificateEntry(String alias,
634 Certificate cert) throws KeyStoreException
635 {
636 KeyEntry entry = entries.get(alias.toLowerCase());
637 if (entry != null) {
638 throw new KeyStoreException("Cannot overwrite own certificate");
639 } else
640 throw new KeyStoreException("TrustedCertEntry not supported");
641 }
642
643 /**
644 * Deletes the entry identified by the given alias from this keystore.
645 *
646 * @param alias the alias name
647 *
648 * @exception KeyStoreException if the entry cannot be removed.
649 */
650 public synchronized void engineDeleteEntry(String alias)
651 throws KeyStoreException
652 {
653 entries.remove(alias.toLowerCase());
654 }
655
656 /**
657 * Lists all the alias names of this keystore.
658 *
659 * @return enumeration of the alias names
660 */
661 public Enumeration<String> engineAliases() {
662 return entries.keys();
663 }
664
665 /**
666 * Checks if the given alias exists in this keystore.
667 *
668 * @param alias the alias name
669 *
670 * @return true if the alias exists, false otherwise
671 */
672 public boolean engineContainsAlias(String alias) {
673 return entries.containsKey(alias.toLowerCase());
674 }
675
676 /**
677 * Retrieves the number of entries in this keystore.
678 *
679 * @return the number of entries in this keystore
680 */
681 public int engineSize() {
682 return entries.size();
683 }
684
685 /**
686 * Returns true if the entry identified by the given alias is a
687 * <i>key entry</i>, and false otherwise.
688 *
689 * @return true if the entry identified by the given alias is a
690 * <i>key entry</i>, false otherwise.
691 */
692 public boolean engineIsKeyEntry(String alias) {
693 KeyEntry entry = entries.get(alias.toLowerCase());
694 if (entry != null) {
695 return true;
696 } else {
697 return false;
698 }
699 }
700
701 /**
702 * Returns true if the entry identified by the given alias is a
703 * <i>trusted certificate entry</i>, and false otherwise.
704 *
705 * @return true if the entry identified by the given alias is a
706 * <i>trusted certificate entry</i>, false otherwise.
707 */
708 public boolean engineIsCertificateEntry(String alias) {
709 // TrustedCertEntry is not supported
710 return false;
711 }
712
713 /**
714 * Returns the (alias) name of the first keystore entry whose certificate
715 * matches the given certificate.
716 *
717 * <p>This method attempts to match the given certificate with each
718 * keystore entry. If the entry being considered
719 * is a <i>trusted certificate entry</i>, the given certificate is
720 * compared to that entry's certificate. If the entry being considered is
721 * a <i>key entry</i>, the given certificate is compared to the first
722 * element of that entry's certificate chain (if a chain exists).
723 *
724 * @param cert the certificate to match with.
725 *
726 * @return the (alias) name of the first entry with matching certificate,
727 * or null if no such entry exists in this keystore.
728 */
729 public String engineGetCertificateAlias(Certificate cert) {
730 Certificate certElem = null;
731
732 for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
733 String alias = e.nextElement();
734 KeyEntry entry = entries.get(alias);
735 if (entry.chain != null) {
736 certElem = entry.chain[0];
737 }
738 if (certElem.equals(cert)) {
739 return alias;
740 }
741 }
742 return null;
743 }
744
745 /**
746 * Stores this keystore to the given output stream, and protects its
747 * integrity with the given password.
748 *
749 * @param stream the output stream to which this keystore is written.
750 * @param password the password to generate the keystore integrity check
751 *
752 * @exception IOException if there was an I/O problem with data
753 * @exception NoSuchAlgorithmException if the appropriate data integrity
754 * algorithm could not be found
755 * @exception CertificateException if any of the certificates included in
756 * the keystore data could not be stored
757 */
758 public synchronized void engineStore(OutputStream stream, char[] password)
759 throws IOException, NoSuchAlgorithmException, CertificateException
760 {
761 // password is mandatory when storing
762 if (password == null) {
763 throw new IllegalArgumentException("password can't be null");
764 }
765
766 // -- Create PFX
767 DerOutputStream pfx = new DerOutputStream();
768
769 // PFX version (always write the latest version)
770 DerOutputStream version = new DerOutputStream();
771 version.putInteger(VERSION_3);
772 byte[] pfxVersion = version.toByteArray();
773 pfx.write(pfxVersion);
774
775 // -- Create AuthSafe
776 DerOutputStream authSafe = new DerOutputStream();
777
778 // -- Create ContentInfos
779 DerOutputStream authSafeContentInfo = new DerOutputStream();
780
781 // -- create safeContent Data ContentInfo
782 byte[] safeContentData = createSafeContent();
783 ContentInfo dataContentInfo = new ContentInfo(safeContentData);
784 dataContentInfo.encode(authSafeContentInfo);
785
786 // -- create EncryptedContentInfo
787 byte[] encrData = createEncryptedData(password);
788 ContentInfo encrContentInfo =
789 new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
790 new DerValue(encrData));
791 encrContentInfo.encode(authSafeContentInfo);
792
793 // wrap as SequenceOf ContentInfos
794 DerOutputStream cInfo = new DerOutputStream();
795 cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
796 byte[] authenticatedSafe = cInfo.toByteArray();
797
798 // Create Encapsulated ContentInfo
799 ContentInfo contentInfo = new ContentInfo(authenticatedSafe);
800 contentInfo.encode(authSafe);
801 byte[] authSafeData = authSafe.toByteArray();
802 pfx.write(authSafeData);
803
804 // -- MAC
805 byte[] macData = calculateMac(password, authenticatedSafe);
806 pfx.write(macData);
807
808 // write PFX to output stream
809 DerOutputStream pfxout = new DerOutputStream();
810 pfxout.write(DerValue.tag_Sequence, pfx);
811 byte[] pfxData = pfxout.toByteArray();
812 stream.write(pfxData);
813 stream.flush();
814 }
815
816
817 /*
818 * Generate Hash.
819 */
820 private byte[] generateHash(byte[] data) throws IOException
821 {
822 byte[] digest = null;
823
824 try {
825 MessageDigest md = MessageDigest.getInstance("SHA1");
826 md.update(data);
827 digest = md.digest();
828 } catch (Exception e) {
829 IOException ioe = new IOException("generateHash failed: " + e);
830 ioe.initCause(e);
831 throw ioe;
832 }
833 return digest;
834 }
835
836
837 /*
838 * Calculate MAC using HMAC algorithm (required for password integrity)
839 *
840 * Hash-based MAC algorithm combines secret key with message digest to
841 * create a message authentication code (MAC)
842 */
843 private byte[] calculateMac(char[] passwd, byte[] data)
844 throws IOException
845 {
846 byte[] mData = null;
847 String algName = "SHA1";
848
849 try {
850 // Generate a random salt.
851 byte[] salt = getSalt();
852
853 // generate MAC (MAC key is generated within JCE)
854 Mac m = Mac.getInstance("HmacPBESHA1");
855 PBEParameterSpec params =
856 new PBEParameterSpec(salt, iterationCount);
857 SecretKey key = getPBEKey(passwd);
858 m.init(key, params);
859 m.update(data);
860 byte[] macResult = m.doFinal();
861
862 // encode as MacData
863 MacData macData = new MacData(algName, macResult, salt,
864 iterationCount);
865 DerOutputStream bytes = new DerOutputStream();
866 bytes.write(macData.getEncoded());
867 mData = bytes.toByteArray();
868 } catch (Exception e) {
869 IOException ioe = new IOException("calculateMac failed: " + e);
870 ioe.initCause(e);
871 throw ioe;
872 }
873 return mData;
874 }
875
876
877 /*
878 * Validate Certificate Chain
879 */
880 private boolean validateChain(Certificate[] certChain)
881 {
882 for (int i = 0; i < certChain.length-1; i++) {
883 X500Principal issuerDN =
884 ((X509Certificate)certChain[i]).getIssuerX500Principal();
885 X500Principal subjectDN =
886 ((X509Certificate)certChain[i+1]).getSubjectX500Principal();
887 if (!(issuerDN.equals(subjectDN)))
888 return false;
889 }
890 return true;
891 }
892
893
894 /*
895 * Create PKCS#12 Attributes, friendlyName and localKeyId.
896 *
897 * Although attributes are optional, they could be required.
898 * For e.g. localKeyId attribute is required to match the
899 * private key with the associated end-entity certificate.
900 *
901 * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName.
902 * CertBags may or may not include attributes depending on the type
903 * of Certificate. In end-entity certificates, localKeyID should be
904 * unique, and the corresponding private key should have the same
905 * localKeyID. For trusted CA certs in the cert-chain, localKeyID
906 * attribute is not required, hence most vendors don't include it.
907 * NSS/Netscape require it to be unique or null, where as IE/OpenSSL
908 * ignore it.
909 *
910 * Here is a list of pkcs12 attribute values in CertBags.
911 *
912 * PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE
913 * --------------------------------------------------------------
914 * LocalKeyId
915 * (In EE cert only,
916 * NULL in CA certs) true true true true
917 *
918 * friendlyName unique same/ same/ unique
919 * unique unique/
920 * null
921 *
922 * Note: OpenSSL adds friendlyName for end-entity cert only, and
923 * removes the localKeyID and friendlyName for CA certs.
924 * If the CertBag did not have a friendlyName, most vendors will
925 * add it, and assign it to the DN of the cert.
926 */
927 private byte[] getBagAttributes(String alias, byte[] keyId)
928 throws IOException {
929
930 byte[] localKeyID = null;
931 byte[] friendlyName = null;
932
933 // return null if both attributes are null
934 if ((alias == null) && (keyId == null)) {
935 return null;
936 }
937
938 // SafeBag Attributes
939 DerOutputStream bagAttrs = new DerOutputStream();
940
941 // Encode the friendlyname oid.
942 if (alias != null) {
943 DerOutputStream bagAttr1 = new DerOutputStream();
944 bagAttr1.putOID(PKCS9FriendlyName_OID);
945 DerOutputStream bagAttrContent1 = new DerOutputStream();
946 DerOutputStream bagAttrValue1 = new DerOutputStream();
947 bagAttrContent1.putBMPString(alias);
948 bagAttr1.write(DerValue.tag_Set, bagAttrContent1);
949 bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1);
950 friendlyName = bagAttrValue1.toByteArray();
951 }
952
953 // Encode the localkeyId oid.
954 if (keyId != null) {
955 DerOutputStream bagAttr2 = new DerOutputStream();
956 bagAttr2.putOID(PKCS9LocalKeyId_OID);
957 DerOutputStream bagAttrContent2 = new DerOutputStream();
958 DerOutputStream bagAttrValue2 = new DerOutputStream();
959 bagAttrContent2.putOctetString(keyId);
960 bagAttr2.write(DerValue.tag_Set, bagAttrContent2);
961 bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2);
962 localKeyID = bagAttrValue2.toByteArray();
963 }
964
965 DerOutputStream attrs = new DerOutputStream();
966 if (friendlyName != null) {
967 attrs.write(friendlyName);
968 }
969 if (localKeyID != null) {
970 attrs.write(localKeyID);
971 }
972 bagAttrs.write(DerValue.tag_Set, attrs);
973 return bagAttrs.toByteArray();
974 }
975
976
977 /*
978 * Create EncryptedData content type, that contains EncryptedContentInfo.
979 * Includes certificates in individual SafeBags of type CertBag.
980 * Each CertBag may include pkcs12 attributes
981 * (see comments in getBagAttributes)
982 */
983 private byte[] createEncryptedData(char[] password)
984 throws CertificateException, IOException
985 {
986 DerOutputStream out = new DerOutputStream();
987 for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
988
989 String alias = e.nextElement();
990 KeyEntry entry = entries.get(alias);
991
992 // certificate chain
993 int chainLen;
994 if (entry.chain == null) {
995 chainLen = 0;
996 } else {
997 chainLen = entry.chain.length;
998 }
999
1000 for (int i = 0; i < chainLen; i++) {
1001 // create SafeBag of Type CertBag
1002 DerOutputStream safeBag = new DerOutputStream();
1003 safeBag.putOID(CertBag_OID);
1004
1005 // create a CertBag
1006 DerOutputStream certBag = new DerOutputStream();
1007 certBag.putOID(PKCS9CertType_OID);
1008
1009 // write encoded certs in a context-specific tag
1010 DerOutputStream certValue = new DerOutputStream();
1011 X509Certificate cert = (X509Certificate)entry.chain[i];
1012 certValue.putOctetString(cert.getEncoded());
1013 certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1014 true, (byte) 0), certValue);
1015
1016 // wrap CertBag in a Sequence
1017 DerOutputStream certout = new DerOutputStream();
1018 certout.write(DerValue.tag_Sequence, certBag);
1019 byte[] certBagValue = certout.toByteArray();
1020
1021 // Wrap the CertBag encoding in a context-specific tag.
1022 DerOutputStream bagValue = new DerOutputStream();
1023 bagValue.write(certBagValue);
1024 // write SafeBag Value
1025 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1026 true, (byte) 0), bagValue);
1027
1028 // write SafeBag Attributes
1029 // All Certs should have a unique friendlyName.
1030 // This change is made to meet NSS requirements.
1031 byte[] bagAttrs = null;
1032 if (i == 0) {
1033 // Only End-Entity Cert should have a localKeyId.
1034 bagAttrs = getBagAttributes(entry.alias, entry.keyId);
1035 } else {
1036 // Trusted root CA certs and Intermediate CA certs do not
1037 // need to have a localKeyId, and hence localKeyId is null
1038 // This change is made to meet NSS/Netscape requirements.
1039 // NSS pkcs12 library requires trusted CA certs in the
1040 // certificate chain to have unique or null localKeyID.
1041 // However, IE/OpenSSL do not impose this restriction.
1042 bagAttrs = getBagAttributes(
1043 cert.getSubjectX500Principal().getName(), null);
1044 }
1045 if (bagAttrs != null) {
1046 safeBag.write(bagAttrs);
1047 }
1048
1049 // wrap as Sequence
1050 out.write(DerValue.tag_Sequence, safeBag);
1051 } // for cert-chain
1052 }
1053
1054 // wrap as SequenceOf SafeBag
1055 DerOutputStream safeBagValue = new DerOutputStream();
1056 safeBagValue.write(DerValue.tag_SequenceOf, out);
1057 byte[] safeBagData = safeBagValue.toByteArray();
1058
1059 // encrypt the content (EncryptedContentInfo)
1060 byte[] encrContentInfo = encryptContent(safeBagData, password);
1061
1062 // -- SEQUENCE of EncryptedData
1063 DerOutputStream encrData = new DerOutputStream();
1064 DerOutputStream encrDataContent = new DerOutputStream();
1065 encrData.putInteger(0);
1066 encrData.write(encrContentInfo);
1067 encrDataContent.write(DerValue.tag_Sequence, encrData);
1068 return encrDataContent.toByteArray();
1069 }
1070
1071 /*
1072 * Create SafeContent Data content type.
1073 * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.
1074 * Each PKCS8ShroudedKeyBag includes pkcs12 attributes
1075 * (see comments in getBagAttributes)
1076 */
1077 private byte[] createSafeContent()
1078 throws CertificateException, IOException {
1079
1080 DerOutputStream out = new DerOutputStream();
1081 for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
1082
1083 String alias = e.nextElement();
1084 KeyEntry entry = entries.get(alias);
1085
1086 // Create SafeBag of type pkcs8ShroudedKeyBag
1087 DerOutputStream safeBag = new DerOutputStream();
1088 safeBag.putOID(PKCS8ShroudedKeyBag_OID);
1089
1090 // get the encrypted private key
1091 byte[] encrBytes = entry.protectedPrivKey;
1092 EncryptedPrivateKeyInfo encrInfo = null;
1093 try {
1094 encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
1095 } catch (IOException ioe) {
1096 throw new IOException("Private key not stored as "
1097 + "PKCS#8 EncryptedPrivateKeyInfo" + ioe.getMessage());
1098 }
1099
1100 // Wrap the EncryptedPrivateKeyInfo in a context-specific tag.
1101 DerOutputStream bagValue = new DerOutputStream();
1102 bagValue.write(encrInfo.getEncoded());
1103 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1104 true, (byte) 0), bagValue);
1105
1106 // write SafeBag Attributes
1107 byte[] bagAttrs = getBagAttributes(alias, entry.keyId);
1108 safeBag.write(bagAttrs);
1109
1110 // wrap as Sequence
1111 out.write(DerValue.tag_Sequence, safeBag);
1112 }
1113
1114 // wrap as Sequence
1115 DerOutputStream safeBagValue = new DerOutputStream();
1116 safeBagValue.write(DerValue.tag_Sequence, out);
1117 return safeBagValue.toByteArray();
1118 }
1119
1120
1121 /*
1122 * Encrypt the contents using Password-based (PBE) encryption
1123 * as defined in PKCS #5.
1124 *
1125 * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used
1126 * to derive the key and IV.
1127 *
1128 * @return encrypted contents encoded as EncryptedContentInfo
1129 */
1130 private byte[] encryptContent(byte[] data, char[] password)
1131 throws IOException {
1132
1133 byte[] encryptedData = null;
1134
1135 // create AlgorithmParameters
1136 AlgorithmParameters algParams =
1137 getAlgorithmParameters("PBEWithSHA1AndRC2_40");
1138 DerOutputStream bytes = new DerOutputStream();
1139 AlgorithmId algId =
1140 new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);
1141 algId.encode(bytes);
1142 byte[] encodedAlgId = bytes.toByteArray();
1143
1144 try {
1145 // Use JCE
1146 SecretKey skey = getPBEKey(password);
1147 Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");
1148 cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
1149 encryptedData = cipher.doFinal(data);
1150
1151 } catch (Exception e) {
1152 IOException ioe = new IOException("Failed to encrypt" +
1153 " safe contents entry: " + e);
1154 ioe.initCause(e);
1155 throw ioe;
1156 }
1157
1158 // create EncryptedContentInfo
1159 DerOutputStream bytes2 = new DerOutputStream();
1160 bytes2.putOID(ContentInfo.DATA_OID);
1161 bytes2.write(encodedAlgId);
1162
1163 // Wrap encrypted data in a context-specific tag.
1164 DerOutputStream tmpout2 = new DerOutputStream();
1165 tmpout2.putOctetString(encryptedData);
1166 bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
1167 false, (byte)0), tmpout2);
1168
1169 // wrap EncryptedContentInfo in a Sequence
1170 DerOutputStream out = new DerOutputStream();
1171 out.write(DerValue.tag_Sequence, bytes2);
1172 return out.toByteArray();
1173 }
1174
1175 /**
1176 * Loads the keystore from the given input stream.
1177 *
1178 * <p>If a password is given, it is used to check the integrity of the
1179 * keystore data. Otherwise, the integrity of the keystore is not checked.
1180 *
1181 * @param stream the input stream from which the keystore is loaded
1182 * @param password the (optional) password used to check the integrity of
1183 * the keystore.
1184 *
1185 * @exception IOException if there is an I/O or format problem with the
1186 * keystore data
1187 * @exception NoSuchAlgorithmException if the algorithm used to check
1188 * the integrity of the keystore cannot be found
1189 * @exception CertificateException if any of the certificates in the
1190 * keystore could not be loaded
1191 */
1192 public synchronized void engineLoad(InputStream stream, char[] password)
1193 throws IOException, NoSuchAlgorithmException, CertificateException
1194 {
1195 DataInputStream dis;
1196 CertificateFactory cf = null;
1197 ByteArrayInputStream bais = null;
1198 byte[] encoded = null;
1199
1200 if (stream == null)
1201 return;
1202
1203 // reset the counter
1204 counter = 0;
1205
1206 DerValue val = new DerValue(stream);
1207 DerInputStream s = val.toDerInputStream();
1208 int version = s.getInteger();
1209
1210 if (version != VERSION_3) {
1211 throw new IOException("PKCS12 keystore not in version 3 format");
1212 }
1213
1214 entries.clear();
1215
1216 /*
1217 * Read the authSafe.
1218 */
1219 byte[] authSafeData;
1220 ContentInfo authSafe = new ContentInfo(s);
1221 ObjectIdentifier contentType = authSafe.getContentType();
1222
1223 if (contentType.equals(ContentInfo.DATA_OID)) {
1224 authSafeData = authSafe.getData();
1225 } else /* signed data */ {
1226 throw new IOException("public key protected PKCS12 not supported");
1227 }
1228
1229 DerInputStream as = new DerInputStream(authSafeData);
1230 DerValue[] safeContentsArray = as.getSequence(2);
1231 int count = safeContentsArray.length;
1232
1233 // reset the count at the start
1234 privateKeyCount = 0;
1235
1236 /*
1237 * Spin over the ContentInfos.
1238 */
1239 for (int i = 0; i < count; i++) {
1240 byte[] safeContentsData;
1241 ContentInfo safeContents;
1242 DerInputStream sci;
1243 byte[] eAlgId = null;
1244
1245 sci = new DerInputStream(safeContentsArray[i].toByteArray());
1246 safeContents = new ContentInfo(sci);
1247 contentType = safeContents.getContentType();
1248 safeContentsData = null;
1249 if (contentType.equals(ContentInfo.DATA_OID)) {
1250 safeContentsData = safeContents.getData();
1251 } else if (contentType.equals(ContentInfo.ENCRYPTED_DATA_OID)) {
1252 if (password == null) {
1253 continue;
1254 }
1255 DerInputStream edi =
1256 safeContents.getContent().toDerInputStream();
1257 int edVersion = edi.getInteger();
1258 DerValue[] seq = edi.getSequence(2);
1259 ObjectIdentifier edContentType = seq[0].getOID();
1260 eAlgId = seq[1].toByteArray();
1261 if (!seq[2].isContextSpecific((byte)0)) {
1262 throw new IOException("encrypted content not present!");
1263 }
1264 byte newTag = DerValue.tag_OctetString;
1265 if (seq[2].isConstructed())
1266 newTag |= 0x20;
1267 seq[2].resetTag(newTag);
1268 safeContentsData = seq[2].getOctetString();
1269
1270 // parse Algorithm parameters
1271 DerInputStream in = seq[1].toDerInputStream();
1272 ObjectIdentifier algOid = in.getOID();
1273 AlgorithmParameters algParams = parseAlgParameters(in);
1274
1275 try {
1276 // Use JCE
1277 SecretKey skey = getPBEKey(password);
1278 Cipher cipher = Cipher.getInstance(algOid.toString());
1279 cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
1280 safeContentsData = cipher.doFinal(safeContentsData);
1281
1282 } catch (Exception e) {
1283 IOException ioe = new IOException("failed to decrypt safe"
1284 + " contents entry: " + e);
1285 ioe.initCause(e);
1286 throw ioe;
1287 }
1288 } else {
1289 throw new IOException("public key protected PKCS12" +
1290 " not supported");
1291 }
1292 DerInputStream sc = new DerInputStream(safeContentsData);
1293 loadSafeContents(sc, password);
1294 }
1295
1296 // The MacData is optional.
1297 if (password != null && s.available() > 0) {
1298 MacData macData = new MacData(s);
1299 try {
1300 String algName = macData.getDigestAlgName().toUpperCase();
1301 if (algName.equals("SHA") ||
1302 algName.equals("SHA1") ||
1303 algName.equals("SHA-1")) {
1304 algName = "SHA1";
1305 }
1306
1307 // generate MAC (MAC key is created within JCE)
1308 Mac m = Mac.getInstance("HmacPBE" + algName);
1309 PBEParameterSpec params =
1310 new PBEParameterSpec(macData.getSalt(),
1311 macData.getIterations());
1312 SecretKey key = getPBEKey(password);
1313 m.init(key, params);
1314 m.update(authSafeData);
1315 byte[] macResult = m.doFinal();
1316
1317 if (!Arrays.equals(macData.getDigest(), macResult)) {
1318 throw new SecurityException("Failed PKCS12" +
1319 " integrity checking");
1320 }
1321 } catch (Exception e) {
1322 IOException ioe =
1323 new IOException("Integrity check failed: " + e);
1324 ioe.initCause(e);
1325 throw ioe;
1326 }
1327 }
1328
1329 /*
1330 * Match up private keys with certificate chains.
1331 */
1332 KeyEntry[] list = keyList.toArray(new KeyEntry[keyList.size()]);
1333 for (int m = 0; m < list.length; m++) {
1334 KeyEntry entry = list[m];
1335 if (entry.keyId != null) {
1336 ArrayList<X509Certificate> chain =
1337 new ArrayList<X509Certificate>();
1338 X509Certificate cert = findMatchedCertificate(entry);
1339 while (cert != null) {
1340 chain.add(cert);
1341 X500Principal issuerDN = cert.getIssuerX500Principal();
1342 if (issuerDN.equals(cert.getSubjectX500Principal())) {
1343 break;
1344 }
1345 cert = certsMap.get(issuerDN);
1346 }
1347 /* Update existing KeyEntry in entries table */
1348 if (chain.size() > 0)
1349 entry.chain = chain.toArray(new Certificate[chain.size()]);
1350 }
1351 }
1352 certEntries.clear();
1353 certsMap.clear();
1354 keyList.clear();
1355 }
1356
1357 /**
1358 * Locates a matched CertEntry from certEntries, and returns its cert.
1359 * @param entry the KeyEntry to match
1360 * @return a certificate, null if not found
1361 */
1362 private X509Certificate findMatchedCertificate(KeyEntry entry) {
1363 CertEntry keyIdMatch = null;
1364 CertEntry aliasMatch = null;
1365 for (CertEntry ce: certEntries) {
1366 if (Arrays.equals(entry.keyId, ce.keyId)) {
1367 keyIdMatch = ce;
1368 if (entry.alias.equalsIgnoreCase(ce.alias)) {
1369 // Full match!
1370 return ce.cert;
1371 }
1372 } else if (entry.alias.equalsIgnoreCase(ce.alias)) {
1373 aliasMatch = ce;
1374 }
1375 }
1376 // keyId match first, for compatibility
1377 if (keyIdMatch != null) return keyIdMatch.cert;
1378 else if (aliasMatch != null) return aliasMatch.cert;
1379 else return null;
1380 }
1381
1382 private void loadSafeContents(DerInputStream stream, char[] password)
1383 throws IOException, NoSuchAlgorithmException, CertificateException
1384 {
1385 DerValue[] safeBags = stream.getSequence(2);
1386 int count = safeBags.length;
1387
1388 /*
1389 * Spin over the SafeBags.
1390 */
1391 for (int i = 0; i < count; i++) {
1392 ObjectIdentifier bagId;
1393 DerInputStream sbi;
1394 DerValue bagValue;
1395 Object bagItem = null;
1396
1397 sbi = safeBags[i].toDerInputStream();
1398 bagId = sbi.getOID();
1399 bagValue = sbi.getDerValue();
1400 if (!bagValue.isContextSpecific((byte)0)) {
1401 throw new IOException("unsupported PKCS12 bag value type "
1402 + bagValue.tag);
1403 }
1404 bagValue = bagValue.data.getDerValue();
1405 if (bagId.equals(PKCS8ShroudedKeyBag_OID)) {
1406 KeyEntry kEntry = new KeyEntry();
1407 kEntry.protectedPrivKey = bagValue.toByteArray();
1408 bagItem = kEntry;
1409 privateKeyCount++;
1410 } else if (bagId.equals(CertBag_OID)) {
1411 DerInputStream cs = new DerInputStream(bagValue.toByteArray());
1412 DerValue[] certValues = cs.getSequence(2);
1413 ObjectIdentifier certId = certValues[0].getOID();
1414 if (!certValues[1].isContextSpecific((byte)0)) {
1415 throw new IOException("unsupported PKCS12 cert value type "
1416 + certValues[1].tag);
1417 }
1418 DerValue certValue = certValues[1].data.getDerValue();
1419 CertificateFactory cf = CertificateFactory.getInstance("X509");
1420 X509Certificate cert;
1421 cert = (X509Certificate)cf.generateCertificate
1422 (new ByteArrayInputStream(certValue.getOctetString()));
1423 bagItem = cert;
1424 } else {
1425 // log error message for "unsupported PKCS12 bag type"
1426 }
1427
1428 DerValue[] attrSet;
1429 try {
1430 attrSet = sbi.getSet(2);
1431 } catch (IOException e) {
1432 // entry does not have attributes
1433 // Note: CA certs can have no attributes
1434 // OpenSSL generates pkcs12 with no attr for CA certs.
1435 attrSet = null;
1436 }
1437
1438 String alias = null;
1439 byte[] keyId = null;
1440
1441 if (attrSet != null) {
1442 for (int j = 0; j < attrSet.length; j++) {
1443 DerInputStream as =
1444 new DerInputStream(attrSet[j].toByteArray());
1445 DerValue[] attrSeq = as.getSequence(2);
1446 ObjectIdentifier attrId = attrSeq[0].getOID();
1447 DerInputStream vs =
1448 new DerInputStream(attrSeq[1].toByteArray());
1449 DerValue[] valSet;
1450 try {
1451 valSet = vs.getSet(1);
1452 } catch (IOException e) {
1453 throw new IOException("Attribute " + attrId +
1454 " should have a value " + e.getMessage());
1455 }
1456 if (attrId.equals(PKCS9FriendlyName_OID)) {
1457 alias = valSet[0].getBMPString();
1458 } else if (attrId.equals(PKCS9LocalKeyId_OID)) {
1459 keyId = valSet[0].getOctetString();
1460 } else {
1461 // log error message for "unknown attr"
1462 }
1463 }
1464 }
1465
1466 /*
1467 * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId)
1468 * are optional PKCS12 bagAttributes. But entries in the keyStore
1469 * are identified by their alias. Hence we need to have an
1470 * Unfriendlyname in the alias, if alias is null. The keyId
1471 * attribute is required to match the private key with the
1472 * certificate. If we get a bagItem of type KeyEntry with a
1473 * null keyId, we should skip it entirely.
1474 */
1475 if (bagItem instanceof KeyEntry) {
1476 KeyEntry entry = (KeyEntry)bagItem;
1477 if (keyId == null) {
1478 // Insert a localKeyID for the privateKey
1479 // Note: This is a workaround to allow null localKeyID
1480 // attribute in pkcs12 with one private key entry and
1481 // associated cert-chain
1482 if (privateKeyCount == 1) {
1483 keyId = "01".getBytes("UTF8");
1484 } else {
1485 continue;
1486 }
1487 }
1488 entry.keyId = keyId;
1489 // restore date if it exists
1490 String keyIdStr = new String(keyId, "UTF8");
1491 Date date = null;
1492 if (keyIdStr.startsWith("Time ")) {
1493 try {
1494 date = new Date(
1495 Long.parseLong(keyIdStr.substring(5)));
1496 } catch (Exception e) {
1497 date = null;
1498 }
1499 }
1500 if (date == null) {
1501 date = new Date();
1502 }
1503 entry.date = date;
1504 keyList.add(entry);
1505 if (alias == null)
1506 alias = getUnfriendlyName();
1507 entry.alias = alias;
1508 entries.put(alias.toLowerCase(), entry);
1509 } else if (bagItem instanceof X509Certificate) {
1510 X509Certificate cert = (X509Certificate)bagItem;
1511 // Insert a localKeyID for the corresponding cert
1512 // Note: This is a workaround to allow null localKeyID
1513 // attribute in pkcs12 with one private key entry and
1514 // associated cert-chain
1515 if ((keyId == null) && (privateKeyCount == 1)) {
1516 // insert localKeyID only for EE cert or self-signed cert
1517 if (i == 0) {
1518 keyId = "01".getBytes("UTF8");
1519 }
1520 }
1521 certEntries.add(new CertEntry(cert, keyId, alias));
1522 X500Principal subjectDN = cert.getSubjectX500Principal();
1523 if (subjectDN != null) {
1524 if (!certsMap.containsKey(subjectDN)) {
1525 certsMap.put(subjectDN, cert);
1526 }
1527 }
1528 }
1529 }
1530 }
1531
1532 private String getUnfriendlyName() {
1533 counter++;
1534 return (String.valueOf(counter));
1535 }
1536}