1 /*
   2  * Copyright (c) 1999, 2014, 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             int chainLen = 1;
1646             Certificate[] certs = null;
1647 
1648             if (entry instanceof PrivateKeyEntry) {
1649                 PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
1650                     if (keyEntry.chain == null) {
1651                         chainLen = 0;
1652                     } else {
1653                         chainLen = keyEntry.chain.length;
1654                     }
1655                 certs = keyEntry.chain;
1656 
1657             } else if (entry instanceof CertEntry) {
1658                certs = new Certificate[]{((CertEntry) entry).cert};
1659             }
1660 
1661             for (int i = 0; i < chainLen; i++) {
1662                 // create SafeBag of Type CertBag
1663                 DerOutputStream safeBag = new DerOutputStream();
1664                 safeBag.putOID(CertBag_OID);
1665 
1666                 // create a CertBag
1667                 DerOutputStream certBag = new DerOutputStream();
1668                 certBag.putOID(PKCS9CertType_OID);
1669 
1670                 // write encoded certs in a context-specific tag
1671                 DerOutputStream certValue = new DerOutputStream();
1672                 X509Certificate cert = (X509Certificate) certs[i];
1673                 certValue.putOctetString(cert.getEncoded());
1674                 certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1675                                         true, (byte) 0), certValue);
1676 
1677                 // wrap CertBag in a Sequence
1678                 DerOutputStream certout = new DerOutputStream();
1679                 certout.write(DerValue.tag_Sequence, certBag);
1680                 byte[] certBagValue = certout.toByteArray();
1681 
1682                 // Wrap the CertBag encoding in a context-specific tag.
1683                 DerOutputStream bagValue = new DerOutputStream();
1684                 bagValue.write(certBagValue);
1685                 // write SafeBag Value
1686                 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1687                                 true, (byte) 0), bagValue);
1688 
1689                 // write SafeBag Attributes
1690                 // All Certs should have a unique friendlyName.
1691                 // This change is made to meet NSS requirements.
1692                 byte[] bagAttrs = null;
1693                 if (i == 0) {
1694                     // Only End-Entity Cert should have a localKeyId.
1695                     if (entry instanceof KeyEntry) {
1696                         KeyEntry keyEntry = (KeyEntry) entry;
1697                         bagAttrs =
1698                             getBagAttributes(keyEntry.alias, keyEntry.keyId,
1699                                 keyEntry.attributes);
1700                     } else {
1701                         CertEntry certEntry = (CertEntry) entry;
1702                         bagAttrs =
1703                             getBagAttributes(certEntry.alias, certEntry.keyId,
1704                                 certEntry.trustedKeyUsage,
1705                                 certEntry.attributes);
1706                     }
1707                 } else {
1708                     // Trusted root CA certs and Intermediate CA certs do not
1709                     // need to have a localKeyId, and hence localKeyId is null
1710                     // This change is made to meet NSS/Netscape requirements.
1711                     // NSS pkcs12 library requires trusted CA certs in the
1712                     // certificate chain to have unique or null localKeyID.
1713                     // However, IE/OpenSSL do not impose this restriction.
1714                     bagAttrs = getBagAttributes(
1715                             cert.getSubjectX500Principal().getName(), null,
1716                             entry.attributes);
1717                 }
1718                 if (bagAttrs != null) {
1719                     safeBag.write(bagAttrs);
1720                 }
1721 
1722                 // wrap as Sequence
1723                 out.write(DerValue.tag_Sequence, safeBag);
1724             } // for cert-chain
1725         }
1726 
1727         // wrap as SequenceOf SafeBag
1728         DerOutputStream safeBagValue = new DerOutputStream();
1729         safeBagValue.write(DerValue.tag_SequenceOf, out);
1730         byte[] safeBagData = safeBagValue.toByteArray();
1731 
1732         // encrypt the content (EncryptedContentInfo)
1733         byte[] encrContentInfo = encryptContent(safeBagData, password);
1734 
1735         // -- SEQUENCE of EncryptedData
1736         DerOutputStream encrData = new DerOutputStream();
1737         DerOutputStream encrDataContent = new DerOutputStream();
1738         encrData.putInteger(0);
1739         encrData.write(encrContentInfo);
1740         encrDataContent.write(DerValue.tag_Sequence, encrData);
1741         return encrDataContent.toByteArray();
1742     }
1743 
1744     /*
1745      * Create SafeContent Data content type.
1746      * Includes encrypted secret key in a SafeBag of type SecretBag.
1747      * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.
1748      * Each PKCS8ShroudedKeyBag includes pkcs12 attributes
1749      * (see comments in getBagAttributes)
1750      */
1751     private byte[] createSafeContent()
1752         throws CertificateException, IOException {
1753 
1754         DerOutputStream out = new DerOutputStream();
1755         for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1756 
1757             String alias = e.nextElement();
1758             Entry entry = entries.get(alias);
1759             if (entry == null || (!(entry instanceof KeyEntry))) {
1760                 continue;
1761             }
1762             DerOutputStream safeBag = new DerOutputStream();
1763             KeyEntry keyEntry = (KeyEntry) entry;
1764 
1765             // DER encode the private key
1766             if (keyEntry instanceof PrivateKeyEntry) {
1767                 // Create SafeBag of type pkcs8ShroudedKeyBag
1768                 safeBag.putOID(PKCS8ShroudedKeyBag_OID);
1769 
1770                 // get the encrypted private key
1771                 byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey;
1772                 EncryptedPrivateKeyInfo encrInfo = null;
1773                 try {
1774                     encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
1775 
1776                 } catch (IOException ioe) {
1777                     throw new IOException("Private key not stored as "
1778                             + "PKCS#8 EncryptedPrivateKeyInfo"
1779                             + ioe.getMessage());
1780                 }
1781 
1782                 // Wrap the EncryptedPrivateKeyInfo in a context-specific tag.
1783                 DerOutputStream bagValue = new DerOutputStream();
1784                 bagValue.write(encrInfo.getEncoded());
1785                 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1786                                 true, (byte) 0), bagValue);
1787 
1788             // DER encode the secret key
1789             } else if (keyEntry instanceof SecretKeyEntry) {
1790                 // Create SafeBag of type SecretBag
1791                 safeBag.putOID(SecretBag_OID);
1792 
1793                 // Create a SecretBag
1794                 DerOutputStream secretBag = new DerOutputStream();
1795                 secretBag.putOID(PKCS8ShroudedKeyBag_OID);
1796 
1797                 // Write secret key in a context-specific tag
1798                 DerOutputStream secretKeyValue = new DerOutputStream();
1799                 secretKeyValue.putOctetString(
1800                     ((SecretKeyEntry) keyEntry).protectedSecretKey);
1801                 secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1802                                         true, (byte) 0), secretKeyValue);
1803 
1804                 // Wrap SecretBag in a Sequence
1805                 DerOutputStream secretBagSeq = new DerOutputStream();
1806                 secretBagSeq.write(DerValue.tag_Sequence, secretBag);
1807                 byte[] secretBagValue = secretBagSeq.toByteArray();
1808 
1809                 // Wrap the secret bag in a context-specific tag.
1810                 DerOutputStream bagValue = new DerOutputStream();
1811                 bagValue.write(secretBagValue);
1812 
1813                 // Write SafeBag value
1814                 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1815                                     true, (byte) 0), bagValue);
1816             } else {
1817                 continue; // skip this entry
1818             }
1819 
1820             // write SafeBag Attributes
1821             byte[] bagAttrs =
1822                 getBagAttributes(alias, entry.keyId, entry.attributes);
1823             safeBag.write(bagAttrs);
1824 
1825             // wrap as Sequence
1826             out.write(DerValue.tag_Sequence, safeBag);
1827         }
1828 
1829         // wrap as Sequence
1830         DerOutputStream safeBagValue = new DerOutputStream();
1831         safeBagValue.write(DerValue.tag_Sequence, out);
1832         return safeBagValue.toByteArray();
1833     }
1834 
1835 
1836     /*
1837      * Encrypt the contents using Password-based (PBE) encryption
1838      * as defined in PKCS #5.
1839      *
1840      * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used
1841      *       to derive the key and IV.
1842      *
1843      * @return encrypted contents encoded as EncryptedContentInfo
1844      */
1845     private byte[] encryptContent(byte[] data, char[] password)
1846         throws IOException {
1847 
1848         byte[] encryptedData = null;
1849 
1850         // create AlgorithmParameters
1851         AlgorithmParameters algParams =
1852                 getAlgorithmParameters("PBEWithSHA1AndRC2_40");
1853         DerOutputStream bytes = new DerOutputStream();
1854         AlgorithmId algId =
1855                 new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);
1856         algId.encode(bytes);
1857         byte[] encodedAlgId = bytes.toByteArray();
1858 
1859         try {
1860             // Use JCE
1861             SecretKey skey = getPBEKey(password);
1862             Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");
1863             cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
1864             encryptedData = cipher.doFinal(data);
1865 
1866             if (debug != null) {
1867                 debug.println("  (Cipher algorithm: " + cipher.getAlgorithm() +
1868                     ")");
1869             }
1870 
1871         } catch (Exception e) {
1872             throw new IOException("Failed to encrypt" +
1873                     " safe contents entry: " + e, e);
1874         }
1875 
1876         // create EncryptedContentInfo
1877         DerOutputStream bytes2 = new DerOutputStream();
1878         bytes2.putOID(ContentInfo.DATA_OID);
1879         bytes2.write(encodedAlgId);
1880 
1881         // Wrap encrypted data in a context-specific tag.
1882         DerOutputStream tmpout2 = new DerOutputStream();
1883         tmpout2.putOctetString(encryptedData);
1884         bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
1885                                         false, (byte)0), tmpout2);
1886 
1887         // wrap EncryptedContentInfo in a Sequence
1888         DerOutputStream out = new DerOutputStream();
1889         out.write(DerValue.tag_Sequence, bytes2);
1890         return out.toByteArray();
1891     }
1892 
1893     /**
1894      * Loads the keystore from the given input stream.
1895      *
1896      * <p>If a password is given, it is used to check the integrity of the
1897      * keystore data. Otherwise, the integrity of the keystore is not checked.
1898      *
1899      * @param stream the input stream from which the keystore is loaded
1900      * @param password the (optional) password used to check the integrity of
1901      * the keystore.
1902      *
1903      * @exception IOException if there is an I/O or format problem with the
1904      * keystore data
1905      * @exception NoSuchAlgorithmException if the algorithm used to check
1906      * the integrity of the keystore cannot be found
1907      * @exception CertificateException if any of the certificates in the
1908      * keystore could not be loaded
1909      */
1910     public synchronized void engineLoad(InputStream stream, char[] password)
1911         throws IOException, NoSuchAlgorithmException, CertificateException
1912     {
1913         DataInputStream dis;
1914         CertificateFactory cf = null;
1915         ByteArrayInputStream bais = null;
1916         byte[] encoded = null;
1917 
1918         if (stream == null)
1919            return;
1920 
1921         // reset the counter
1922         counter = 0;
1923 
1924         DerValue val = new DerValue(stream);
1925         DerInputStream s = val.toDerInputStream();
1926         int version = s.getInteger();
1927 
1928         if (version != VERSION_3) {
1929            throw new IOException("PKCS12 keystore not in version 3 format");
1930         }
1931 
1932         entries.clear();
1933 
1934         /*
1935          * Read the authSafe.
1936          */
1937         byte[] authSafeData;
1938         ContentInfo authSafe = new ContentInfo(s);
1939         ObjectIdentifier contentType = authSafe.getContentType();
1940 
1941         if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1942            authSafeData = authSafe.getData();
1943         } else /* signed data */ {
1944            throw new IOException("public key protected PKCS12 not supported");
1945         }
1946 
1947         DerInputStream as = new DerInputStream(authSafeData);
1948         DerValue[] safeContentsArray = as.getSequence(2);
1949         int count = safeContentsArray.length;
1950 
1951         // reset the counters at the start
1952         privateKeyCount = 0;
1953         secretKeyCount = 0;
1954         certificateCount = 0;
1955 
1956         /*
1957          * Spin over the ContentInfos.
1958          */
1959         for (int i = 0; i < count; i++) {
1960             byte[] safeContentsData;
1961             ContentInfo safeContents;
1962             DerInputStream sci;
1963             byte[] eAlgId = null;
1964 
1965             sci = new DerInputStream(safeContentsArray[i].toByteArray());
1966             safeContents = new ContentInfo(sci);
1967             contentType = safeContents.getContentType();
1968             safeContentsData = null;
1969             if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1970 
1971                 if (debug != null) {
1972                     debug.println("Loading PKCS#7 data content-type");
1973                 }
1974 
1975                 safeContentsData = safeContents.getData();
1976             } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) {
1977                 if (password == null) {
1978 
1979                     if (debug != null) {
1980                         debug.println("Warning: skipping PKCS#7 encryptedData" +
1981                             " content-type - no password was supplied");
1982                     }
1983                     continue;
1984                 }
1985 
1986                 if (debug != null) {
1987                     debug.println("Loading PKCS#7 encryptedData content-type");
1988                 }
1989 
1990                 DerInputStream edi =
1991                                 safeContents.getContent().toDerInputStream();
1992                 int edVersion = edi.getInteger();
1993                 DerValue[] seq = edi.getSequence(2);
1994                 ObjectIdentifier edContentType = seq[0].getOID();
1995                 eAlgId = seq[1].toByteArray();
1996                 if (!seq[2].isContextSpecific((byte)0)) {
1997                    throw new IOException("encrypted content not present!");
1998                 }
1999                 byte newTag = DerValue.tag_OctetString;
2000                 if (seq[2].isConstructed())
2001                    newTag |= 0x20;
2002                 seq[2].resetTag(newTag);
2003                 safeContentsData = seq[2].getOctetString();
2004 
2005                 // parse Algorithm parameters
2006                 DerInputStream in = seq[1].toDerInputStream();
2007                 ObjectIdentifier algOid = in.getOID();
2008                 AlgorithmParameters algParams = parseAlgParameters(algOid, in);
2009 
2010                 while (true) {
2011                     try {
2012                         // Use JCE
2013                         SecretKey skey = getPBEKey(password);
2014                         Cipher cipher = Cipher.getInstance(algOid.toString());
2015                         cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
2016                         safeContentsData = cipher.doFinal(safeContentsData);
2017                         break;
2018                     } catch (Exception e) {
2019                         if (password.length == 0) {
2020                             // Retry using an empty password
2021                             // without a NULL terminator.
2022                             password = new char[1];
2023                             continue;
2024                         }
2025                         throw new IOException("keystore password was incorrect",
2026                             new UnrecoverableKeyException(
2027                                 "failed to decrypt safe contents entry: " + e));
2028                     }
2029                 }
2030             } else {
2031                 throw new IOException("public key protected PKCS12" +
2032                                         " not supported");
2033             }
2034             DerInputStream sc = new DerInputStream(safeContentsData);
2035             loadSafeContents(sc, password);
2036         }
2037 
2038         // The MacData is optional.
2039         if (password != null && s.available() > 0) {
2040            MacData macData = new MacData(s);
2041            try {
2042                 String algName =
2043                         macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
2044 
2045                 // Change SHA-1 to SHA1
2046                 algName = algName.replace("-", "");
2047 
2048                 // generate MAC (MAC key is created within JCE)
2049                 Mac m = Mac.getInstance("HmacPBE" + algName);
2050                 PBEParameterSpec params =
2051                         new PBEParameterSpec(macData.getSalt(),
2052                                         macData.getIterations());
2053                 SecretKey key = getPBEKey(password);
2054                 m.init(key, params);
2055                 m.update(authSafeData);
2056                 byte[] macResult = m.doFinal();
2057 
2058                 if (debug != null) {
2059                     debug.println("Checking keystore integrity " +
2060                         "(MAC algorithm: " + m.getAlgorithm() + ")");
2061                 }
2062 
2063                 if (!Arrays.equals(macData.getDigest(), macResult)) {
2064                    throw new SecurityException("Failed PKCS12" +
2065                                         " integrity checking");
2066                 }
2067            } catch (Exception e) {
2068                 throw new IOException("Integrity check failed: " + e, e);
2069            }
2070         }
2071 
2072         /*
2073          * Match up private keys with certificate chains.
2074          */
2075         PrivateKeyEntry[] list =
2076             keyList.toArray(new PrivateKeyEntry[keyList.size()]);
2077         for (int m = 0; m < list.length; m++) {
2078             PrivateKeyEntry entry = list[m];
2079             if (entry.keyId != null) {
2080                 ArrayList<X509Certificate> chain =
2081                                 new ArrayList<X509Certificate>();
2082                 X509Certificate cert = findMatchedCertificate(entry);
2083 
2084                 mainloop:
2085                 while (cert != null) {
2086                     // Check for loops in the certificate chain
2087                     if (!chain.isEmpty()) {
2088                         for (X509Certificate chainCert : chain) {
2089                             if (cert.equals(chainCert)) {
2090                                 if (debug != null) {
2091                                     debug.println("Loop detected in " +
2092                                         "certificate chain. Skip adding " +
2093                                         "repeated cert to chain. Subject: " +
2094                                         cert.getSubjectX500Principal()
2095                                             .toString());
2096                                 }
2097                                 break mainloop;
2098                             }
2099                         }
2100                     }
2101                     chain.add(cert);
2102                     X500Principal issuerDN = cert.getIssuerX500Principal();
2103                     if (issuerDN.equals(cert.getSubjectX500Principal())) {
2104                         break;
2105                     }
2106                     cert = certsMap.get(issuerDN);
2107                 }
2108                 /* Update existing KeyEntry in entries table */
2109                 if (chain.size() > 0)
2110                     entry.chain = chain.toArray(new Certificate[chain.size()]);
2111             }
2112         }
2113 
2114         if (debug != null) {
2115             if (privateKeyCount > 0) {
2116                 debug.println("Loaded " + privateKeyCount +
2117                     " protected private key(s)");
2118             }
2119             if (secretKeyCount > 0) {
2120                 debug.println("Loaded " + secretKeyCount +
2121                     " protected secret key(s)");
2122             }
2123             if (certificateCount > 0) {
2124                 debug.println("Loaded " + certificateCount +
2125                     " certificate(s)");
2126             }
2127         }
2128 
2129         certEntries.clear();
2130         certsMap.clear();
2131         keyList.clear();
2132     }
2133 
2134     /**
2135      * Locates a matched CertEntry from certEntries, and returns its cert.
2136      * @param entry the KeyEntry to match
2137      * @return a certificate, null if not found
2138      */
2139     private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
2140         CertEntry keyIdMatch = null;
2141         CertEntry aliasMatch = null;
2142         for (CertEntry ce: certEntries) {
2143             if (Arrays.equals(entry.keyId, ce.keyId)) {
2144                 keyIdMatch = ce;
2145                 if (entry.alias.equalsIgnoreCase(ce.alias)) {
2146                     // Full match!
2147                     return ce.cert;
2148                 }
2149             } else if (entry.alias.equalsIgnoreCase(ce.alias)) {
2150                 aliasMatch = ce;
2151             }
2152         }
2153         // keyId match first, for compatibility
2154         if (keyIdMatch != null) return keyIdMatch.cert;
2155         else if (aliasMatch != null) return aliasMatch.cert;
2156         else return null;
2157     }
2158 
2159     private void loadSafeContents(DerInputStream stream, char[] password)
2160         throws IOException, NoSuchAlgorithmException, CertificateException
2161     {
2162         DerValue[] safeBags = stream.getSequence(2);
2163         int count = safeBags.length;
2164 
2165         /*
2166          * Spin over the SafeBags.
2167          */
2168         for (int i = 0; i < count; i++) {
2169             ObjectIdentifier bagId;
2170             DerInputStream sbi;
2171             DerValue bagValue;
2172             Object bagItem = null;
2173 
2174             sbi = safeBags[i].toDerInputStream();
2175             bagId = sbi.getOID();
2176             bagValue = sbi.getDerValue();
2177             if (!bagValue.isContextSpecific((byte)0)) {
2178                 throw new IOException("unsupported PKCS12 bag value type "
2179                                         + bagValue.tag);
2180             }
2181             bagValue = bagValue.data.getDerValue();
2182             if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) {
2183                 PrivateKeyEntry kEntry = new PrivateKeyEntry();
2184                 kEntry.protectedPrivKey = bagValue.toByteArray();
2185                 bagItem = kEntry;
2186                 privateKeyCount++;
2187             } else if (bagId.equals((Object)CertBag_OID)) {
2188                 DerInputStream cs = new DerInputStream(bagValue.toByteArray());
2189                 DerValue[] certValues = cs.getSequence(2);
2190                 ObjectIdentifier certId = certValues[0].getOID();
2191                 if (!certValues[1].isContextSpecific((byte)0)) {
2192                     throw new IOException("unsupported PKCS12 cert value type "
2193                                         + certValues[1].tag);
2194                 }
2195                 DerValue certValue = certValues[1].data.getDerValue();
2196                 CertificateFactory cf = CertificateFactory.getInstance("X509");
2197                 X509Certificate cert;
2198                 cert = (X509Certificate)cf.generateCertificate
2199                         (new ByteArrayInputStream(certValue.getOctetString()));
2200                 bagItem = cert;
2201                 certificateCount++;
2202             } else if (bagId.equals((Object)SecretBag_OID)) {
2203                 DerInputStream ss = new DerInputStream(bagValue.toByteArray());
2204                 DerValue[] secretValues = ss.getSequence(2);
2205                 ObjectIdentifier secretId = secretValues[0].getOID();
2206                 if (!secretValues[1].isContextSpecific((byte)0)) {
2207                     throw new IOException(
2208                         "unsupported PKCS12 secret value type "
2209                                         + secretValues[1].tag);
2210                 }
2211                 DerValue secretValue = secretValues[1].data.getDerValue();
2212                 SecretKeyEntry kEntry = new SecretKeyEntry();
2213                 kEntry.protectedSecretKey = secretValue.getOctetString();
2214                 bagItem = kEntry;
2215                 secretKeyCount++;
2216             } else {
2217 
2218                 if (debug != null) {
2219                     debug.println("Unsupported PKCS12 bag type: " + bagId);
2220                 }
2221             }
2222 
2223             DerValue[] attrSet;
2224             try {
2225                 attrSet = sbi.getSet(3);
2226             } catch (IOException e) {
2227                 // entry does not have attributes
2228                 // Note: CA certs can have no attributes
2229                 // OpenSSL generates pkcs12 with no attr for CA certs.
2230                 attrSet = null;
2231             }
2232 
2233             String alias = null;
2234             byte[] keyId = null;
2235             ObjectIdentifier[] trustedKeyUsage = null;
2236             Set<PKCS12Attribute> attributes = new HashSet<>();
2237 
2238             if (attrSet != null) {
2239                 for (int j = 0; j < attrSet.length; j++) {
2240                     byte[] encoded = attrSet[j].toByteArray();
2241                     DerInputStream as = new DerInputStream(encoded);
2242                     DerValue[] attrSeq = as.getSequence(2);
2243                     ObjectIdentifier attrId = attrSeq[0].getOID();
2244                     DerInputStream vs =
2245                         new DerInputStream(attrSeq[1].toByteArray());
2246                     DerValue[] valSet;
2247                     try {
2248                         valSet = vs.getSet(1);
2249                     } catch (IOException e) {
2250                         throw new IOException("Attribute " + attrId +
2251                                 " should have a value " + e.getMessage());
2252                     }
2253                     if (attrId.equals((Object)PKCS9FriendlyName_OID)) {
2254                         alias = valSet[0].getBMPString();
2255                     } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) {
2256                         keyId = valSet[0].getOctetString();
2257                     } else if
2258                         (attrId.equals((Object)TrustedKeyUsage_OID)) {
2259                         trustedKeyUsage = new ObjectIdentifier[valSet.length];
2260                         for (int k = 0; k < valSet.length; k++) {
2261                             trustedKeyUsage[k] = valSet[k].getOID();
2262                         }
2263                     } else {
2264                         attributes.add(new PKCS12Attribute(encoded));
2265                     }
2266                 }
2267             }
2268 
2269             /*
2270              * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId)
2271              * are optional PKCS12 bagAttributes. But entries in the keyStore
2272              * are identified by their alias. Hence we need to have an
2273              * Unfriendlyname in the alias, if alias is null. The keyId
2274              * attribute is required to match the private key with the
2275              * certificate. If we get a bagItem of type KeyEntry with a
2276              * null keyId, we should skip it entirely.
2277              */
2278             if (bagItem instanceof KeyEntry) {
2279                 KeyEntry entry = (KeyEntry)bagItem;
2280 
2281                 if (bagItem instanceof PrivateKeyEntry) {
2282                     if (keyId == null) {
2283                        // Insert a localKeyID for the privateKey
2284                        // Note: This is a workaround to allow null localKeyID
2285                        // attribute in pkcs12 with one private key entry and
2286                        // associated cert-chain
2287                        if (privateKeyCount == 1) {
2288                             keyId = "01".getBytes("UTF8");
2289                        } else {
2290                             continue;
2291                        }
2292                     }
2293                 }
2294                 entry.keyId = keyId;
2295                 // restore date if it exists
2296                 String keyIdStr = new String(keyId, "UTF8");
2297                 Date date = null;
2298                 if (keyIdStr.startsWith("Time ")) {
2299                     try {
2300                         date = new Date(
2301                                 Long.parseLong(keyIdStr.substring(5)));
2302                     } catch (Exception e) {
2303                         date = null;
2304                     }
2305                 }
2306                 if (date == null) {
2307                     date = new Date();
2308                 }
2309                 entry.date = date;
2310 
2311                 if (bagItem instanceof PrivateKeyEntry) {
2312                     keyList.add((PrivateKeyEntry) entry);
2313                 }
2314                 if (entry.attributes == null) {
2315                     entry.attributes = new HashSet<>();
2316                 }
2317                 entry.attributes.addAll(attributes);
2318                 if (alias == null) {
2319                    alias = getUnfriendlyName();
2320                 }
2321                 entry.alias = alias;
2322                 entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
2323 
2324             } else if (bagItem instanceof X509Certificate) {
2325                 X509Certificate cert = (X509Certificate)bagItem;
2326                 // Insert a localKeyID for the corresponding cert
2327                 // Note: This is a workaround to allow null localKeyID
2328                 // attribute in pkcs12 with one private key entry and
2329                 // associated cert-chain
2330                 if ((keyId == null) && (privateKeyCount == 1)) {
2331                     // insert localKeyID only for EE cert or self-signed cert
2332                     if (i == 0) {
2333                         keyId = "01".getBytes("UTF8");
2334                     }
2335                 }
2336                 // Trusted certificate
2337                 if (trustedKeyUsage != null) {
2338                     if (alias == null) {
2339                         alias = getUnfriendlyName();
2340                     }
2341                     CertEntry certEntry =
2342                         new CertEntry(cert, keyId, alias, trustedKeyUsage,
2343                             attributes);
2344                     entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
2345                 } else {
2346                     certEntries.add(new CertEntry(cert, keyId, alias));
2347                 }
2348                 X500Principal subjectDN = cert.getSubjectX500Principal();
2349                 if (subjectDN != null) {
2350                     if (!certsMap.containsKey(subjectDN)) {
2351                         certsMap.put(subjectDN, cert);
2352                     }
2353                 }
2354             }
2355         }
2356     }
2357 
2358     private String getUnfriendlyName() {
2359         counter++;
2360         return (String.valueOf(counter));
2361     }
2362 
2363     /*
2364      * PKCS12 permitted first 24 bytes:
2365      *
2366      * 30 82 -- -- 02 01 03 30 82 -- -- 06 09 2A 86 48 86 F7 0D 01 07 01 A0 8-
2367      * 30 -- 02 01 03 30 -- 06 09 2A 86 48 86 F7 0D 01 07 01 A0 -- 04 -- -- --
2368      * 30 81 -- 02 01 03 30 81 -- 06 09 2A 86 48 86 F7 0D 01 07 01 A0 81 -- 04
2369      * 30 82 -- -- 02 01 03 30 81 -- 06 09 2A 86 48 86 F7 0D 01 07 01 A0 81 --
2370      * 30 83 -- -- -- 02 01 03 30 82 -- -- 06 09 2A 86 48 86 F7 0D 01 07 01 A0
2371      * 30 83 -- -- -- 02 01 03 30 83 -- -- -- 06 09 2A 86 48 86 F7 0D 01 07 01
2372      * 30 84 -- -- -- -- 02 01 03 30 83 -- -- -- 06 09 2A 86 48 86 F7 0D 01 07
2373      * 30 84 -- -- -- -- 02 01 03 30 84 -- -- -- -- 06 09 2A 86 48 86 F7 0D 01
2374      */
2375 
2376     private static final long[][] PKCS12_HEADER_PATTERNS = {
2377         { 0x3082000002010330L, 0x82000006092A8648L, 0x86F70D010701A080L },
2378         { 0x3000020103300006L, 0x092A864886F70D01L, 0x0701A00004000000L },
2379         { 0x3081000201033081L, 0x0006092A864886F7L, 0x0D010701A0810004L },
2380         { 0x3082000002010330L, 0x810006092A864886L, 0xF70D010701A08100L },
2381         { 0x3083000000020103L, 0x3082000006092A86L, 0x4886F70D010701A0L },
2382         { 0x3083000000020103L, 0x308200000006092AL, 0x864886F70D010701L },
2383         { 0x3084000000000201L, 0x0330820000000609L, 0x2A864886F70D0107L },
2384         { 0x3084000000000201L, 0x0330820000000006L, 0x092A864886F70D01L }
2385     };
2386 
2387     private static final long[][] PKCS12_HEADER_MASKS = {
2388         { 0xFFFF0000FFFFFFFFL, 0xFF0000FFFFFFFFFFL, 0xFFFFFFFFFFFFFFF0L },
2389         { 0xFF00FFFFFFFF00FFL, 0xFFFFFFFFFFFFFFFFL, 0xFFFFFF00FF000000L },
2390         { 0xFFFF00FFFFFFFFFFL, 0x00FFFFFFFFFFFFFFL, 0xFFFFFFFFFFFF00FFL },
2391         { 0xFFFF0000FFFFFFFFL, 0xFF00FFFFFFFFFFFFL, 0xFFFFFFFFFFFFFF00L },
2392         { 0xFFFF000000FFFFFFL, 0xFFFF0000FFFFFFFFL, 0xFFFFFFFFFFFFFFFFL },
2393         { 0xFFFF000000FFFFFFL, 0xFFFF000000FFFFFFL, 0xFFFFFFFFFFFFFFFFL },
2394         { 0xFFFF00000000FFFFL, 0xFFFFFF000000FFFFL, 0xFFFFFFFFFFFFFFFFL },
2395         { 0xFFFF00000000FFFFL, 0xFFFFFF00000000FFL, 0xFFFFFFFFFFFFFFFFL }
2396     };
2397 
2398     /**
2399      * Probe the first few bytes of the keystore data stream for a valid
2400      * PKCS12 keystore encoding.
2401      */
2402     @Override
2403     public boolean engineProbe(InputStream stream) throws IOException {
2404 
2405         DataInputStream dataStream;
2406         if (stream instanceof DataInputStream) {
2407             dataStream = (DataInputStream)stream;
2408         } else {
2409             dataStream = new DataInputStream(stream);
2410         }
2411 
2412         long firstPeek = dataStream.readLong();
2413         long nextPeek = dataStream.readLong();
2414         long finalPeek = dataStream.readLong();
2415         boolean result = false;
2416 
2417         for (int i = 0; i < PKCS12_HEADER_PATTERNS.length; i++) {
2418             if (PKCS12_HEADER_PATTERNS[i][0] ==
2419                     (firstPeek & PKCS12_HEADER_MASKS[i][0]) &&
2420                 (PKCS12_HEADER_PATTERNS[i][1] ==
2421                     (nextPeek & PKCS12_HEADER_MASKS[i][1])) &&
2422                 (PKCS12_HEADER_PATTERNS[i][2] ==
2423                     (finalPeek & PKCS12_HEADER_MASKS[i][2]))) {
2424                 result = true;
2425                 break;
2426             }
2427         }
2428 
2429         return result;
2430     }
2431 }