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