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