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