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