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      * Returns the (alias) name of the first keystore entry whose certificate
1062      * matches the given certificate.
1063      *
1064      * <p>This method attempts to match the given certificate with each
1065      * keystore entry. If the entry being considered
1066      * is a <i>trusted certificate entry</i>, the given certificate is
1067      * compared to that entry's certificate. If the entry being considered is
1068      * a <i>key entry</i>, the given certificate is compared to the first
1069      * element of that entry's certificate chain (if a chain exists).
1070      *
1071      * @param cert the certificate to match with.
1072      *
1073      * @return the (alias) name of the first entry with matching certificate,
1074      * or null if no such entry exists in this keystore.
1075      */
1076     public String engineGetCertificateAlias(Certificate cert) {
1077         Certificate certElem = null;
1078 
1079         for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1080             String alias = e.nextElement();
1081             Entry entry = entries.get(alias);
1082             if (entry instanceof PrivateKeyEntry) {
1083                 if (((PrivateKeyEntry) entry).chain != null) {
1084                     certElem = ((PrivateKeyEntry) entry).chain[0];
1085                 }
1086             } else if (entry instanceof CertEntry &&
1087                     ((CertEntry) entry).trustedKeyUsage != null) {
1088                 certElem = ((CertEntry) entry).cert;
1089             } else {
1090                 continue;
1091             }
1092             if (certElem.equals(cert)) {
1093                 return alias;
1094             }
1095         }
1096         return null;
1097     }
1098 
1099     /**
1100      * Stores this keystore to the given output stream, and protects its
1101      * integrity with the given password.
1102      *
1103      * @param stream the output stream to which this keystore is written.
1104      * @param password the password to generate the keystore integrity check
1105      *
1106      * @exception IOException if there was an I/O problem with data
1107      * @exception NoSuchAlgorithmException if the appropriate data integrity
1108      * algorithm could not be found
1109      * @exception CertificateException if any of the certificates included in
1110      * the keystore data could not be stored
1111      */
1112     public synchronized void engineStore(OutputStream stream, char[] password)
1113         throws IOException, NoSuchAlgorithmException, CertificateException
1114     {
1115         // password is mandatory when storing
1116         if (password == null) {
1117            throw new IllegalArgumentException("password can't be null");
1118         }
1119 
1120         // -- Create PFX
1121         DerOutputStream pfx = new DerOutputStream();
1122 
1123         // PFX version (always write the latest version)
1124         DerOutputStream version = new DerOutputStream();
1125         version.putInteger(VERSION_3);
1126         byte[] pfxVersion = version.toByteArray();
1127         pfx.write(pfxVersion);
1128 
1129         // -- Create AuthSafe
1130         DerOutputStream authSafe = new DerOutputStream();
1131 
1132         // -- Create ContentInfos
1133         DerOutputStream authSafeContentInfo = new DerOutputStream();
1134 
1135         // -- create safeContent Data ContentInfo
1136         if (privateKeyCount > 0 || secretKeyCount > 0) {
1137 
1138             if (debug != null) {
1139                 debug.println("Storing " + (privateKeyCount + secretKeyCount) +
1140                     " protected key(s) in a PKCS#7 data content-type");
1141             }
1142 
1143             byte[] safeContentData = createSafeContent();
1144             ContentInfo dataContentInfo = new ContentInfo(safeContentData);
1145             dataContentInfo.encode(authSafeContentInfo);
1146         }
1147 
1148         // -- create EncryptedContentInfo
1149         if (certificateCount > 0) {
1150 
1151             if (debug != null) {
1152                 debug.println("Storing " + certificateCount +
1153                     " certificate(s) in a PKCS#7 encryptedData content-type");
1154             }
1155 
1156             byte[] encrData = createEncryptedData(password);
1157             ContentInfo encrContentInfo =
1158                 new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
1159                                 new DerValue(encrData));
1160             encrContentInfo.encode(authSafeContentInfo);
1161         }
1162 
1163         // wrap as SequenceOf ContentInfos
1164         DerOutputStream cInfo = new DerOutputStream();
1165         cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
1166         byte[] authenticatedSafe = cInfo.toByteArray();
1167 
1168         // Create Encapsulated ContentInfo
1169         ContentInfo contentInfo = new ContentInfo(authenticatedSafe);
1170         contentInfo.encode(authSafe);
1171         byte[] authSafeData = authSafe.toByteArray();
1172         pfx.write(authSafeData);
1173 
1174         // -- MAC
1175         byte[] macData = calculateMac(password, authenticatedSafe);
1176         pfx.write(macData);
1177 
1178         // write PFX to output stream
1179         DerOutputStream pfxout = new DerOutputStream();
1180         pfxout.write(DerValue.tag_Sequence, pfx);
1181         byte[] pfxData = pfxout.toByteArray();
1182         stream.write(pfxData);
1183         stream.flush();
1184     }
1185 
1186     /**
1187      * Gets a <code>KeyStore.Entry</code> for the specified alias
1188      * with the specified protection parameter.
1189      *
1190      * @param alias get the <code>KeyStore.Entry</code> for this alias
1191      * @param protParam the <code>ProtectionParameter</code>
1192      *          used to protect the <code>Entry</code>,
1193      *          which may be <code>null</code>
1194      *
1195      * @return the <code>KeyStore.Entry</code> for the specified alias,
1196      *          or <code>null</code> if there is no such entry
1197      *
1198      * @exception KeyStoreException if the operation failed
1199      * @exception NoSuchAlgorithmException if the algorithm for recovering the
1200      *          entry cannot be found
1201      * @exception UnrecoverableEntryException if the specified
1202      *          <code>protParam</code> were insufficient or invalid
1203      * @exception UnrecoverableKeyException if the entry is a
1204      *          <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>
1205      *          and the specified <code>protParam</code> does not contain
1206      *          the information needed to recover the key (e.g. wrong password)
1207      *
1208      * @since 1.5
1209      */
1210     @Override
1211     public KeyStore.Entry engineGetEntry(String alias,
1212                         KeyStore.ProtectionParameter protParam)
1213                 throws KeyStoreException, NoSuchAlgorithmException,
1214                 UnrecoverableEntryException {
1215 
1216         if (!engineContainsAlias(alias)) {
1217             return null;
1218         }
1219 
1220         Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
1221         if (protParam == null) {
1222             if (engineIsCertificateEntry(alias)) {
1223                 if (entry instanceof CertEntry &&
1224                     ((CertEntry) entry).trustedKeyUsage != null) {
1225 
1226                     if (debug != null) {
1227                         debug.println("Retrieved a trusted certificate at " +
1228                             "alias '" + alias + "'");
1229                     }
1230 
1231                     return new KeyStore.TrustedCertificateEntry(
1232                         ((CertEntry)entry).cert, getAttributes(entry));
1233                 }
1234             } else {
1235                 throw new UnrecoverableKeyException
1236                         ("requested entry requires a password");
1237             }
1238         }
1239 
1240         if (protParam instanceof KeyStore.PasswordProtection) {
1241             if (engineIsCertificateEntry(alias)) {
1242                 throw new UnsupportedOperationException
1243                     ("trusted certificate entries are not password-protected");
1244             } else if (engineIsKeyEntry(alias)) {
1245                 KeyStore.PasswordProtection pp =
1246                         (KeyStore.PasswordProtection)protParam;
1247                 char[] password = pp.getPassword();
1248 
1249                 Key key = engineGetKey(alias, password);
1250                 if (key instanceof PrivateKey) {
1251                     Certificate[] chain = engineGetCertificateChain(alias);
1252 
1253                     return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain,
1254                         getAttributes(entry));
1255 
1256                 } else if (key instanceof SecretKey) {
1257 
1258                     return new KeyStore.SecretKeyEntry((SecretKey)key,
1259                         getAttributes(entry));
1260                 }
1261             } else if (!engineIsKeyEntry(alias)) {
1262                 throw new UnsupportedOperationException
1263                     ("untrusted certificate entries are not " +
1264                         "password-protected");
1265             }
1266         }
1267 
1268         throw new UnsupportedOperationException();
1269     }
1270 
1271     /**
1272      * Saves a <code>KeyStore.Entry</code> under the specified alias.
1273      * The specified protection parameter is used to protect the
1274      * <code>Entry</code>.
1275      *
1276      * <p> If an entry already exists for the specified alias,
1277      * it is overridden.
1278      *
1279      * @param alias save the <code>KeyStore.Entry</code> under this alias
1280      * @param entry the <code>Entry</code> to save
1281      * @param protParam the <code>ProtectionParameter</code>
1282      *          used to protect the <code>Entry</code>,
1283      *          which may be <code>null</code>
1284      *
1285      * @exception KeyStoreException if this operation fails
1286      *
1287      * @since 1.5
1288      */
1289     @Override
1290     public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
1291         KeyStore.ProtectionParameter protParam) throws KeyStoreException {
1292 
1293         // get password
1294         if (protParam != null &&
1295             !(protParam instanceof KeyStore.PasswordProtection)) {
1296             throw new KeyStoreException("unsupported protection parameter");
1297         }
1298         KeyStore.PasswordProtection pProtect = null;
1299         if (protParam != null) {
1300             pProtect = (KeyStore.PasswordProtection)protParam;
1301         }
1302 
1303         // set entry
1304         if (entry instanceof KeyStore.TrustedCertificateEntry) {
1305             if (protParam != null && pProtect.getPassword() != null) {
1306                 // pre-1.5 style setCertificateEntry did not allow password
1307                 throw new KeyStoreException
1308                     ("trusted certificate entries are not password-protected");
1309             } else {
1310                 KeyStore.TrustedCertificateEntry tce =
1311                         (KeyStore.TrustedCertificateEntry)entry;
1312                 setCertEntry(alias, tce.getTrustedCertificate(),
1313                     tce.getAttributes());
1314 
1315                 return;
1316             }
1317         } else if (entry instanceof KeyStore.PrivateKeyEntry) {
1318             if (pProtect == null || pProtect.getPassword() == null) {
1319                 // pre-1.5 style setKeyEntry required password
1320                 throw new KeyStoreException
1321                     ("non-null password required to create PrivateKeyEntry");
1322             } else {
1323                 KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry;
1324                 setKeyEntry(alias, pke.getPrivateKey(), pProtect,
1325                     pke.getCertificateChain(), pke.getAttributes());
1326 
1327                 return;
1328             }
1329         } else if (entry instanceof KeyStore.SecretKeyEntry) {
1330             if (pProtect == null || pProtect.getPassword() == null) {
1331                 // pre-1.5 style setKeyEntry required password
1332                 throw new KeyStoreException
1333                     ("non-null password required to create SecretKeyEntry");
1334             } else {
1335                 KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;
1336                 setKeyEntry(alias, ske.getSecretKey(), pProtect,
1337                     (Certificate[])null, ske.getAttributes());
1338 
1339                 return;
1340             }
1341         }
1342 
1343         throw new KeyStoreException
1344                 ("unsupported entry type: " + entry.getClass().getName());
1345     }
1346 
1347     /*
1348      * Assemble the entry attributes
1349      */
1350     private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) {
1351 
1352         if (entry.attributes == null) {
1353             entry.attributes = new HashSet<>();
1354         }
1355 
1356         // friendlyName
1357         entry.attributes.add(new PKCS12Attribute(
1358             PKCS9FriendlyName_OID.toString(), entry.alias));
1359 
1360         // localKeyID
1361         byte[] keyIdValue = entry.keyId;
1362         if (keyIdValue != null) {
1363             entry.attributes.add(new PKCS12Attribute(
1364                 PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue)));
1365         }
1366 
1367         // trustedKeyUsage
1368         if (entry instanceof CertEntry) {
1369             ObjectIdentifier[] trustedKeyUsageValue =
1370                 ((CertEntry) entry).trustedKeyUsage;
1371             if (trustedKeyUsageValue != null) {
1372                 if (trustedKeyUsageValue.length == 1) { // omit brackets
1373                     entry.attributes.add(new PKCS12Attribute(
1374                         TrustedKeyUsage_OID.toString(),
1375                         trustedKeyUsageValue[0].toString()));
1376                 } else { // multi-valued
1377                     entry.attributes.add(new PKCS12Attribute(
1378                         TrustedKeyUsage_OID.toString(),
1379                         Arrays.toString(trustedKeyUsageValue)));
1380                 }
1381             }
1382         }
1383 
1384         return entry.attributes;
1385     }
1386 
1387     /*
1388      * Generate Hash.
1389      */
1390     private byte[] generateHash(byte[] data) throws IOException
1391     {
1392         byte[] digest = null;
1393 
1394         try {
1395             MessageDigest md = MessageDigest.getInstance("SHA1");
1396             md.update(data);
1397             digest = md.digest();
1398         } catch (Exception e) {
1399             throw new IOException("generateHash failed: " + e, e);
1400         }
1401         return digest;
1402     }
1403 
1404 
1405     /*
1406      * Calculate MAC using HMAC algorithm (required for password integrity)
1407      *
1408      * Hash-based MAC algorithm combines secret key with message digest to
1409      * create a message authentication code (MAC)
1410      */
1411     private byte[] calculateMac(char[] passwd, byte[] data)
1412         throws IOException
1413     {
1414         byte[] mData = null;
1415         String algName = "SHA1";
1416 
1417         try {
1418             // Generate a random salt.
1419             byte[] salt = getSalt();
1420 
1421             // generate MAC (MAC key is generated within JCE)
1422             Mac m = Mac.getInstance("HmacPBESHA1");
1423             PBEParameterSpec params =
1424                         new PBEParameterSpec(salt, iterationCount);
1425             SecretKey key = getPBEKey(passwd);
1426             m.init(key, params);
1427             m.update(data);
1428             byte[] macResult = m.doFinal();
1429 
1430             // encode as MacData
1431             MacData macData = new MacData(algName, macResult, salt,
1432                                                 iterationCount);
1433             DerOutputStream bytes = new DerOutputStream();
1434             bytes.write(macData.getEncoded());
1435             mData = bytes.toByteArray();
1436         } catch (Exception e) {
1437             throw new IOException("calculateMac failed: " + e, e);
1438         }
1439         return mData;
1440     }
1441 
1442 
1443     /*
1444      * Validate Certificate Chain
1445      */
1446     private boolean validateChain(Certificate[] certChain)
1447     {
1448         for (int i = 0; i < certChain.length-1; i++) {
1449             X500Principal issuerDN =
1450                 ((X509Certificate)certChain[i]).getIssuerX500Principal();
1451             X500Principal subjectDN =
1452                 ((X509Certificate)certChain[i+1]).getSubjectX500Principal();
1453             if (!(issuerDN.equals(subjectDN)))
1454                 return false;
1455         }
1456 
1457         // Check for loops in the chain. If there are repeated certs,
1458         // the Set of certs in the chain will contain fewer certs than
1459         // the chain
1460         Set<Certificate> set = new HashSet<>(Arrays.asList(certChain));
1461         return set.size() == certChain.length;
1462     }
1463 
1464 
1465     /*
1466      * Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage.
1467      *
1468      * Although attributes are optional, they could be required.
1469      * For e.g. localKeyId attribute is required to match the
1470      * private key with the associated end-entity certificate.
1471      * The trustedKeyUsage attribute is used to denote a trusted certificate.
1472      *
1473      * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName.
1474      * CertBags may or may not include attributes depending on the type
1475      * of Certificate. In end-entity certificates, localKeyID should be
1476      * unique, and the corresponding private key should have the same
1477      * localKeyID. For trusted CA certs in the cert-chain, localKeyID
1478      * attribute is not required, hence most vendors don't include it.
1479      * NSS/Netscape require it to be unique or null, where as IE/OpenSSL
1480      * ignore it.
1481      *
1482      * Here is a list of pkcs12 attribute values in CertBags.
1483      *
1484      * PKCS12 Attribute       NSS/Netscape    IE     OpenSSL    J2SE
1485      * --------------------------------------------------------------
1486      * LocalKeyId
1487      * (In EE cert only,
1488      *  NULL in CA certs)      true          true     true      true
1489      *
1490      * friendlyName            unique        same/    same/     unique
1491      *                                       unique   unique/
1492      *                                                null
1493      * trustedKeyUsage         -             -        -         true
1494      *
1495      * Note: OpenSSL adds friendlyName for end-entity cert only, and
1496      * removes the localKeyID and friendlyName for CA certs.
1497      * If the CertBag did not have a friendlyName, most vendors will
1498      * add it, and assign it to the DN of the cert.
1499      */
1500     private byte[] getBagAttributes(String alias, byte[] keyId,
1501         Set<KeyStore.Entry.Attribute> attributes) throws IOException {
1502         return getBagAttributes(alias, keyId, null, attributes);
1503     }
1504 
1505     private byte[] getBagAttributes(String alias, byte[] keyId,
1506         ObjectIdentifier[] trustedUsage,
1507         Set<KeyStore.Entry.Attribute> attributes) throws IOException {
1508 
1509         byte[] localKeyID = null;
1510         byte[] friendlyName = null;
1511         byte[] trustedKeyUsage = null;
1512 
1513         // return null if all three attributes are null
1514         if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) {
1515             return null;
1516         }
1517 
1518         // SafeBag Attributes
1519         DerOutputStream bagAttrs = new DerOutputStream();
1520 
1521         // Encode the friendlyname oid.
1522         if (alias != null) {
1523             DerOutputStream bagAttr1 = new DerOutputStream();
1524             bagAttr1.putOID(PKCS9FriendlyName_OID);
1525             DerOutputStream bagAttrContent1 = new DerOutputStream();
1526             DerOutputStream bagAttrValue1 = new DerOutputStream();
1527             bagAttrContent1.putBMPString(alias);
1528             bagAttr1.write(DerValue.tag_Set, bagAttrContent1);
1529             bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1);
1530             friendlyName = bagAttrValue1.toByteArray();
1531         }
1532 
1533         // Encode the localkeyId oid.
1534         if (keyId != null) {
1535             DerOutputStream bagAttr2 = new DerOutputStream();
1536             bagAttr2.putOID(PKCS9LocalKeyId_OID);
1537             DerOutputStream bagAttrContent2 = new DerOutputStream();
1538             DerOutputStream bagAttrValue2 = new DerOutputStream();
1539             bagAttrContent2.putOctetString(keyId);
1540             bagAttr2.write(DerValue.tag_Set, bagAttrContent2);
1541             bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2);
1542             localKeyID = bagAttrValue2.toByteArray();
1543         }
1544 
1545         // Encode the trustedKeyUsage oid.
1546         if (trustedUsage != null) {
1547             DerOutputStream bagAttr3 = new DerOutputStream();
1548             bagAttr3.putOID(TrustedKeyUsage_OID);
1549             DerOutputStream bagAttrContent3 = new DerOutputStream();
1550             DerOutputStream bagAttrValue3 = new DerOutputStream();
1551             for (ObjectIdentifier usage : trustedUsage) {
1552                 bagAttrContent3.putOID(usage);
1553             }
1554             bagAttr3.write(DerValue.tag_Set, bagAttrContent3);
1555             bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3);
1556             trustedKeyUsage = bagAttrValue3.toByteArray();
1557         }
1558 
1559         DerOutputStream attrs = new DerOutputStream();
1560         if (friendlyName != null) {
1561             attrs.write(friendlyName);
1562         }
1563         if (localKeyID != null) {
1564             attrs.write(localKeyID);
1565         }
1566         if (trustedKeyUsage != null) {
1567             attrs.write(trustedKeyUsage);
1568         }
1569 
1570         if (attributes != null) {
1571             for (KeyStore.Entry.Attribute attribute : attributes) {
1572                 String attributeName = attribute.getName();
1573                 // skip friendlyName, localKeyId and trustedKeyUsage
1574                 if (CORE_ATTRIBUTES[0].equals(attributeName) ||
1575                     CORE_ATTRIBUTES[1].equals(attributeName) ||
1576                     CORE_ATTRIBUTES[2].equals(attributeName)) {
1577                     continue;
1578                 }
1579                 attrs.write(((PKCS12Attribute) attribute).getEncoded());
1580             }
1581         }
1582 
1583         bagAttrs.write(DerValue.tag_Set, attrs);
1584         return bagAttrs.toByteArray();
1585     }
1586 
1587     /*
1588      * Create EncryptedData content type, that contains EncryptedContentInfo.
1589      * Includes certificates in individual SafeBags of type CertBag.
1590      * Each CertBag may include pkcs12 attributes
1591      * (see comments in getBagAttributes)
1592      */
1593     private byte[] createEncryptedData(char[] password)
1594         throws CertificateException, IOException
1595     {
1596         DerOutputStream out = new DerOutputStream();
1597         for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1598 
1599             String alias = e.nextElement();
1600             Entry entry = entries.get(alias);
1601 
1602             // certificate chain
1603             Certificate[] certs;
1604 
1605             if (entry instanceof PrivateKeyEntry) {
1606                 PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
1607                 if (keyEntry.chain != null) {
1608                     certs = keyEntry.chain;
1609                 } else {
1610                     certs = new Certificate[0];
1611                 }
1612             } else if (entry instanceof CertEntry) {
1613                 certs = new Certificate[]{((CertEntry) entry).cert};
1614             } else {
1615                 certs = new Certificate[0];
1616             }
1617 
1618             for (int i = 0; i < certs.length; i++) {
1619                 // create SafeBag of Type CertBag
1620                 DerOutputStream safeBag = new DerOutputStream();
1621                 safeBag.putOID(CertBag_OID);
1622 
1623                 // create a CertBag
1624                 DerOutputStream certBag = new DerOutputStream();
1625                 certBag.putOID(PKCS9CertType_OID);
1626 
1627                 // write encoded certs in a context-specific tag
1628                 DerOutputStream certValue = new DerOutputStream();
1629                 X509Certificate cert = (X509Certificate) certs[i];
1630                 certValue.putOctetString(cert.getEncoded());
1631                 certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1632                                         true, (byte) 0), certValue);
1633 
1634                 // wrap CertBag in a Sequence
1635                 DerOutputStream certout = new DerOutputStream();
1636                 certout.write(DerValue.tag_Sequence, certBag);
1637                 byte[] certBagValue = certout.toByteArray();
1638 
1639                 // Wrap the CertBag encoding in a context-specific tag.
1640                 DerOutputStream bagValue = new DerOutputStream();
1641                 bagValue.write(certBagValue);
1642                 // write SafeBag Value
1643                 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1644                                 true, (byte) 0), bagValue);
1645 
1646                 // write SafeBag Attributes
1647                 // All Certs should have a unique friendlyName.
1648                 // This change is made to meet NSS requirements.
1649                 byte[] bagAttrs = null;
1650                 if (i == 0) {
1651                     // Only End-Entity Cert should have a localKeyId.
1652                     if (entry instanceof KeyEntry) {
1653                         KeyEntry keyEntry = (KeyEntry) entry;
1654                         bagAttrs =
1655                             getBagAttributes(keyEntry.alias, keyEntry.keyId,
1656                                 keyEntry.attributes);
1657                     } else {
1658                         CertEntry certEntry = (CertEntry) entry;
1659                         bagAttrs =
1660                             getBagAttributes(certEntry.alias, certEntry.keyId,
1661                                 certEntry.trustedKeyUsage,
1662                                 certEntry.attributes);
1663                     }
1664                 } else {
1665                     // Trusted root CA certs and Intermediate CA certs do not
1666                     // need to have a localKeyId, and hence localKeyId is null
1667                     // This change is made to meet NSS/Netscape requirements.
1668                     // NSS pkcs12 library requires trusted CA certs in the
1669                     // certificate chain to have unique or null localKeyID.
1670                     // However, IE/OpenSSL do not impose this restriction.
1671                     bagAttrs = getBagAttributes(
1672                             cert.getSubjectX500Principal().getName(), null,
1673                             entry.attributes);
1674                 }
1675                 if (bagAttrs != null) {
1676                     safeBag.write(bagAttrs);
1677                 }
1678 
1679                 // wrap as Sequence
1680                 out.write(DerValue.tag_Sequence, safeBag);
1681             } // for cert-chain
1682         }
1683 
1684         // wrap as SequenceOf SafeBag
1685         DerOutputStream safeBagValue = new DerOutputStream();
1686         safeBagValue.write(DerValue.tag_SequenceOf, out);
1687         byte[] safeBagData = safeBagValue.toByteArray();
1688 
1689         // encrypt the content (EncryptedContentInfo)
1690         byte[] encrContentInfo = encryptContent(safeBagData, password);
1691 
1692         // -- SEQUENCE of EncryptedData
1693         DerOutputStream encrData = new DerOutputStream();
1694         DerOutputStream encrDataContent = new DerOutputStream();
1695         encrData.putInteger(0);
1696         encrData.write(encrContentInfo);
1697         encrDataContent.write(DerValue.tag_Sequence, encrData);
1698         return encrDataContent.toByteArray();
1699     }
1700 
1701     /*
1702      * Create SafeContent Data content type.
1703      * Includes encrypted secret key in a SafeBag of type SecretBag.
1704      * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.
1705      * Each PKCS8ShroudedKeyBag includes pkcs12 attributes
1706      * (see comments in getBagAttributes)
1707      */
1708     private byte[] createSafeContent()
1709         throws CertificateException, IOException {
1710 
1711         DerOutputStream out = new DerOutputStream();
1712         for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1713 
1714             String alias = e.nextElement();
1715             Entry entry = entries.get(alias);
1716             if (entry == null || (!(entry instanceof KeyEntry))) {
1717                 continue;
1718             }
1719             DerOutputStream safeBag = new DerOutputStream();
1720             KeyEntry keyEntry = (KeyEntry) entry;
1721 
1722             // DER encode the private key
1723             if (keyEntry instanceof PrivateKeyEntry) {
1724                 // Create SafeBag of type pkcs8ShroudedKeyBag
1725                 safeBag.putOID(PKCS8ShroudedKeyBag_OID);
1726 
1727                 // get the encrypted private key
1728                 byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey;
1729                 EncryptedPrivateKeyInfo encrInfo = null;
1730                 try {
1731                     encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
1732 
1733                 } catch (IOException ioe) {
1734                     throw new IOException("Private key not stored as "
1735                             + "PKCS#8 EncryptedPrivateKeyInfo"
1736                             + ioe.getMessage());
1737                 }
1738 
1739                 // Wrap the EncryptedPrivateKeyInfo in a context-specific tag.
1740                 DerOutputStream bagValue = new DerOutputStream();
1741                 bagValue.write(encrInfo.getEncoded());
1742                 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1743                                 true, (byte) 0), bagValue);
1744 
1745             // DER encode the secret key
1746             } else if (keyEntry instanceof SecretKeyEntry) {
1747                 // Create SafeBag of type SecretBag
1748                 safeBag.putOID(SecretBag_OID);
1749 
1750                 // Create a SecretBag
1751                 DerOutputStream secretBag = new DerOutputStream();
1752                 secretBag.putOID(PKCS8ShroudedKeyBag_OID);
1753 
1754                 // Write secret key in a context-specific tag
1755                 DerOutputStream secretKeyValue = new DerOutputStream();
1756                 secretKeyValue.putOctetString(
1757                     ((SecretKeyEntry) keyEntry).protectedSecretKey);
1758                 secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1759                                         true, (byte) 0), secretKeyValue);
1760 
1761                 // Wrap SecretBag in a Sequence
1762                 DerOutputStream secretBagSeq = new DerOutputStream();
1763                 secretBagSeq.write(DerValue.tag_Sequence, secretBag);
1764                 byte[] secretBagValue = secretBagSeq.toByteArray();
1765 
1766                 // Wrap the secret bag in a context-specific tag.
1767                 DerOutputStream bagValue = new DerOutputStream();
1768                 bagValue.write(secretBagValue);
1769 
1770                 // Write SafeBag value
1771                 safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
1772                                     true, (byte) 0), bagValue);
1773             } else {
1774                 continue; // skip this entry
1775             }
1776 
1777             // write SafeBag Attributes
1778             byte[] bagAttrs =
1779                 getBagAttributes(alias, entry.keyId, entry.attributes);
1780             safeBag.write(bagAttrs);
1781 
1782             // wrap as Sequence
1783             out.write(DerValue.tag_Sequence, safeBag);
1784         }
1785 
1786         // wrap as Sequence
1787         DerOutputStream safeBagValue = new DerOutputStream();
1788         safeBagValue.write(DerValue.tag_Sequence, out);
1789         return safeBagValue.toByteArray();
1790     }
1791 
1792 
1793     /*
1794      * Encrypt the contents using Password-based (PBE) encryption
1795      * as defined in PKCS #5.
1796      *
1797      * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used
1798      *       to derive the key and IV.
1799      *
1800      * @return encrypted contents encoded as EncryptedContentInfo
1801      */
1802     private byte[] encryptContent(byte[] data, char[] password)
1803         throws IOException {
1804 
1805         byte[] encryptedData = null;
1806 
1807         // create AlgorithmParameters
1808         AlgorithmParameters algParams =
1809                 getAlgorithmParameters("PBEWithSHA1AndRC2_40");
1810         DerOutputStream bytes = new DerOutputStream();
1811         AlgorithmId algId =
1812                 new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);
1813         algId.encode(bytes);
1814         byte[] encodedAlgId = bytes.toByteArray();
1815 
1816         try {
1817             // Use JCE
1818             SecretKey skey = getPBEKey(password);
1819             Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");
1820             cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
1821             encryptedData = cipher.doFinal(data);
1822 
1823             if (debug != null) {
1824                 debug.println("  (Cipher algorithm: " + cipher.getAlgorithm() +
1825                     ")");
1826             }
1827 
1828         } catch (Exception e) {
1829             throw new IOException("Failed to encrypt" +
1830                     " safe contents entry: " + e, e);
1831         }
1832 
1833         // create EncryptedContentInfo
1834         DerOutputStream bytes2 = new DerOutputStream();
1835         bytes2.putOID(ContentInfo.DATA_OID);
1836         bytes2.write(encodedAlgId);
1837 
1838         // Wrap encrypted data in a context-specific tag.
1839         DerOutputStream tmpout2 = new DerOutputStream();
1840         tmpout2.putOctetString(encryptedData);
1841         bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
1842                                         false, (byte)0), tmpout2);
1843 
1844         // wrap EncryptedContentInfo in a Sequence
1845         DerOutputStream out = new DerOutputStream();
1846         out.write(DerValue.tag_Sequence, bytes2);
1847         return out.toByteArray();
1848     }
1849 
1850     /**
1851      * Loads the keystore from the given input stream.
1852      *
1853      * <p>If a password is given, it is used to check the integrity of the
1854      * keystore data. Otherwise, the integrity of the keystore is not checked.
1855      *
1856      * @param stream the input stream from which the keystore is loaded
1857      * @param password the (optional) password used to check the integrity of
1858      * the keystore.
1859      *
1860      * @exception IOException if there is an I/O or format problem with the
1861      * keystore data
1862      * @exception NoSuchAlgorithmException if the algorithm used to check
1863      * the integrity of the keystore cannot be found
1864      * @exception CertificateException if any of the certificates in the
1865      * keystore could not be loaded
1866      */
1867     public synchronized void engineLoad(InputStream stream, char[] password)
1868         throws IOException, NoSuchAlgorithmException, CertificateException
1869     {
1870         DataInputStream dis;
1871         CertificateFactory cf = null;
1872         ByteArrayInputStream bais = null;
1873         byte[] encoded = null;
1874 
1875         if (stream == null)
1876            return;
1877 
1878         // reset the counter
1879         counter = 0;
1880 
1881         DerValue val = new DerValue(stream);
1882         DerInputStream s = val.toDerInputStream();
1883         int version = s.getInteger();
1884 
1885         if (version != VERSION_3) {
1886            throw new IOException("PKCS12 keystore not in version 3 format");
1887         }
1888 
1889         entries.clear();
1890 
1891         /*
1892          * Read the authSafe.
1893          */
1894         byte[] authSafeData;
1895         ContentInfo authSafe = new ContentInfo(s);
1896         ObjectIdentifier contentType = authSafe.getContentType();
1897 
1898         if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1899            authSafeData = authSafe.getData();
1900         } else /* signed data */ {
1901            throw new IOException("public key protected PKCS12 not supported");
1902         }
1903 
1904         DerInputStream as = new DerInputStream(authSafeData);
1905         DerValue[] safeContentsArray = as.getSequence(2);
1906         int count = safeContentsArray.length;
1907 
1908         // reset the counters at the start
1909         privateKeyCount = 0;
1910         secretKeyCount = 0;
1911         certificateCount = 0;
1912 
1913         /*
1914          * Spin over the ContentInfos.
1915          */
1916         for (int i = 0; i < count; i++) {
1917             byte[] safeContentsData;
1918             ContentInfo safeContents;
1919             DerInputStream sci;
1920             byte[] eAlgId = null;
1921 
1922             sci = new DerInputStream(safeContentsArray[i].toByteArray());
1923             safeContents = new ContentInfo(sci);
1924             contentType = safeContents.getContentType();
1925             safeContentsData = null;
1926             if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1927 
1928                 if (debug != null) {
1929                     debug.println("Loading PKCS#7 data content-type");
1930                 }
1931 
1932                 safeContentsData = safeContents.getData();
1933             } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) {
1934                 if (password == null) {
1935                    continue;
1936                 }
1937 
1938                 if (debug != null) {
1939                     debug.println("Loading PKCS#7 encryptedData content-type");
1940                 }
1941 
1942                 DerInputStream edi =
1943                                 safeContents.getContent().toDerInputStream();
1944                 int edVersion = edi.getInteger();
1945                 DerValue[] seq = edi.getSequence(2);
1946                 ObjectIdentifier edContentType = seq[0].getOID();
1947                 eAlgId = seq[1].toByteArray();
1948                 if (!seq[2].isContextSpecific((byte)0)) {
1949                    throw new IOException("encrypted content not present!");
1950                 }
1951                 byte newTag = DerValue.tag_OctetString;
1952                 if (seq[2].isConstructed())
1953                    newTag |= 0x20;
1954                 seq[2].resetTag(newTag);
1955                 safeContentsData = seq[2].getOctetString();
1956 
1957                 // parse Algorithm parameters
1958                 DerInputStream in = seq[1].toDerInputStream();
1959                 ObjectIdentifier algOid = in.getOID();
1960                 AlgorithmParameters algParams = parseAlgParameters(algOid, in);
1961 
1962                 while (true) {
1963                     try {
1964                         // Use JCE
1965                         SecretKey skey = getPBEKey(password);
1966                         Cipher cipher = Cipher.getInstance(algOid.toString());
1967                         cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
1968                         safeContentsData = cipher.doFinal(safeContentsData);
1969                         break;
1970                     } catch (Exception e) {
1971                         if (password.length == 0) {
1972                             // Retry using an empty password
1973                             // without a NULL terminator.
1974                             password = new char[1];
1975                             continue;
1976                         }
1977                         throw new IOException(
1978                             "failed to decrypt safe contents entry: " + e, e);
1979                     }
1980                 }
1981             } else {
1982                 throw new IOException("public key protected PKCS12" +
1983                                         " not supported");
1984             }
1985             DerInputStream sc = new DerInputStream(safeContentsData);
1986             loadSafeContents(sc, password);
1987         }
1988 
1989         // The MacData is optional.
1990         if (password != null && s.available() > 0) {
1991            MacData macData = new MacData(s);
1992            try {
1993                 String algName =
1994                         macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
1995 
1996                 // Change SHA-1 to SHA1
1997                 algName = algName.replace("-", "");
1998 
1999                 // generate MAC (MAC key is created within JCE)
2000                 Mac m = Mac.getInstance("HmacPBE" + algName);
2001                 PBEParameterSpec params =
2002                         new PBEParameterSpec(macData.getSalt(),
2003                                         macData.getIterations());
2004                 SecretKey key = getPBEKey(password);
2005                 m.init(key, params);
2006                 m.update(authSafeData);
2007                 byte[] macResult = m.doFinal();
2008 
2009                 if (debug != null) {
2010                     debug.println("Checking keystore integrity " +
2011                         "(MAC algorithm: " + m.getAlgorithm() + ")");
2012                 }
2013 
2014                 if (!Arrays.equals(macData.getDigest(), macResult)) {
2015                    throw new SecurityException("Failed PKCS12" +
2016                                         " integrity checking");
2017                 }
2018            } catch (Exception e) {
2019                 throw new IOException("Integrity check failed: " + e, e);
2020            }
2021         }
2022 
2023         /*
2024          * Match up private keys with certificate chains.
2025          */
2026         PrivateKeyEntry[] list =
2027             keyList.toArray(new PrivateKeyEntry[keyList.size()]);
2028         for (int m = 0; m < list.length; m++) {
2029             PrivateKeyEntry entry = list[m];
2030             if (entry.keyId != null) {
2031                 ArrayList<X509Certificate> chain =
2032                                 new ArrayList<X509Certificate>();
2033                 X509Certificate cert = findMatchedCertificate(entry);
2034 
2035                 mainloop:
2036                 while (cert != null) {
2037                     // Check for loops in the certificate chain
2038                     if (!chain.isEmpty()) {
2039                         for (X509Certificate chainCert : chain) {
2040                             if (cert.equals(chainCert)) {
2041                                 if (debug != null) {
2042                                     debug.println("Loop detected in " +
2043                                         "certificate chain. Skip adding " +
2044                                         "repeated cert to chain. Subject: " +
2045                                         cert.getSubjectX500Principal()
2046                                             .toString());
2047                                 }
2048                                 break mainloop;
2049                             }
2050                         }
2051                     }
2052                     chain.add(cert);
2053                     X500Principal issuerDN = cert.getIssuerX500Principal();
2054                     if (issuerDN.equals(cert.getSubjectX500Principal())) {
2055                         break;
2056                     }
2057                     cert = certsMap.get(issuerDN);
2058                 }
2059                 /* Update existing KeyEntry in entries table */
2060                 if (chain.size() > 0)
2061                     entry.chain = chain.toArray(new Certificate[chain.size()]);
2062             }
2063         }
2064 
2065         if (debug != null) {
2066             if (privateKeyCount > 0) {
2067                 debug.println("Loaded " + privateKeyCount +
2068                     " protected private key(s)");
2069             }
2070             if (secretKeyCount > 0) {
2071                 debug.println("Loaded " + secretKeyCount +
2072                     " protected secret key(s)");
2073             }
2074             if (certificateCount > 0) {
2075                 debug.println("Loaded " + certificateCount +
2076                     " certificate(s)");
2077             }
2078         }
2079 
2080         certEntries.clear();
2081         certsMap.clear();
2082         keyList.clear();
2083     }
2084 
2085     /**
2086      * Locates a matched CertEntry from certEntries, and returns its cert.
2087      * @param entry the KeyEntry to match
2088      * @return a certificate, null if not found
2089      */
2090     private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
2091         CertEntry keyIdMatch = null;
2092         CertEntry aliasMatch = null;
2093         for (CertEntry ce: certEntries) {
2094             if (Arrays.equals(entry.keyId, ce.keyId)) {
2095                 keyIdMatch = ce;
2096                 if (entry.alias.equalsIgnoreCase(ce.alias)) {
2097                     // Full match!
2098                     return ce.cert;
2099                 }
2100             } else if (entry.alias.equalsIgnoreCase(ce.alias)) {
2101                 aliasMatch = ce;
2102             }
2103         }
2104         // keyId match first, for compatibility
2105         if (keyIdMatch != null) return keyIdMatch.cert;
2106         else if (aliasMatch != null) return aliasMatch.cert;
2107         else return null;
2108     }
2109 
2110     private void loadSafeContents(DerInputStream stream, char[] password)
2111         throws IOException, NoSuchAlgorithmException, CertificateException
2112     {
2113         DerValue[] safeBags = stream.getSequence(2);
2114         int count = safeBags.length;
2115 
2116         /*
2117          * Spin over the SafeBags.
2118          */
2119         for (int i = 0; i < count; i++) {
2120             ObjectIdentifier bagId;
2121             DerInputStream sbi;
2122             DerValue bagValue;
2123             Object bagItem = null;
2124 
2125             sbi = safeBags[i].toDerInputStream();
2126             bagId = sbi.getOID();
2127             bagValue = sbi.getDerValue();
2128             if (!bagValue.isContextSpecific((byte)0)) {
2129                 throw new IOException("unsupported PKCS12 bag value type "
2130                                         + bagValue.tag);
2131             }
2132             bagValue = bagValue.data.getDerValue();
2133             if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) {
2134                 PrivateKeyEntry kEntry = new PrivateKeyEntry();
2135                 kEntry.protectedPrivKey = bagValue.toByteArray();
2136                 bagItem = kEntry;
2137                 privateKeyCount++;
2138             } else if (bagId.equals((Object)CertBag_OID)) {
2139                 DerInputStream cs = new DerInputStream(bagValue.toByteArray());
2140                 DerValue[] certValues = cs.getSequence(2);
2141                 ObjectIdentifier certId = certValues[0].getOID();
2142                 if (!certValues[1].isContextSpecific((byte)0)) {
2143                     throw new IOException("unsupported PKCS12 cert value type "
2144                                         + certValues[1].tag);
2145                 }
2146                 DerValue certValue = certValues[1].data.getDerValue();
2147                 CertificateFactory cf = CertificateFactory.getInstance("X509");
2148                 X509Certificate cert;
2149                 cert = (X509Certificate)cf.generateCertificate
2150                         (new ByteArrayInputStream(certValue.getOctetString()));
2151                 bagItem = cert;
2152                 certificateCount++;
2153             } else if (bagId.equals((Object)SecretBag_OID)) {
2154                 DerInputStream ss = new DerInputStream(bagValue.toByteArray());
2155                 DerValue[] secretValues = ss.getSequence(2);
2156                 ObjectIdentifier secretId = secretValues[0].getOID();
2157                 if (!secretValues[1].isContextSpecific((byte)0)) {
2158                     throw new IOException(
2159                         "unsupported PKCS12 secret value type "
2160                                         + secretValues[1].tag);
2161                 }
2162                 DerValue secretValue = secretValues[1].data.getDerValue();
2163                 SecretKeyEntry kEntry = new SecretKeyEntry();
2164                 kEntry.protectedSecretKey = secretValue.getOctetString();
2165                 bagItem = kEntry;
2166                 secretKeyCount++;
2167             } else {
2168 
2169                 if (debug != null) {
2170                     debug.println("Unsupported PKCS12 bag type: " + bagId);
2171                 }
2172             }
2173 
2174             DerValue[] attrSet;
2175             try {
2176                 attrSet = sbi.getSet(3);
2177             } catch (IOException e) {
2178                 // entry does not have attributes
2179                 // Note: CA certs can have no attributes
2180                 // OpenSSL generates pkcs12 with no attr for CA certs.
2181                 attrSet = null;
2182             }
2183 
2184             String alias = null;
2185             byte[] keyId = null;
2186             ObjectIdentifier[] trustedKeyUsage = null;
2187             Set<PKCS12Attribute> attributes = new HashSet<>();
2188 
2189             if (attrSet != null) {
2190                 for (int j = 0; j < attrSet.length; j++) {
2191                     byte[] encoded = attrSet[j].toByteArray();
2192                     DerInputStream as = new DerInputStream(encoded);
2193                     DerValue[] attrSeq = as.getSequence(2);
2194                     ObjectIdentifier attrId = attrSeq[0].getOID();
2195                     DerInputStream vs =
2196                         new DerInputStream(attrSeq[1].toByteArray());
2197                     DerValue[] valSet;
2198                     try {
2199                         valSet = vs.getSet(1);
2200                     } catch (IOException e) {
2201                         throw new IOException("Attribute " + attrId +
2202                                 " should have a value " + e.getMessage());
2203                     }
2204                     if (attrId.equals((Object)PKCS9FriendlyName_OID)) {
2205                         alias = valSet[0].getBMPString();
2206                     } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) {
2207                         keyId = valSet[0].getOctetString();
2208                     } else if
2209                         (attrId.equals((Object)TrustedKeyUsage_OID)) {
2210                         trustedKeyUsage = new ObjectIdentifier[valSet.length];
2211                         for (int k = 0; k < valSet.length; k++) {
2212                             trustedKeyUsage[k] = valSet[k].getOID();
2213                         }
2214                     } else {
2215                         attributes.add(new PKCS12Attribute(encoded));
2216                     }
2217                 }
2218             }
2219 
2220             /*
2221              * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId)
2222              * are optional PKCS12 bagAttributes. But entries in the keyStore
2223              * are identified by their alias. Hence we need to have an
2224              * Unfriendlyname in the alias, if alias is null. The keyId
2225              * attribute is required to match the private key with the
2226              * certificate. If we get a bagItem of type KeyEntry with a
2227              * null keyId, we should skip it entirely.
2228              */
2229             if (bagItem instanceof KeyEntry) {
2230                 KeyEntry entry = (KeyEntry)bagItem;
2231 
2232                 if (bagItem instanceof PrivateKeyEntry) {
2233                     if (keyId == null) {
2234                        // Insert a localKeyID for the privateKey
2235                        // Note: This is a workaround to allow null localKeyID
2236                        // attribute in pkcs12 with one private key entry and
2237                        // associated cert-chain
2238                        if (privateKeyCount == 1) {
2239                             keyId = "01".getBytes("UTF8");
2240                        } else {
2241                             continue;
2242                        }
2243                     }
2244                 }
2245                 entry.keyId = keyId;
2246                 // restore date if it exists
2247                 String keyIdStr = new String(keyId, "UTF8");
2248                 Date date = null;
2249                 if (keyIdStr.startsWith("Time ")) {
2250                     try {
2251                         date = new Date(
2252                                 Long.parseLong(keyIdStr.substring(5)));
2253                     } catch (Exception e) {
2254                         date = null;
2255                     }
2256                 }
2257                 if (date == null) {
2258                     date = new Date();
2259                 }
2260                 entry.date = date;
2261 
2262                 if (bagItem instanceof PrivateKeyEntry) {
2263                     keyList.add((PrivateKeyEntry) entry);
2264                 }
2265                 if (entry.attributes == null) {
2266                     entry.attributes = new HashSet<>();
2267                 }
2268                 entry.attributes.addAll(attributes);
2269                 if (alias == null) {
2270                    alias = getUnfriendlyName();
2271                 }
2272                 entry.alias = alias;
2273                 entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
2274 
2275             } else if (bagItem instanceof X509Certificate) {
2276                 X509Certificate cert = (X509Certificate)bagItem;
2277                 // Insert a localKeyID for the corresponding cert
2278                 // Note: This is a workaround to allow null localKeyID
2279                 // attribute in pkcs12 with one private key entry and
2280                 // associated cert-chain
2281                 if ((keyId == null) && (privateKeyCount == 1)) {
2282                     // insert localKeyID only for EE cert or self-signed cert
2283                     if (i == 0) {
2284                         keyId = "01".getBytes("UTF8");
2285                     }
2286                 }
2287                 // Trusted certificate
2288                 if (trustedKeyUsage != null) {
2289                     if (alias == null) {
2290                         alias = getUnfriendlyName();
2291                     }
2292                     CertEntry certEntry =
2293                         new CertEntry(cert, keyId, alias, trustedKeyUsage,
2294                             attributes);
2295                     entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
2296                 } else {
2297                     certEntries.add(new CertEntry(cert, keyId, alias));
2298                 }
2299                 X500Principal subjectDN = cert.getSubjectX500Principal();
2300                 if (subjectDN != null) {
2301                     if (!certsMap.containsKey(subjectDN)) {
2302                         certsMap.put(subjectDN, cert);
2303                     }
2304                 }
2305             }
2306         }
2307     }
2308 
2309     private String getUnfriendlyName() {
2310         counter++;
2311         return (String.valueOf(counter));
2312     }
2313 }