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