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