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+
26+package sun.security.pkcs12;
27+
28+import java.io.*;
29+import java.security.MessageDigest;
30+import java.security.NoSuchAlgorithmException;
31+import java.security.Key;
32+import java.security.KeyFactory;
33+import java.security.PrivateKey;
34+import java.security.KeyStoreSpi;
35+import java.security.KeyStoreException;
36+import java.security.UnrecoverableKeyException;
37+import java.security.SecureRandom;
38+import java.security.cert.Certificate;
39+import java.security.cert.CertificateFactory;
40+import java.security.cert.X509Certificate;
41+import java.security.cert.CertificateException;
42+import java.security.spec.PKCS8EncodedKeySpec;
43+import java.util.*;
44+import java.math.*;
45+
46+import java.security.AlgorithmParameters;
47+import java.security.spec.AlgorithmParameterSpec;
48+import javax.crypto.spec.PBEParameterSpec;
49+import javax.crypto.spec.PBEKeySpec;
50+import javax.crypto.spec.SecretKeySpec;
51+import javax.crypto.SecretKeyFactory;
52+import javax.crypto.SecretKey;
53+import javax.crypto.Cipher;
54+import javax.crypto.Mac;
55+import javax.security.auth.x500.X500Principal;
56+
57+import sun.security.util.DerInputStream;
58+import sun.security.util.DerOutputStream;
59+import sun.security.util.DerValue;
60+import sun.security.util.ObjectIdentifier;
61+import sun.security.pkcs.ContentInfo;
62+import sun.security.x509.AlgorithmId;
63+import 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+ */
121+public 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+}