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