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