< prev index next >

src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java

Print this page




  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  *


 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     };


 364                 new UnrecoverableKeyException("Private key not stored as "
 365                                  + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
 366             uke.initCause(ioe);
 367             throw uke;
 368         }
 369 
 370        try {
 371             PBEParameterSpec pbeSpec;
 372             int ic;
 373 
 374             if (algParams != null) {
 375                 try {
 376                     pbeSpec =
 377                         algParams.getParameterSpec(PBEParameterSpec.class);
 378                 } catch (InvalidParameterSpecException ipse) {
 379                     throw new IOException("Invalid PBE algorithm parameters");
 380                 }
 381                 ic = pbeSpec.getIterationCount();
 382 
 383                 if (ic > MAX_ITERATION_COUNT) {
 384                     throw new IOException("PBE iteration count too large");
 385                 }
 386             } else {
 387                 ic = 0;
 388             }
 389 
 390             key = RetryWithZero.run(pass -> {
 391                 // Use JCE
 392                 SecretKey skey = getPBEKey(pass);
 393                 Cipher cipher = Cipher.getInstance(
 394                         mapPBEParamsToAlgorithm(algOid, algParams));
 395                 cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
 396                 byte[] keyInfo = cipher.doFinal(encryptedKey);
 397                 /*
 398                  * Parse the key algorithm and then use a JCA key factory
 399                  * to re-create the key.
 400                  */
 401                 DerValue val = new DerValue(keyInfo);
 402                 DerInputStream in = val.toDerInputStream();
 403                 int i = in.getInteger();
 404                 DerValue[] value = in.getSequence(2);
 405                 if (value.length < 1 || value.length > 2) {
 406                     throw new IOException("Invalid length for AlgorithmIdentifier");
 407                 }
 408                 AlgorithmId algId = new AlgorithmId(value[0].getOID());
 409                 String keyAlgo = algId.getName();
 410 
 411                 // decode private key
 412                 if (entry instanceof PrivateKeyEntry) {
 413                     KeyFactory kfac = KeyFactory.getInstance(keyAlgo);
 414                     PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo);
 415                     Key tmp = kfac.generatePrivate(kspec);
 416 
 417                     if (debug != null) {
 418                         debug.println("Retrieved a protected private key at alias" +
 419                                 " '" + alias + "' (" +
 420                                 new AlgorithmId(algOid).getName() +
 421                                 " iterations: " + ic + ")");
 422                     }
 423                     return tmp;
 424                     // decode secret key
 425                 } else {
 426                     byte[] keyBytes = in.getOctetString();
 427                     SecretKeySpec secretKeySpec =
 428                             new SecretKeySpec(keyBytes, keyAlgo);
 429 
 430                     // Special handling required for PBE: needs a PBEKeySpec
 431                     Key tmp;
 432                     if (keyAlgo.startsWith("PBE")) {
 433                         SecretKeyFactory sKeyFactory =
 434                                 SecretKeyFactory.getInstance(keyAlgo);
 435                         KeySpec pbeKeySpec =
 436                                 sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class);
 437                         tmp = sKeyFactory.generateSecret(pbeKeySpec);
 438                     } else {
 439                         tmp = secretKeySpec;
 440                     }
 441 
 442                     if (debug != null) {
 443                         debug.println("Retrieved a protected secret key at alias " +
 444                                 "'" + alias + "' (" +
 445                                 new AlgorithmId(algOid).getName() +
 446                                 " iterations: " + ic + ")");
 447                     }
 448                     return tmp;
 449                 }
 450             }, password);
 451 
 452         } catch (Exception e) {
 453             UnrecoverableKeyException uke =
 454                 new UnrecoverableKeyException("Get Key failed: " +
 455                                         e.getMessage());
 456             uke.initCause(e);
 457             throw uke;
 458         }
 459         return key;
 460     }
 461 
 462     /**
 463      * Returns the certificate chain associated with the given alias.
 464      *
 465      * @param alias the alias name


 674                 }
 675                 secretKeyCount++;
 676                 entry = keyEntry;
 677 
 678             } else {
 679                 throw new KeyStoreException("Unsupported Key type");
 680             }
 681 
 682             entry.attributes = new HashSet<>();
 683             if (attributes != null) {
 684                 entry.attributes.addAll(attributes);
 685             }
 686             // set the keyId to current date
 687             entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
 688             // set the alias
 689             entry.alias = alias.toLowerCase(Locale.ENGLISH);
 690             // add the entry
 691             entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
 692 
 693         } catch (Exception nsae) {
 694             throw new KeyStoreException("Key protection " +
 695                        " algorithm not found: " + nsae, nsae);
 696         }
 697     }
 698 
 699     /**
 700      * Assigns the given key (that has already been protected) to the given
 701      * alias.
 702      *
 703      * <p>If the protected key is of type
 704      * <code>java.security.PrivateKey</code>, it must be accompanied by a
 705      * certificate chain certifying the corresponding public key. If the
 706      * underlying keystore implementation is of type <code>jks</code>,
 707      * <code>key</code> must be encoded as an
 708      * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
 709      *
 710      * <p>If the given alias already exists, the keystore information
 711      * associated with it is overridden by the given key (and possibly
 712      * certificate chain).
 713      *
 714      * @param alias the alias name


 771     }
 772 
 773 
 774     /*
 775      * Generate random salt
 776      */
 777     private byte[] getSalt()
 778     {
 779         // Generate a random salt.
 780         byte[] salt = new byte[SALT_LEN];
 781         if (random == null) {
 782            random = new SecureRandom();
 783         }
 784         random.nextBytes(salt);
 785         return salt;
 786     }
 787 
 788     /*
 789      * Generate PBE Algorithm Parameters
 790      */
 791     private AlgorithmParameters getPBEAlgorithmParameters(String algorithm)
 792         throws IOException
 793     {
 794         AlgorithmParameters algParams = null;
 795 
 796         // create PBE parameters from salt and iteration count
 797         PBEParameterSpec paramSpec =
 798                 new PBEParameterSpec(getSalt(), PBE_ITERATION_COUNT);
 799         try {
 800            algParams = AlgorithmParameters.getInstance(algorithm);
 801            algParams.init(paramSpec);
 802         } catch (Exception e) {
 803            throw new IOException("getPBEAlgorithmParameters failed: " +
 804                                  e.getMessage(), e);
 805         }
 806         return algParams;
 807     }
 808 
 809     /*
 810      * parse Algorithm Parameters
 811      */
 812     private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm,
 813         DerInputStream in) throws IOException
 814     {
 815         AlgorithmParameters algParams = null;
 816         try {
 817             DerValue params;
 818             if (in.available() == 0) {


 841     /*
 842      * Generate PBE key
 843      */
 844     private SecretKey getPBEKey(char[] password) throws IOException
 845     {
 846         SecretKey skey = null;
 847 
 848         try {
 849             PBEKeySpec keySpec = new PBEKeySpec(password);
 850             SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
 851             skey = skFac.generateSecret(keySpec);
 852             keySpec.clearPassword();
 853         } catch (Exception e) {
 854            throw new IOException("getSecretKey failed: " +
 855                                  e.getMessage(), e);
 856         }
 857         return skey;
 858     }
 859 
 860     /*
 861      * Encrypt private key using Password-based encryption (PBE)
 862      * as defined in PKCS#5.
 863      *
 864      * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
 865      *       used to derive the key and IV.
 866      *
 867      * @return encrypted private key encoded as EncryptedPrivateKeyInfo

 868      */
 869     private byte[] encryptPrivateKey(byte[] data,
 870         KeyStore.PasswordProtection passwordProtection)
 871         throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
 872     {
 873         byte[] key = null;
 874 
 875         try {
 876             String algorithm;
 877             AlgorithmParameters algParams;
 878             AlgorithmId algid;
 879 
 880             // Initialize PBE algorithm and parameters
 881             algorithm = passwordProtection.getProtectionAlgorithm();
 882             if (algorithm != null) {
 883                 AlgorithmParameterSpec algParamSpec =
 884                     passwordProtection.getProtectionParameters();
 885                 if (algParamSpec != null) {
 886                     algParams = AlgorithmParameters.getInstance(algorithm);
 887                     algParams.init(algParamSpec);
 888                 } else {
 889                     algParams = getPBEAlgorithmParameters(algorithm);

 890                 }
 891             } else {
 892                 // Check default key protection algorithm for PKCS12 keystores
 893                 algorithm = AccessController.doPrivileged(
 894                     new PrivilegedAction<String>() {
 895                         public String run() {
 896                             String prop =
 897                                 Security.getProperty(
 898                                     KEY_PROTECTION_ALGORITHM[0]);
 899                             if (prop == null) {
 900                                 prop = Security.getProperty(
 901                                     KEY_PROTECTION_ALGORITHM[1]);
 902                             }
 903                             return prop;
 904                         }
 905                     });
 906                 if (algorithm == null || algorithm.isEmpty()) {
 907                     algorithm = "PBEWithSHA1AndDESede";
 908                 }
 909                 algParams = getPBEAlgorithmParameters(algorithm);
 910             }
 911 
 912             ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm);
 913             if (pbeOID == null) {
 914                     throw new IOException("PBE algorithm '" + algorithm +
 915                         " 'is not supported for key entry protection");
 916             }
 917 
 918             // Use JCE
 919             SecretKey skey = getPBEKey(passwordProtection.getPassword());
 920             Cipher cipher = Cipher.getInstance(algorithm);
 921             cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
 922             byte[] encryptedKey = cipher.doFinal(data);
 923             algid = new AlgorithmId(pbeOID, cipher.getParameters());
 924 
 925             if (debug != null) {
 926                 debug.println("  (Cipher algorithm: " + cipher.getAlgorithm() +
 927                     ")");
 928             }
 929 


 947      * Map a PBE algorithm name onto its object identifier
 948      */
 949     private static ObjectIdentifier mapPBEAlgorithmToOID(String algorithm)
 950         throws NoSuchAlgorithmException {
 951         // Check for PBES2 algorithms
 952         if (algorithm.toLowerCase(Locale.ENGLISH).startsWith("pbewithhmacsha")) {
 953             return pbes2_OID;
 954         }
 955         return AlgorithmId.get(algorithm).getOID();
 956     }
 957 
 958     /*
 959      * Map a PBE algorithm parameters onto its algorithm name
 960      */
 961     private static String mapPBEParamsToAlgorithm(ObjectIdentifier algorithm,
 962         AlgorithmParameters algParams) throws NoSuchAlgorithmException {
 963         // Check for PBES2 algorithms
 964         if (algorithm.equals((Object)pbes2_OID) && algParams != null) {
 965             return algParams.toString();
 966         }
 967         return algorithm.toString();
 968     }
 969 
 970     /**
 971      * Assigns the given certificate to the given alias.
 972      *
 973      * <p>If the given alias already exists in this keystore and identifies a
 974      * <i>trusted certificate entry</i>, the certificate associated with it is
 975      * overridden by the given certificate.
 976      *
 977      * @param alias the alias name
 978      * @param cert the certificate
 979      *
 980      * @exception KeyStoreException if the given alias already exists and does
 981      * not identify a <i>trusted certificate entry</i>, or this operation fails
 982      * for some other reason.
 983      */
 984     public synchronized void engineSetCertificateEntry(String alias,
 985         Certificate cert) throws KeyStoreException
 986     {
 987         setCertEntry(alias, cert, null);


1172         }
1173         return null;
1174     }
1175 
1176     /**
1177      * Stores this keystore to the given output stream, and protects its
1178      * integrity with the given password.
1179      *
1180      * @param stream the output stream to which this keystore is written.
1181      * @param password the password to generate the keystore integrity check
1182      *
1183      * @exception IOException if there was an I/O problem with data
1184      * @exception NoSuchAlgorithmException if the appropriate data integrity
1185      * algorithm could not be found
1186      * @exception CertificateException if any of the certificates included in
1187      * the keystore data could not be stored
1188      */
1189     public synchronized void engineStore(OutputStream stream, char[] password)
1190         throws IOException, NoSuchAlgorithmException, CertificateException
1191     {
1192         // password is mandatory when storing
1193         if (password == null) {
1194            throw new IllegalArgumentException("password can't be null");
1195         }
1196 
1197         // -- Create PFX
1198         DerOutputStream pfx = new DerOutputStream();
1199 
1200         // PFX version (always write the latest version)
1201         DerOutputStream version = new DerOutputStream();
1202         version.putInteger(VERSION_3);
1203         byte[] pfxVersion = version.toByteArray();
1204         pfx.write(pfxVersion);
1205 
1206         // -- Create AuthSafe
1207         DerOutputStream authSafe = new DerOutputStream();
1208 
1209         // -- Create ContentInfos
1210         DerOutputStream authSafeContentInfo = new DerOutputStream();
1211 
1212         // -- create safeContent Data ContentInfo
1213         if (privateKeyCount > 0 || secretKeyCount > 0) {
1214 
1215             if (debug != null) {
1216                 debug.println("Storing " + (privateKeyCount + secretKeyCount) +
1217                     " protected key(s) in a PKCS#7 data");
1218             }
1219 
1220             byte[] safeContentData = createSafeContent();
1221             ContentInfo dataContentInfo = new ContentInfo(safeContentData);
1222             dataContentInfo.encode(authSafeContentInfo);
1223         }
1224 
1225         // -- create EncryptedContentInfo
1226         if (certificateCount > 0) {
1227 







1228             if (debug != null) {
1229                 debug.println("Storing " + certificateCount +
1230                     " certificate(s) in a PKCS#7 encryptedData");
1231             }
1232 
1233             byte[] encrData = createEncryptedData(password);

1234             ContentInfo encrContentInfo =
1235                 new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
1236                                 new DerValue(encrData));
1237             encrContentInfo.encode(authSafeContentInfo);




1238         }
1239 
1240         // wrap as SequenceOf ContentInfos
1241         DerOutputStream cInfo = new DerOutputStream();
1242         cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
1243         byte[] authenticatedSafe = cInfo.toByteArray();
1244 
1245         // Create Encapsulated ContentInfo
1246         ContentInfo contentInfo = new ContentInfo(authenticatedSafe);
1247         contentInfo.encode(authSafe);
1248         byte[] authSafeData = authSafe.toByteArray();
1249         pfx.write(authSafeData);
1250 
1251         // -- MAC







1252         byte[] macData = calculateMac(password, authenticatedSafe);
1253         pfx.write(macData);
1254 
1255         // write PFX to output stream
1256         DerOutputStream pfxout = new DerOutputStream();
1257         pfxout.write(DerValue.tag_Sequence, pfx);
1258         byte[] pfxData = pfxout.toByteArray();
1259         stream.write(pfxData);
1260         stream.flush();
1261     }
1262 
1263     /**
1264      * Gets a <code>KeyStore.Entry</code> for the specified alias
1265      * with the specified protection parameter.
1266      *
1267      * @param alias get the <code>KeyStore.Entry</code> for this alias
1268      * @param protParam the <code>ProtectionParameter</code>
1269      *          used to protect the <code>Entry</code>,
1270      *          which may be <code>null</code>
1271      *
1272      * @return the <code>KeyStore.Entry</code> for the specified alias,
1273      *          or <code>null</code> if there is no such entry
1274      *


1445         if (entry instanceof CertEntry) {
1446             ObjectIdentifier[] trustedKeyUsageValue =
1447                 ((CertEntry) entry).trustedKeyUsage;
1448             if (trustedKeyUsageValue != null) {
1449                 if (trustedKeyUsageValue.length == 1) { // omit brackets
1450                     entry.attributes.add(new PKCS12Attribute(
1451                         TrustedKeyUsage_OID.toString(),
1452                         trustedKeyUsageValue[0].toString()));
1453                 } else { // multi-valued
1454                     entry.attributes.add(new PKCS12Attribute(
1455                         TrustedKeyUsage_OID.toString(),
1456                         Arrays.toString(trustedKeyUsageValue)));
1457                 }
1458             }
1459         }
1460 
1461         return entry.attributes;
1462     }
1463 
1464     /*
1465      * Generate Hash.
1466      */
1467     private byte[] generateHash(byte[] data) throws IOException
1468     {
1469         byte[] digest = null;
1470 
1471         try {
1472             MessageDigest md = MessageDigest.getInstance("SHA1");
1473             md.update(data);
1474             digest = md.digest();
1475         } catch (Exception e) {
1476             throw new IOException("generateHash failed: " + e, e);
1477         }
1478         return digest;
1479     }
1480 
1481 
1482     /*
1483      * Calculate MAC using HMAC algorithm (required for password integrity)
1484      *
1485      * Hash-based MAC algorithm combines secret key with message digest to
1486      * create a message authentication code (MAC)
1487      */
1488     private byte[] calculateMac(char[] passwd, byte[] data)
1489         throws IOException
1490     {
1491         byte[] mData = null;
1492         String algName = "SHA1";
1493 
1494         try {
1495             // Generate a random salt.
1496             byte[] salt = getSalt();
1497 
1498             // generate MAC (MAC key is generated within JCE)
1499             Mac m = Mac.getInstance("HmacPBESHA1");
1500             PBEParameterSpec params =
1501                         new PBEParameterSpec(salt, MAC_ITERATION_COUNT);
1502             SecretKey key = getPBEKey(passwd);
1503             m.init(key, params);
1504             m.update(data);
1505             byte[] macResult = m.doFinal();
1506 
1507             // encode as MacData
1508             MacData macData = new MacData(algName, macResult, salt,
1509                                                 MAC_ITERATION_COUNT);
1510             DerOutputStream bytes = new DerOutputStream();
1511             bytes.write(macData.getEncoded());
1512             mData = bytes.toByteArray();
1513         } catch (Exception e) {
1514             throw new IOException("calculateMac failed: " + e, e);
1515         }
1516         return mData;
1517     }
1518 
1519 
1520     /*
1521      * Validate Certificate Chain
1522      */
1523     private boolean validateChain(Certificate[] certChain)
1524     {
1525         for (int i = 0; i < certChain.length-1; i++) {
1526             X500Principal issuerDN =
1527                 ((X509Certificate)certChain[i]).getIssuerX500Principal();
1528             X500Principal subjectDN =
1529                 ((X509Certificate)certChain[i+1]).getSubjectX500Principal();


1747                     // However, IE/OpenSSL do not impose this restriction.
1748                     bagAttrs = getBagAttributes(
1749                             cert.getSubjectX500Principal().getName(), null,
1750                             entry.attributes);
1751                 }
1752                 if (bagAttrs != null) {
1753                     safeBag.write(bagAttrs);
1754                 }
1755 
1756                 // wrap as Sequence
1757                 out.write(DerValue.tag_Sequence, safeBag);
1758             } // for cert-chain
1759         }
1760 
1761         // wrap as SequenceOf SafeBag
1762         DerOutputStream safeBagValue = new DerOutputStream();
1763         safeBagValue.write(DerValue.tag_SequenceOf, out);
1764         byte[] safeBagData = safeBagValue.toByteArray();
1765 
1766         // encrypt the content (EncryptedContentInfo)

1767         byte[] encrContentInfo = encryptContent(safeBagData, password);
1768 
1769         // -- SEQUENCE of EncryptedData
1770         DerOutputStream encrData = new DerOutputStream();
1771         DerOutputStream encrDataContent = new DerOutputStream();
1772         encrData.putInteger(0);
1773         encrData.write(encrContentInfo);
1774         encrDataContent.write(DerValue.tag_Sequence, encrData);
1775         return encrDataContent.toByteArray();



1776     }
1777 
1778     /*
1779      * Create SafeContent Data content type.
1780      * Includes encrypted secret key in a SafeBag of type SecretBag.
1781      * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.
1782      * Each PKCS8ShroudedKeyBag includes pkcs12 attributes
1783      * (see comments in getBagAttributes)
1784      */
1785     private byte[] createSafeContent()
1786         throws CertificateException, IOException {
1787 
1788         DerOutputStream out = new DerOutputStream();
1789         for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1790 
1791             String alias = e.nextElement();
1792             Entry entry = entries.get(alias);
1793             if (entry == null || (!(entry instanceof KeyEntry))) {
1794                 continue;
1795             }


1864         DerOutputStream safeBagValue = new DerOutputStream();
1865         safeBagValue.write(DerValue.tag_Sequence, out);
1866         return safeBagValue.toByteArray();
1867     }
1868 
1869 
1870     /*
1871      * Encrypt the contents using Password-based (PBE) encryption
1872      * as defined in PKCS #5.
1873      *
1874      * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used
1875      *       to derive the key and IV.
1876      *
1877      * @return encrypted contents encoded as EncryptedContentInfo
1878      */
1879     private byte[] encryptContent(byte[] data, char[] password)
1880         throws IOException {
1881 
1882         byte[] encryptedData = null;
1883 


1884         // create AlgorithmParameters
1885         AlgorithmParameters algParams =
1886                 getPBEAlgorithmParameters("PBEWithSHA1AndRC2_40");
1887         DerOutputStream bytes = new DerOutputStream();
1888         AlgorithmId algId =
1889                 new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);
1890         algId.encode(bytes);
1891         byte[] encodedAlgId = bytes.toByteArray();
1892 
1893         try {
1894             // Use JCE
1895             SecretKey skey = getPBEKey(password);
1896             Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");
1897             cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
1898             encryptedData = cipher.doFinal(data);
1899 







1900             if (debug != null) {
1901                 debug.println("  (Cipher algorithm: " + cipher.getAlgorithm() +
1902                     ")");
1903             }
1904 
1905         } catch (Exception e) {
1906             throw new IOException("Failed to encrypt" +
1907                     " safe contents entry: " + e, e);
1908         }
1909 
1910         // create EncryptedContentInfo
1911         DerOutputStream bytes2 = new DerOutputStream();
1912         bytes2.putOID(ContentInfo.DATA_OID);
1913         bytes2.write(encodedAlgId);
1914 
1915         // Wrap encrypted data in a context-specific tag.
1916         DerOutputStream tmpout2 = new DerOutputStream();
1917         tmpout2.putOctetString(encryptedData);
1918         bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
1919                                         false, (byte)0), tmpout2);
1920 
1921         // wrap EncryptedContentInfo in a Sequence
1922         DerOutputStream out = new DerOutputStream();
1923         out.write(DerValue.tag_Sequence, bytes2);
1924         return out.toByteArray();






1925     }
1926 
1927     /**
1928      * Loads the keystore from the given input stream.
1929      *
1930      * <p>If a password is given, it is used to check the integrity of the
1931      * keystore data. Otherwise, the integrity of the keystore is not checked.
1932      *
1933      * @param stream the input stream from which the keystore is loaded
1934      * @param password the (optional) password used to check the integrity of
1935      * the keystore.
1936      *
1937      * @exception IOException if there is an I/O or format problem with the
1938      * keystore data
1939      * @exception NoSuchAlgorithmException if the algorithm used to check
1940      * the integrity of the keystore cannot be found
1941      * @exception CertificateException if any of the certificates in the
1942      * keystore could not be loaded
1943      */
1944     public synchronized void engineLoad(InputStream stream, char[] password)
1945         throws IOException, NoSuchAlgorithmException, CertificateException
1946     {
1947         DataInputStream dis;
1948         CertificateFactory cf = null;
1949         ByteArrayInputStream bais = null;
1950         byte[] encoded = null;


1951 
1952         if (stream == null)
1953            return;
1954 
1955         // reset the counter
1956         counter = 0;
1957 
1958         DerValue val = new DerValue(stream);
1959         DerInputStream s = val.toDerInputStream();
1960         int version = s.getInteger();
1961 
1962         if (version != VERSION_3) {
1963            throw new IOException("PKCS12 keystore not in version 3 format");
1964         }
1965 
1966         entries.clear();
1967 
1968         /*
1969          * Read the authSafe.
1970          */
1971         byte[] authSafeData;
1972         ContentInfo authSafe = new ContentInfo(s);
1973         ObjectIdentifier contentType = authSafe.getContentType();
1974 
1975         if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1976            authSafeData = authSafe.getData();
1977         } else /* signed data */ {
1978            throw new IOException("public key protected PKCS12 not supported");
1979         }
1980 
1981         DerInputStream as = new DerInputStream(authSafeData);
1982         DerValue[] safeContentsArray = as.getSequence(2);
1983         int count = safeContentsArray.length;
1984 
1985         // reset the counters at the start
1986         privateKeyCount = 0;
1987         secretKeyCount = 0;
1988         certificateCount = 0;
1989 


1990         /*
1991          * Spin over the ContentInfos.
1992          */
1993         for (int i = 0; i < count; i++) {
1994             ContentInfo safeContents;
1995             DerInputStream sci;
1996             byte[] eAlgId = null;
1997 
1998             sci = new DerInputStream(safeContentsArray[i].toByteArray());
1999             safeContents = new ContentInfo(sci);
2000             contentType = safeContents.getContentType();
2001             if (contentType.equals((Object)ContentInfo.DATA_OID)) {
2002 
2003                 if (debug != null) {
2004                     debug.println("Loading PKCS#7 data");
2005                 }
2006 
2007                 loadSafeContents(new DerInputStream(safeContents.getData()));
2008             } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) {
2009                 if (password == null) {
2010 
2011                     if (debug != null) {
2012                         debug.println("Warning: skipping PKCS#7 encryptedData" +
2013                             " - no password was supplied");
2014                     }















2015                     continue;
2016                 }
2017 
2018                 DerInputStream edi =
2019                                 safeContents.getContent().toDerInputStream();
2020                 int edVersion = edi.getInteger();
2021                 DerValue[] seq = edi.getSequence(3);
2022                 if (seq.length != 3) {
2023                     // We require the encryptedContent field, even though
2024                     // it is optional
2025                     throw new IOException("Invalid length for EncryptedContentInfo");
2026                 }
2027                 ObjectIdentifier edContentType = seq[0].getOID();
2028                 eAlgId = seq[1].toByteArray();
2029                 if (!seq[2].isContextSpecific((byte)0)) {
2030                     throw new IOException("unsupported encrypted content type "
2031                                           + seq[2].tag);
2032                 }
2033                 byte newTag = DerValue.tag_OctetString;
2034                 if (seq[2].isConstructed())


2038 
2039                 // parse Algorithm parameters
2040                 DerInputStream in = seq[1].toDerInputStream();
2041                 ObjectIdentifier algOid = in.getOID();
2042                 AlgorithmParameters algParams = parseAlgParameters(algOid, in);
2043 
2044                 PBEParameterSpec pbeSpec;
2045                 int ic = 0;
2046 
2047                 if (algParams != null) {
2048                     try {
2049                         pbeSpec =
2050                             algParams.getParameterSpec(PBEParameterSpec.class);
2051                     } catch (InvalidParameterSpecException ipse) {
2052                         throw new IOException(
2053                             "Invalid PBE algorithm parameters");
2054                     }
2055                     ic = pbeSpec.getIterationCount();
2056 
2057                     if (ic > MAX_ITERATION_COUNT) {
2058                         throw new IOException("PBE iteration count too large");
2059                     }





2060                 }
2061 
2062                 if (debug != null) {
2063                     debug.println("Loading PKCS#7 encryptedData " +
2064                         "(" + new AlgorithmId(algOid).getName() +
2065                         " iterations: " + ic + ")");
2066                 }
2067 
2068                 try {
2069                     RetryWithZero.run(pass -> {
2070                         // Use JCE
2071                         SecretKey skey = getPBEKey(pass);
2072                         Cipher cipher = Cipher.getInstance(
2073                                 mapPBEParamsToAlgorithm(algOid, algParams));
2074                         cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
2075                         loadSafeContents(new DerInputStream(cipher.doFinal(rawData)));
2076                         return null;
2077                     }, password);
2078                 } catch (Exception e) {
2079                     throw new IOException("keystore password was incorrect",
2080                             new UnrecoverableKeyException(
2081                                     "failed to decrypt safe contents entry: " + e));
2082                 }
2083             } else {
2084                 throw new IOException("public key protected PKCS12" +
2085                                         " not supported");
2086             }
2087         }
2088 





2089         // The MacData is optional.
2090         if (password != null && s.available() > 0) {



2091             MacData macData = new MacData(s);
2092             int ic = macData.getIterations();
2093 
2094             try {
2095                 if (ic > MAX_ITERATION_COUNT) {
2096                     throw new InvalidAlgorithmParameterException(
2097                         "MAC iteration count too large: " + ic);
2098                 }
2099 
2100                 String algName =
2101                         macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
2102 
2103                 // Change SHA-1 to SHA1
2104                 algName = algName.replace("-", "");
2105 



2106                 // generate MAC (MAC key is created within JCE)
2107                 Mac m = Mac.getInstance("HmacPBE" + algName);
2108                 PBEParameterSpec params =
2109                         new PBEParameterSpec(macData.getSalt(), ic);
2110 
2111                 RetryWithZero.run(pass -> {
2112                     SecretKey key = getPBEKey(pass);
2113                     m.init(key, params);
2114                     m.update(authSafeData);
2115                     byte[] macResult = m.doFinal();
2116 
2117                     if (debug != null) {
2118                         debug.println("Checking keystore integrity " +
2119                                 "(" + m.getAlgorithm() + " iterations: " + ic + ")");
2120                     }
2121 
2122                     if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
2123                         throw new UnrecoverableKeyException("Failed PKCS12" +
2124                                 " integrity checking");
2125                     }
2126                     return (Void)null;
2127                 }, password);
2128             } catch (Exception e) {
2129                 throw new IOException("Integrity check failed: " + e, e);
2130             }
2131         }



2132 
2133         /*
2134          * Match up private keys with certificate chains.
2135          */
2136         PrivateKeyEntry[] list =
2137             keyList.toArray(new PrivateKeyEntry[keyList.size()]);
2138         for (int m = 0; m < list.length; m++) {
2139             PrivateKeyEntry entry = list[m];
2140             if (entry.keyId != null) {
2141                 ArrayList<X509Certificate> chain =
2142                                 new ArrayList<X509Certificate>();
2143                 X509Certificate cert = findMatchedCertificate(entry);
2144 
2145                 mainloop:
2146                 while (cert != null) {
2147                     // Check for loops in the certificate chain
2148                     if (!chain.isEmpty()) {
2149                         for (X509Certificate chainCert : chain) {
2150                             if (cert.equals(chainCert)) {
2151                                 if (debug != null) {
2152                                     debug.println("Loop detected in " +
2153                                         "certificate chain. Skip adding " +
2154                                         "repeated cert to chain. Subject: " +
2155                                         cert.getSubjectX500Principal()
2156                                             .toString());
2157                                 }
2158                                 break mainloop;
2159                             }
2160                         }
2161                     }
2162                     chain.add(cert);
2163                     X500Principal issuerDN = cert.getIssuerX500Principal();
2164                     if (issuerDN.equals(cert.getSubjectX500Principal())) {
2165                         break;
2166                     }
2167                     cert = certsMap.get(issuerDN);
2168                 }
2169                 /* Update existing KeyEntry in entries table */
2170                 if (chain.size() > 0)
2171                     entry.chain = chain.toArray(new Certificate[chain.size()]);






2172             }
2173         }
2174 
2175         if (debug != null) {
2176             debug.println("PKCS12KeyStore load: private key count: " +
2177                     privateKeyCount + ". secret key count: " + secretKeyCount +
2178                     ". certificate count: " + certificateCount);
2179         }
2180 
2181         certEntries.clear();
2182         certsMap.clear();
2183         keyList.clear();
2184     }
2185 
2186     /**








































2187      * Locates a matched CertEntry from certEntries, and returns its cert.
2188      * @param entry the KeyEntry to match
2189      * @return a certificate, null if not found
2190      */
2191     private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
2192         CertEntry keyIdMatch = null;
2193         CertEntry aliasMatch = null;
2194         for (CertEntry ce: certEntries) {
2195             if (Arrays.equals(entry.keyId, ce.keyId)) {
2196                 keyIdMatch = ce;
2197                 if (entry.alias.equalsIgnoreCase(ce.alias)) {
2198                     // Full match!
2199                     return ce.cert;
2200                 }
2201             } else if (entry.alias.equalsIgnoreCase(ce.alias)) {
2202                 aliasMatch = ce;
2203             }
2204         }
2205         // keyId match first, for compatibility
2206         if (keyIdMatch != null) return keyIdMatch.cert;


2322                             trustedKeyUsage[k] = valSet[k].getOID();
2323                         }
2324                     } else {
2325                         attributes.add(new PKCS12Attribute(encoded));
2326                     }
2327                 }
2328             }
2329 
2330             /*
2331              * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId)
2332              * are optional PKCS12 bagAttributes. But entries in the keyStore
2333              * are identified by their alias. Hence we need to have an
2334              * Unfriendlyname in the alias, if alias is null. The keyId
2335              * attribute is required to match the private key with the
2336              * certificate. If we get a bagItem of type KeyEntry with a
2337              * null keyId, we should skip it entirely.
2338              */
2339             if (bagItem instanceof KeyEntry) {
2340                 KeyEntry entry = (KeyEntry)bagItem;
2341 
2342                 if (bagItem instanceof PrivateKeyEntry) {
2343                     if (keyId == null) {

2344                        // Insert a localKeyID for the privateKey
2345                        // Note: This is a workaround to allow null localKeyID
2346                        // attribute in pkcs12 with one private key entry and
2347                        // associated cert-chain
2348                        if (privateKeyCount == 1) {
2349                             keyId = "01".getBytes("UTF8");
2350                        } else {
2351                             continue;
2352                        }



2353                     }
2354                 }
2355                 entry.keyId = keyId;
2356                 // restore date if it exists
2357                 String keyIdStr = new String(keyId, "UTF8");
2358                 Date date = null;
2359                 if (keyIdStr.startsWith("Time ")) {
2360                     try {
2361                         date = new Date(
2362                                 Long.parseLong(keyIdStr.substring(5)));
2363                     } catch (Exception e) {
2364                         date = null;
2365                     }
2366                 }
2367                 if (date == null) {
2368                     date = new Date();
2369                 }
2370                 entry.date = date;
2371 
2372                 if (bagItem instanceof PrivateKeyEntry) {


2403                         new CertEntry(cert, keyId, alias, trustedKeyUsage,
2404                             attributes);
2405                     entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
2406                 } else {
2407                     certEntries.add(new CertEntry(cert, keyId, alias));
2408                 }
2409                 X500Principal subjectDN = cert.getSubjectX500Principal();
2410                 if (subjectDN != null) {
2411                     if (!certsMap.containsKey(subjectDN)) {
2412                         certsMap.put(subjectDN, cert);
2413                     }
2414                 }
2415             }
2416         }
2417     }
2418 
2419     private String getUnfriendlyName() {
2420         counter++;
2421         return (String.valueOf(counter));
2422     }















































































2423 }


  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.util.SecurityProperties;
  73 import sun.security.x509.AlgorithmId;
  74 import sun.security.pkcs.EncryptedPrivateKeyInfo;
  75 
  76 
  77 /**
  78  * This class provides the keystore implementation referred to as "PKCS12".
  79  * Implements the PKCS#12 PFX protected using the Password privacy mode.
  80  * The contents are protected using Password integrity mode.
  81  *
  82  * Currently these PBE algorithms are used by default:
  83  *  - PBEWithSHA1AndDESede to encrypt private keys, iteration count 50000.
  84  *  - PBEWithSHA1AndRC2_40 to encrypt certificates, iteration count 50000.
  85  *
  86  * The default Mac algorithm is  HmacPBESHA1, iteration count 100000.
  87  *
  88  * Supported encryption of various implementations :
  89  *
  90  * Software and mode.     Certificate encryption  Private key encryption
  91  * ---------------------------------------------------------------------
  92  * MSIE4 (domestic            40 bit RC2.            40 bit RC2
  93  * and xport versions)
  94  * PKCS#12 export.
  95  *
  96  * MSIE4, 5 (domestic         40 bit RC2,            40 bit RC2,
  97  * and export versions)       3 key triple DES       3 key triple DES
  98  * PKCS#12 import.
  99  *
 100  * MSIE5                      40 bit RC2             3 key triple DES,
 101  * PKCS#12 export.                                   with SHA1 (168 bits)
 102  *
 103  * Netscape Communicator      40 bit RC2             3 key triple DES,
 104  * (domestic and export                              with SHA1 (168 bits)
 105  * versions) PKCS#12 export
 106  *


 109  * PKCS#12 import.
 110  *
 111  * Netscape Communicator      All.                   All.
 112  * (domestic or fortified
 113  * version) PKCS#12 import.
 114  *
 115  * OpenSSL PKCS#12 code.      All.                   All.
 116  * ---------------------------------------------------------------------
 117  *
 118  * NOTE: PKCS12 KeyStore supports PrivateKeyEntry and TrustedCertficateEntry.
 119  * PKCS#12 is mainly used to deliver private keys with their associated
 120  * certificate chain and aliases. In a PKCS12 keystore, entries are
 121  * identified by the alias, and a localKeyId is required to match the
 122  * private key with the certificate. Trusted certificate entries are identified
 123  * by the presence of an trustedKeyUsage attribute.
 124  *
 125  * @author Seema Malkani
 126  * @author Jeff Nisewanger
 127  * @author Jan Luehe
 128  *

 129  * @see java.security.KeyStoreSpi



 130  */
 131 public final class PKCS12KeyStore extends KeyStoreSpi {
 132 
 133     public static final int VERSION_3 = 3;
 134 





 135     private static final int MAX_ITERATION_COUNT = 5000000;


 136     private static final int SALT_LEN = 20;
 137 
 138     // friendlyName, localKeyId, trustedKeyUsage
 139     private static final String[] CORE_ATTRIBUTES = {
 140         "1.2.840.113549.1.9.20",
 141         "1.2.840.113549.1.9.21",
 142         "2.16.840.1.113894.746875.1.1"
 143     };
 144 
 145     private static final Debug debug = Debug.getInstance("pkcs12");
 146 
 147     private static final int keyBag[]  = {1, 2, 840, 113549, 1, 12, 10, 1, 2};
 148     private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3};
 149     private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5};
 150 
 151     private static final int pkcs9Name[]  = {1, 2, 840, 113549, 1, 9, 20};
 152     private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21};
 153 
 154     private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1};
 155 




 156     private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13};
 157     // TODO: temporary Oracle OID
 158     /*
 159      * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894)
 160      *   jdk(746875) crypto(1) id-at-trustedKeyUsage(1) }
 161      */
 162     private static final int TrustedKeyUsage[] =
 163                                         {2, 16, 840, 1, 113894, 746875, 1, 1};
 164     private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0};
 165 
 166     private static final ObjectIdentifier PKCS8ShroudedKeyBag_OID;
 167     private static final ObjectIdentifier CertBag_OID;
 168     private static final ObjectIdentifier SecretBag_OID;
 169     private static final ObjectIdentifier PKCS9FriendlyName_OID;
 170     private static final ObjectIdentifier PKCS9LocalKeyId_OID;
 171     private static final ObjectIdentifier PKCS9CertType_OID;
 172     private static final ObjectIdentifier pbes2_OID;
 173     private static final ObjectIdentifier TrustedKeyUsage_OID;
 174     private static final ObjectIdentifier[] AnyUsage;


 175 
 176     private int counter = 0;
 177 
 178     // private key count
 179     // Note: This is a workaround to allow null localKeyID attribute
 180     // in pkcs12 with one private key entry and associated cert-chain
 181     private int privateKeyCount = 0;
 182 
 183     // secret key count
 184     private int secretKeyCount = 0;
 185 
 186     // certificate count
 187     private int certificateCount = 0;
 188 
 189     // Alg/params used for *this* keystore. Initialized as -1 for ic and
 190     // null for algorithm names. When an existing file is read, they will be
 191     // assigned inside engineLoad() so storing an existing keystore uses the
 192     // old alg/params. This makes sure if a keystore is created password-less
 193     // it will be password-less forever. Otherwise, engineStore() will read
 194     // the default values. These fields are always reset when load() is called.
 195     private String certProtectionAlgorithm = null;
 196     private int certPbeIterationCount = -1;
 197     private String macAlgorithm = null;
 198     private int macIterationCount = -1;
 199 
 200     // the source of randomness
 201     private SecureRandom random;
 202 
 203     static {
 204         try {
 205             PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
 206             CertBag_OID = new ObjectIdentifier(certBag);
 207             SecretBag_OID = new ObjectIdentifier(secretBag);
 208             PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name);
 209             PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId);
 210             PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType);




 211             pbes2_OID = new ObjectIdentifier(pbes2);
 212             TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage);
 213             AnyUsage = new ObjectIdentifier[]{
 214                 new ObjectIdentifier(AnyExtendedKeyUsage)};
 215         } catch (IOException ioe) {
 216             throw new AssertionError("OID not initialized", ioe);
 217         }
 218     }
 219 
 220     // A keystore entry and associated attributes
 221     private static class Entry {
 222         Date date; // the creation date of this entry
 223         String alias;
 224         byte[] keyId;
 225         Set<KeyStore.Entry.Attribute> attributes;
 226     }
 227 
 228     // A key entry
 229     private static class KeyEntry extends Entry {
 230     }
 231 
 232     // A private key entry and its supporting certificate chain
 233     private static class PrivateKeyEntry extends KeyEntry {
 234         byte[] protectedPrivKey;
 235         Certificate chain[];
 236     };


 357                 new UnrecoverableKeyException("Private key not stored as "
 358                                  + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
 359             uke.initCause(ioe);
 360             throw uke;
 361         }
 362 
 363        try {
 364             PBEParameterSpec pbeSpec;
 365             int ic;
 366 
 367             if (algParams != null) {
 368                 try {
 369                     pbeSpec =
 370                         algParams.getParameterSpec(PBEParameterSpec.class);
 371                 } catch (InvalidParameterSpecException ipse) {
 372                     throw new IOException("Invalid PBE algorithm parameters");
 373                 }
 374                 ic = pbeSpec.getIterationCount();
 375 
 376                 if (ic > MAX_ITERATION_COUNT) {
 377                     throw new IOException("key PBE iteration count too large");
 378                 }
 379             } else {
 380                 ic = 0;
 381             }
 382 
 383             key = RetryWithZero.run(pass -> {
 384                 // Use JCE
 385                 SecretKey skey = getPBEKey(pass);
 386                 Cipher cipher = Cipher.getInstance(
 387                         mapPBEParamsToAlgorithm(algOid, algParams));
 388                 cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
 389                 byte[] keyInfo = cipher.doFinal(encryptedKey);
 390                 /*
 391                  * Parse the key algorithm and then use a JCA key factory
 392                  * to re-create the key.
 393                  */
 394                 DerValue val = new DerValue(keyInfo);
 395                 DerInputStream in = val.toDerInputStream();
 396                 int i = in.getInteger();
 397                 DerValue[] value = in.getSequence(2);
 398                 if (value.length < 1 || value.length > 2) {
 399                     throw new IOException("Invalid length for AlgorithmIdentifier");
 400                 }
 401                 AlgorithmId algId = new AlgorithmId(value[0].getOID());
 402                 String keyAlgo = algId.getName();
 403 
 404                 // decode private key
 405                 if (entry instanceof PrivateKeyEntry) {
 406                     KeyFactory kfac = KeyFactory.getInstance(keyAlgo);
 407                     PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo);
 408                     Key tmp = kfac.generatePrivate(kspec);
 409 
 410                     if (debug != null) {
 411                         debug.println("Retrieved a protected private key at alias" +
 412                                 " '" + alias + "' (" +
 413                                 mapPBEParamsToAlgorithm(algOid, algParams) +
 414                                 " iterations: " + ic + ")");
 415                     }
 416                     return tmp;
 417                     // decode secret key
 418                 } else {
 419                     byte[] keyBytes = in.getOctetString();
 420                     SecretKeySpec secretKeySpec =
 421                             new SecretKeySpec(keyBytes, keyAlgo);
 422 
 423                     // Special handling required for PBE: needs a PBEKeySpec
 424                     Key tmp;
 425                     if (keyAlgo.startsWith("PBE")) {
 426                         SecretKeyFactory sKeyFactory =
 427                                 SecretKeyFactory.getInstance(keyAlgo);
 428                         KeySpec pbeKeySpec =
 429                                 sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class);
 430                         tmp = sKeyFactory.generateSecret(pbeKeySpec);
 431                     } else {
 432                         tmp = secretKeySpec;
 433                     }
 434 
 435                     if (debug != null) {
 436                         debug.println("Retrieved a protected secret key at alias " +
 437                                 "'" + alias + "' (" +
 438                                 mapPBEParamsToAlgorithm(algOid, algParams) +
 439                                 " iterations: " + ic + ")");
 440                     }
 441                     return tmp;
 442                 }
 443             }, password);
 444 
 445         } catch (Exception e) {
 446             UnrecoverableKeyException uke =
 447                 new UnrecoverableKeyException("Get Key failed: " +
 448                                         e.getMessage());
 449             uke.initCause(e);
 450             throw uke;
 451         }
 452         return key;
 453     }
 454 
 455     /**
 456      * Returns the certificate chain associated with the given alias.
 457      *
 458      * @param alias the alias name


 667                 }
 668                 secretKeyCount++;
 669                 entry = keyEntry;
 670 
 671             } else {
 672                 throw new KeyStoreException("Unsupported Key type");
 673             }
 674 
 675             entry.attributes = new HashSet<>();
 676             if (attributes != null) {
 677                 entry.attributes.addAll(attributes);
 678             }
 679             // set the keyId to current date
 680             entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
 681             // set the alias
 682             entry.alias = alias.toLowerCase(Locale.ENGLISH);
 683             // add the entry
 684             entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
 685 
 686         } catch (Exception nsae) {
 687             throw new KeyStoreException("Key protection" +
 688                        " algorithm not found: " + nsae, nsae);
 689         }
 690     }
 691 
 692     /**
 693      * Assigns the given key (that has already been protected) to the given
 694      * alias.
 695      *
 696      * <p>If the protected key is of type
 697      * <code>java.security.PrivateKey</code>, it must be accompanied by a
 698      * certificate chain certifying the corresponding public key. If the
 699      * underlying keystore implementation is of type <code>jks</code>,
 700      * <code>key</code> must be encoded as an
 701      * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
 702      *
 703      * <p>If the given alias already exists, the keystore information
 704      * associated with it is overridden by the given key (and possibly
 705      * certificate chain).
 706      *
 707      * @param alias the alias name


 764     }
 765 
 766 
 767     /*
 768      * Generate random salt
 769      */
 770     private byte[] getSalt()
 771     {
 772         // Generate a random salt.
 773         byte[] salt = new byte[SALT_LEN];
 774         if (random == null) {
 775            random = new SecureRandom();
 776         }
 777         random.nextBytes(salt);
 778         return salt;
 779     }
 780 
 781     /*
 782      * Generate PBE Algorithm Parameters
 783      */
 784     private AlgorithmParameters getPBEAlgorithmParameters(
 785             String algorithm, int iterationCount) throws IOException {

 786         AlgorithmParameters algParams = null;
 787 
 788         // create PBE parameters from salt and iteration count
 789         PBEParameterSpec paramSpec =
 790                 new PBEParameterSpec(getSalt(), iterationCount);
 791         try {
 792            algParams = AlgorithmParameters.getInstance(algorithm);
 793            algParams.init(paramSpec);
 794         } catch (Exception e) {
 795            throw new IOException("getPBEAlgorithmParameters failed: " +
 796                                  e.getMessage(), e);
 797         }
 798         return algParams;
 799     }
 800 
 801     /*
 802      * parse Algorithm Parameters
 803      */
 804     private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm,
 805         DerInputStream in) throws IOException
 806     {
 807         AlgorithmParameters algParams = null;
 808         try {
 809             DerValue params;
 810             if (in.available() == 0) {


 833     /*
 834      * Generate PBE key
 835      */
 836     private SecretKey getPBEKey(char[] password) throws IOException
 837     {
 838         SecretKey skey = null;
 839 
 840         try {
 841             PBEKeySpec keySpec = new PBEKeySpec(password);
 842             SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
 843             skey = skFac.generateSecret(keySpec);
 844             keySpec.clearPassword();
 845         } catch (Exception e) {
 846            throw new IOException("getSecretKey failed: " +
 847                                  e.getMessage(), e);
 848         }
 849         return skey;
 850     }
 851 
 852     /*
 853      * Encrypt private key or secret key using Password-based encryption (PBE)
 854      * as defined in PKCS#5.
 855      *
 856      * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
 857      *       used to derive the key and IV.
 858      *
 859      * @return encrypted private key or secret key encoded as
 860      *         EncryptedPrivateKeyInfo
 861      */
 862     private byte[] encryptPrivateKey(byte[] data,
 863         KeyStore.PasswordProtection passwordProtection)
 864         throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
 865     {
 866         byte[] key = null;
 867 
 868         try {
 869             String algorithm;
 870             AlgorithmParameters algParams;
 871             AlgorithmId algid;
 872 
 873             // Initialize PBE algorithm and parameters
 874             algorithm = passwordProtection.getProtectionAlgorithm();
 875             if (algorithm != null) {
 876                 AlgorithmParameterSpec algParamSpec =
 877                     passwordProtection.getProtectionParameters();
 878                 if (algParamSpec != null) {
 879                     algParams = AlgorithmParameters.getInstance(algorithm);
 880                     algParams.init(algParamSpec);
 881                 } else {
 882                     algParams = getPBEAlgorithmParameters(algorithm,
 883                             defaultKeyPbeIterationCount());
 884                 }
 885             } else {
 886                 // Check default key protection algorithm for PKCS12 keystores
 887                 algorithm = defaultKeyProtectionAlgorithm();
 888                 algParams = getPBEAlgorithmParameters(algorithm,
 889                         defaultKeyPbeIterationCount());














 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 


 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 new AlgorithmId(algorithm).getName();
 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);


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 
1173         // -- Create PFX
1174         DerOutputStream pfx = new DerOutputStream();
1175 
1176         // PFX version (always write the latest version)
1177         DerOutputStream version = new DerOutputStream();
1178         version.putInteger(VERSION_3);
1179         byte[] pfxVersion = version.toByteArray();
1180         pfx.write(pfxVersion);
1181 
1182         // -- Create AuthSafe
1183         DerOutputStream authSafe = new DerOutputStream();
1184 
1185         // -- Create ContentInfos
1186         DerOutputStream authSafeContentInfo = new DerOutputStream();
1187 
1188         // -- create safeContent Data ContentInfo
1189         if (privateKeyCount > 0 || secretKeyCount > 0) {
1190 
1191             if (debug != null) {
1192                 debug.println("Storing " + (privateKeyCount + secretKeyCount) +
1193                     " protected key(s) in a PKCS#7 data");
1194             }
1195 
1196             byte[] safeContentData = createSafeContent();
1197             ContentInfo dataContentInfo = new ContentInfo(safeContentData);
1198             dataContentInfo.encode(authSafeContentInfo);
1199         }
1200 
1201         // -- create EncryptedContentInfo
1202         if (certificateCount > 0) {
1203 
1204             if (certProtectionAlgorithm == null) {
1205                 certProtectionAlgorithm = defaultCertProtectionAlgorithm();
1206             }
1207             if (certPbeIterationCount < 0) {
1208                 certPbeIterationCount = defaultCertPbeIterationCount();
1209             }
1210 
1211             if (debug != null) {
1212                 debug.println("Storing " + certificateCount +
1213                     " certificate(s) in a PKCS#7 encryptedData");
1214             }
1215 
1216             byte[] encrData = createEncryptedData(password);
1217             if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
1218                 ContentInfo encrContentInfo =
1219                         new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
1220                                 new DerValue(encrData));
1221                 encrContentInfo.encode(authSafeContentInfo);
1222             } else {
1223                 ContentInfo dataContentInfo = new ContentInfo(encrData);
1224                 dataContentInfo.encode(authSafeContentInfo);
1225             }
1226         }
1227 
1228         // wrap as SequenceOf ContentInfos
1229         DerOutputStream cInfo = new DerOutputStream();
1230         cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
1231         byte[] authenticatedSafe = cInfo.toByteArray();
1232 
1233         // Create Encapsulated ContentInfo
1234         ContentInfo contentInfo = new ContentInfo(authenticatedSafe);
1235         contentInfo.encode(authSafe);
1236         byte[] authSafeData = authSafe.toByteArray();
1237         pfx.write(authSafeData);
1238 
1239         // -- MAC
1240         if (macAlgorithm == null) {
1241             macAlgorithm = defaultMacAlgorithm();
1242         }
1243         if (macIterationCount < 0) {
1244             macIterationCount = defaultMacIterationCount();
1245         }
1246         if (!macAlgorithm.equalsIgnoreCase("NONE")) {
1247             byte[] macData = calculateMac(password, authenticatedSafe);
1248             pfx.write(macData);
1249         }
1250         // write PFX to output stream
1251         DerOutputStream pfxout = new DerOutputStream();
1252         pfxout.write(DerValue.tag_Sequence, pfx);
1253         byte[] pfxData = pfxout.toByteArray();
1254         stream.write(pfxData);
1255         stream.flush();
1256     }
1257 
1258     /**
1259      * Gets a <code>KeyStore.Entry</code> for the specified alias
1260      * with the specified protection parameter.
1261      *
1262      * @param alias get the <code>KeyStore.Entry</code> for this alias
1263      * @param protParam the <code>ProtectionParameter</code>
1264      *          used to protect the <code>Entry</code>,
1265      *          which may be <code>null</code>
1266      *
1267      * @return the <code>KeyStore.Entry</code> for the specified alias,
1268      *          or <code>null</code> if there is no such entry
1269      *


1440         if (entry instanceof CertEntry) {
1441             ObjectIdentifier[] trustedKeyUsageValue =
1442                 ((CertEntry) entry).trustedKeyUsage;
1443             if (trustedKeyUsageValue != null) {
1444                 if (trustedKeyUsageValue.length == 1) { // omit brackets
1445                     entry.attributes.add(new PKCS12Attribute(
1446                         TrustedKeyUsage_OID.toString(),
1447                         trustedKeyUsageValue[0].toString()));
1448                 } else { // multi-valued
1449                     entry.attributes.add(new PKCS12Attribute(
1450                         TrustedKeyUsage_OID.toString(),
1451                         Arrays.toString(trustedKeyUsageValue)));
1452                 }
1453             }
1454         }
1455 
1456         return entry.attributes;
1457     }
1458 
1459     /*


















1460      * Calculate MAC using HMAC algorithm (required for password integrity)
1461      *
1462      * Hash-based MAC algorithm combines secret key with message digest to
1463      * create a message authentication code (MAC)
1464      */
1465     private byte[] calculateMac(char[] passwd, byte[] data)
1466         throws IOException
1467     {
1468         byte[] mData = null;
1469         String algName = macAlgorithm.substring(7);
1470 
1471         try {
1472             // Generate a random salt.
1473             byte[] salt = getSalt();
1474 
1475             // generate MAC (MAC key is generated within JCE)
1476             Mac m = Mac.getInstance(macAlgorithm);
1477             PBEParameterSpec params =
1478                         new PBEParameterSpec(salt, macIterationCount);
1479             SecretKey key = getPBEKey(passwd);
1480             m.init(key, params);
1481             m.update(data);
1482             byte[] macResult = m.doFinal();
1483 
1484             // encode as MacData
1485             MacData macData = new MacData(algName, macResult, salt,
1486                     macIterationCount);
1487             DerOutputStream bytes = new DerOutputStream();
1488             bytes.write(macData.getEncoded());
1489             mData = bytes.toByteArray();
1490         } catch (Exception e) {
1491             throw new IOException("calculateMac failed: " + e, e);
1492         }
1493         return mData;
1494     }
1495 
1496 
1497     /*
1498      * Validate Certificate Chain
1499      */
1500     private boolean validateChain(Certificate[] certChain)
1501     {
1502         for (int i = 0; i < certChain.length-1; i++) {
1503             X500Principal issuerDN =
1504                 ((X509Certificate)certChain[i]).getIssuerX500Principal();
1505             X500Principal subjectDN =
1506                 ((X509Certificate)certChain[i+1]).getSubjectX500Principal();


1724                     // However, IE/OpenSSL do not impose this restriction.
1725                     bagAttrs = getBagAttributes(
1726                             cert.getSubjectX500Principal().getName(), null,
1727                             entry.attributes);
1728                 }
1729                 if (bagAttrs != null) {
1730                     safeBag.write(bagAttrs);
1731                 }
1732 
1733                 // wrap as Sequence
1734                 out.write(DerValue.tag_Sequence, safeBag);
1735             } // for cert-chain
1736         }
1737 
1738         // wrap as SequenceOf SafeBag
1739         DerOutputStream safeBagValue = new DerOutputStream();
1740         safeBagValue.write(DerValue.tag_SequenceOf, out);
1741         byte[] safeBagData = safeBagValue.toByteArray();
1742 
1743         // encrypt the content (EncryptedContentInfo)
1744         if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
1745             byte[] encrContentInfo = encryptContent(safeBagData, password);
1746 
1747             // -- SEQUENCE of EncryptedData
1748             DerOutputStream encrData = new DerOutputStream();
1749             DerOutputStream encrDataContent = new DerOutputStream();
1750             encrData.putInteger(0);
1751             encrData.write(encrContentInfo);
1752             encrDataContent.write(DerValue.tag_Sequence, encrData);
1753             return encrDataContent.toByteArray();
1754         } else {
1755             return safeBagData;
1756         }
1757     }
1758 
1759     /*
1760      * Create SafeContent Data content type.
1761      * Includes encrypted secret key in a SafeBag of type SecretBag.
1762      * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.
1763      * Each PKCS8ShroudedKeyBag includes pkcs12 attributes
1764      * (see comments in getBagAttributes)
1765      */
1766     private byte[] createSafeContent()
1767         throws CertificateException, IOException {
1768 
1769         DerOutputStream out = new DerOutputStream();
1770         for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
1771 
1772             String alias = e.nextElement();
1773             Entry entry = entries.get(alias);
1774             if (entry == null || (!(entry instanceof KeyEntry))) {
1775                 continue;
1776             }


1845         DerOutputStream safeBagValue = new DerOutputStream();
1846         safeBagValue.write(DerValue.tag_Sequence, out);
1847         return safeBagValue.toByteArray();
1848     }
1849 
1850 
1851     /*
1852      * Encrypt the contents using Password-based (PBE) encryption
1853      * as defined in PKCS #5.
1854      *
1855      * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used
1856      *       to derive the key and IV.
1857      *
1858      * @return encrypted contents encoded as EncryptedContentInfo
1859      */
1860     private byte[] encryptContent(byte[] data, char[] password)
1861         throws IOException {
1862 
1863         byte[] encryptedData = null;
1864 
1865 
1866         try {
1867             // create AlgorithmParameters
1868             AlgorithmParameters algParams = getPBEAlgorithmParameters(
1869                     certProtectionAlgorithm, certPbeIterationCount);
1870             DerOutputStream bytes = new DerOutputStream();




1871 

1872             // Use JCE
1873             SecretKey skey = getPBEKey(password);
1874             Cipher cipher = Cipher.getInstance(certProtectionAlgorithm);
1875             cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
1876             encryptedData = cipher.doFinal(data);
1877 
1878             AlgorithmId algId = new AlgorithmId(
1879                     mapPBEAlgorithmToOID(certProtectionAlgorithm),
1880                     cipher.getParameters());
1881                     // cipher.getParameters() now has IV
1882             algId.encode(bytes);
1883             byte[] encodedAlgId = bytes.toByteArray();
1884 
1885             if (debug != null) {
1886                 debug.println("  (Cipher algorithm: " + cipher.getAlgorithm() +
1887                         ")");
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         } catch (IOException ioe) {
1906             throw ioe;
1907         } catch (Exception e) {
1908             throw new IOException("Failed to encrypt" +
1909                     " safe contents entry: " + e, e);
1910         }
1911     }
1912 
1913     /**
1914      * Loads the keystore from the given input stream.
1915      *
1916      * <p>If a password is given, it is used to check the integrity of the
1917      * keystore data. Otherwise, the integrity of the keystore is not checked.
1918      *
1919      * @param stream the input stream from which the keystore is loaded
1920      * @param password the (optional) password used to check the integrity of
1921      * the keystore.
1922      *
1923      * @exception IOException if there is an I/O or format problem with the
1924      * keystore data
1925      * @exception NoSuchAlgorithmException if the algorithm used to check
1926      * the integrity of the keystore cannot be found
1927      * @exception CertificateException if any of the certificates in the
1928      * keystore could not be loaded
1929      */
1930     public synchronized void engineLoad(InputStream stream, char[] password)
1931         throws IOException, NoSuchAlgorithmException, CertificateException
1932     {
1933 
1934         // Reset config when loading a different keystore.
1935         certProtectionAlgorithm = null;
1936         certPbeIterationCount = -1;
1937         macAlgorithm = null;
1938         macIterationCount = -1;
1939 
1940         if (stream == null)
1941            return;
1942 
1943         // reset the counter
1944         counter = 0;
1945 
1946         DerValue val = new DerValue(stream);
1947         DerInputStream s = val.toDerInputStream();
1948         int version = s.getInteger();
1949 
1950         if (version != VERSION_3) {
1951            throw new IOException("PKCS12 keystore not in version 3 format");
1952         }
1953 
1954         entries.clear();
1955 
1956         /*
1957          * Read the authSafe.
1958          */
1959         byte[] authSafeData;
1960         ContentInfo authSafe = new ContentInfo(s);
1961         ObjectIdentifier contentType = authSafe.getContentType();
1962 
1963         if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1964            authSafeData = authSafe.getData();
1965         } else /* signed data */ {
1966            throw new IOException("public key protected PKCS12 not supported");
1967         }
1968 
1969         DerInputStream as = new DerInputStream(authSafeData);
1970         DerValue[] safeContentsArray = as.getSequence(2);
1971         int count = safeContentsArray.length;
1972 
1973         // reset the counters at the start
1974         privateKeyCount = 0;
1975         secretKeyCount = 0;
1976         certificateCount = 0;
1977 
1978         boolean seeEncBag = false;
1979 
1980         /*
1981          * Spin over the ContentInfos.
1982          */
1983         for (int i = 0; i < count; i++) {
1984             ContentInfo safeContents;
1985             DerInputStream sci;
1986             byte[] eAlgId = null;
1987 
1988             sci = new DerInputStream(safeContentsArray[i].toByteArray());
1989             safeContents = new ContentInfo(sci);
1990             contentType = safeContents.getContentType();
1991             if (contentType.equals((Object)ContentInfo.DATA_OID)) {
1992 
1993                 if (debug != null) {
1994                     debug.println("Loading PKCS#7 data");
1995                 }
1996 
1997                 loadSafeContents(new DerInputStream(safeContents.getData()));
1998             } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) {
1999                 if (password == null) {
2000 
2001                     if (debug != null) {
2002                         debug.println("Warning: skipping PKCS#7 encryptedData" +
2003                             " - no password was supplied");
2004                     }
2005                     // No password to decrypt ENCRYPTED_DATA_OID. *Skip it*.
2006                     // This means user will see a PrivateKeyEntry without
2007                     // certificates and a whole TrustedCertificateEntry will
2008                     // be lost. This is not a perfect solution but alternative
2009                     // solutions are more disruptive:
2010                     //
2011                     // We cannot just fail, since KeyStore.load(is, null)
2012                     // has been known to never fail because of a null password.
2013                     //
2014                     // We cannot just throw away the whole PrivateKeyEntry,
2015                     // this is too silent and no one will notice anything.
2016                     //
2017                     // We also cannot fail when getCertificate() on such a
2018                     // PrivateKeyEntry is called, since the method has not
2019                     // specified this behavior.
2020                     continue;
2021                 }
2022 
2023                 DerInputStream edi =
2024                                 safeContents.getContent().toDerInputStream();
2025                 int edVersion = edi.getInteger();
2026                 DerValue[] seq = edi.getSequence(3);
2027                 if (seq.length != 3) {
2028                     // We require the encryptedContent field, even though
2029                     // it is optional
2030                     throw new IOException("Invalid length for EncryptedContentInfo");
2031                 }
2032                 ObjectIdentifier edContentType = seq[0].getOID();
2033                 eAlgId = seq[1].toByteArray();
2034                 if (!seq[2].isContextSpecific((byte)0)) {
2035                     throw new IOException("unsupported encrypted content type "
2036                                           + seq[2].tag);
2037                 }
2038                 byte newTag = DerValue.tag_OctetString;
2039                 if (seq[2].isConstructed())


2043 
2044                 // parse Algorithm parameters
2045                 DerInputStream in = seq[1].toDerInputStream();
2046                 ObjectIdentifier algOid = in.getOID();
2047                 AlgorithmParameters algParams = parseAlgParameters(algOid, in);
2048 
2049                 PBEParameterSpec pbeSpec;
2050                 int ic = 0;
2051 
2052                 if (algParams != null) {
2053                     try {
2054                         pbeSpec =
2055                             algParams.getParameterSpec(PBEParameterSpec.class);
2056                     } catch (InvalidParameterSpecException ipse) {
2057                         throw new IOException(
2058                             "Invalid PBE algorithm parameters");
2059                     }
2060                     ic = pbeSpec.getIterationCount();
2061 
2062                     if (ic > MAX_ITERATION_COUNT) {
2063                         throw new IOException("cert PBE iteration count too large");
2064                     }
2065 
2066                     certProtectionAlgorithm
2067                             = mapPBEParamsToAlgorithm(algOid, algParams);
2068                     certPbeIterationCount = ic;
2069                     seeEncBag = true;
2070                 }
2071 
2072                 if (debug != null) {
2073                     debug.println("Loading PKCS#7 encryptedData " +
2074                         "(" + mapPBEParamsToAlgorithm(algOid, algParams) +
2075                         " iterations: " + ic + ")");
2076                 }
2077 
2078                 try {
2079                     RetryWithZero.run(pass -> {
2080                         // Use JCE
2081                         SecretKey skey = getPBEKey(pass);
2082                         Cipher cipher = Cipher.getInstance(
2083                                 mapPBEParamsToAlgorithm(algOid, algParams));
2084                         cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
2085                         loadSafeContents(new DerInputStream(cipher.doFinal(rawData)));
2086                         return null;
2087                     }, password);
2088                 } catch (Exception e) {
2089                     throw new IOException("keystore password was incorrect",
2090                             new UnrecoverableKeyException(
2091                                     "failed to decrypt safe contents entry: " + e));
2092                 }
2093             } else {
2094                 throw new IOException("public key protected PKCS12" +
2095                                         " not supported");
2096             }
2097         }
2098 
2099         // No ENCRYPTED_DATA_OID but see certificate. Must be passwordless.
2100         if (!seeEncBag && certificateCount > 0) {
2101             certProtectionAlgorithm = "NONE";
2102         }
2103 
2104         // The MacData is optional.
2105         if (s.available() > 0) {
2106             // If there is no password, we cannot fail. KeyStore.load(is, null)
2107             // has been known to never fail because of a null password.
2108             if (password != null) {
2109                 MacData macData = new MacData(s);
2110                 int ic = macData.getIterations();
2111 
2112                 try {
2113                     if (ic > MAX_ITERATION_COUNT) {
2114                         throw new InvalidAlgorithmParameterException(
2115                                 "MAC iteration count too large: " + ic);
2116                     }
2117 
2118                     String algName =
2119                             macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
2120 
2121                     // Change SHA-1 to SHA1
2122                     algName = algName.replace("-", "");
2123 
2124                     macAlgorithm = "HmacPBE" + algName;
2125                     macIterationCount = ic;
2126 
2127                     // generate MAC (MAC key is created within JCE)
2128                     Mac m = Mac.getInstance(macAlgorithm);
2129                     PBEParameterSpec params =
2130                             new PBEParameterSpec(macData.getSalt(), ic);
2131 
2132                     RetryWithZero.run(pass -> {
2133                         SecretKey key = getPBEKey(pass);
2134                         m.init(key, params);
2135                         m.update(authSafeData);
2136                         byte[] macResult = m.doFinal();
2137 
2138                         if (debug != null) {
2139                             debug.println("Checking keystore integrity " +
2140                                     "(" + m.getAlgorithm() + " iterations: " + ic + ")");
2141                         }
2142 
2143                         if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
2144                             throw new UnrecoverableKeyException("Failed PKCS12" +
2145                                     " integrity checking");
2146                         }
2147                         return (Void) null;
2148                     }, password);
2149                 } catch (Exception e) {
2150                     throw new IOException("Integrity check failed: " + e, e);
2151                 }
2152             }
2153         } else {
2154             macAlgorithm = "NONE";
2155         }
2156 
2157         /*
2158          * Match up private keys with certificate chains.
2159          */
2160         PrivateKeyEntry[] list =
2161             keyList.toArray(new PrivateKeyEntry[keyList.size()]);
2162         for (int m = 0; m < list.length; m++) {
2163             PrivateKeyEntry entry = list[m];
2164             if (entry.keyId != null) {
2165                 ArrayList<X509Certificate> chain =
2166                                 new ArrayList<X509Certificate>();
2167                 X509Certificate cert = findMatchedCertificate(entry);
2168 
2169                 mainloop:
2170                 while (cert != null) {
2171                     // Check for loops in the certificate chain
2172                     if (!chain.isEmpty()) {
2173                         for (X509Certificate chainCert : chain) {
2174                             if (cert.equals(chainCert)) {
2175                                 if (debug != null) {
2176                                     debug.println("Loop detected in " +
2177                                         "certificate chain. Skip adding " +
2178                                         "repeated cert to chain. Subject: " +
2179                                         cert.getSubjectX500Principal()
2180                                             .toString());
2181                                 }
2182                                 break mainloop;
2183                             }
2184                         }
2185                     }
2186                     chain.add(cert);
2187                     X500Principal issuerDN = cert.getIssuerX500Principal();
2188                     if (issuerDN.equals(cert.getSubjectX500Principal())) {
2189                         break;
2190                     }
2191                     cert = certsMap.get(issuerDN);
2192                 }
2193                 /* Update existing KeyEntry in entries table */
2194                 if (chain.size() > 0) {
2195                     entry.chain = chain.toArray(new Certificate[chain.size()]);
2196                 } else {
2197                     // Remove private key entries where there is no associated
2198                     // certs. Most likely the keystore is loaded with a null
2199                     // password.
2200                     entries.remove(entry);
2201                 }
2202             }
2203         }
2204 
2205         if (debug != null) {
2206             debug.println("PKCS12KeyStore load: private key count: " +
2207                     privateKeyCount + ". secret key count: " + secretKeyCount +
2208                     ". certificate count: " + certificateCount);
2209         }
2210 
2211         certEntries.clear();
2212         certsMap.clear();
2213         keyList.clear();
2214     }
2215 
2216     /**
2217      * Returns if a pkcs12 file is password-less. This means no cert is
2218      * encrypted and there is no Mac. Please note that the private key
2219      * can be encrypted.
2220      *
2221      * This is a simplified version of {@link #engineLoad} that only looks
2222      * at the ContentInfo types.
2223      *
2224      * @param f the pkcs12 file
2225      * @return if it's password-less
2226      * @throws IOException
2227      */
2228     public static boolean isPasswordless(File f) throws IOException {
2229 
2230         try (FileInputStream stream = new FileInputStream(f)) {
2231             DerValue val = new DerValue(stream);
2232             DerInputStream s = val.toDerInputStream();
2233 
2234             s.getInteger(); // skip version
2235 
2236             ContentInfo authSafe = new ContentInfo(s);
2237             DerInputStream as = new DerInputStream(authSafe.getData());
2238             for (DerValue seq : as.getSequence(2)) {
2239                 DerInputStream sci = new DerInputStream(seq.toByteArray());
2240                 ContentInfo safeContents = new ContentInfo(sci);
2241                 if (safeContents.getContentType()
2242                         .equals(ContentInfo.ENCRYPTED_DATA_OID)) {
2243                     // Certificate encrypted
2244                     return false;
2245                 }
2246             }
2247 
2248             if (s.available() > 0) {
2249                 // The MacData exists.
2250                 return false;
2251             }
2252         }
2253         return true;
2254     }
2255 
2256     /**
2257      * Locates a matched CertEntry from certEntries, and returns its cert.
2258      * @param entry the KeyEntry to match
2259      * @return a certificate, null if not found
2260      */
2261     private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
2262         CertEntry keyIdMatch = null;
2263         CertEntry aliasMatch = null;
2264         for (CertEntry ce: certEntries) {
2265             if (Arrays.equals(entry.keyId, ce.keyId)) {
2266                 keyIdMatch = ce;
2267                 if (entry.alias.equalsIgnoreCase(ce.alias)) {
2268                     // Full match!
2269                     return ce.cert;
2270                 }
2271             } else if (entry.alias.equalsIgnoreCase(ce.alias)) {
2272                 aliasMatch = ce;
2273             }
2274         }
2275         // keyId match first, for compatibility
2276         if (keyIdMatch != null) return keyIdMatch.cert;


2392                             trustedKeyUsage[k] = valSet[k].getOID();
2393                         }
2394                     } else {
2395                         attributes.add(new PKCS12Attribute(encoded));
2396                     }
2397                 }
2398             }
2399 
2400             /*
2401              * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId)
2402              * are optional PKCS12 bagAttributes. But entries in the keyStore
2403              * are identified by their alias. Hence we need to have an
2404              * Unfriendlyname in the alias, if alias is null. The keyId
2405              * attribute is required to match the private key with the
2406              * certificate. If we get a bagItem of type KeyEntry with a
2407              * null keyId, we should skip it entirely.
2408              */
2409             if (bagItem instanceof KeyEntry) {
2410                 KeyEntry entry = (KeyEntry)bagItem;
2411 

2412                 if (keyId == null) {
2413                     if (bagItem instanceof PrivateKeyEntry) {
2414                        // Insert a localKeyID for the privateKey
2415                        // Note: This is a workaround to allow null localKeyID
2416                        // attribute in pkcs12 with one private key entry and
2417                        // associated cert-chain
2418                        if (privateKeyCount == 1) {
2419                             keyId = "01".getBytes("UTF8");
2420                        } else {
2421                             continue;
2422                        }
2423                     } else {
2424                         // keyId in a SecretKeyEntry is not significant
2425                         keyId = "00".getBytes("UTF8");
2426                     }
2427                 }
2428                 entry.keyId = keyId;
2429                 // restore date if it exists
2430                 String keyIdStr = new String(keyId, "UTF8");
2431                 Date date = null;
2432                 if (keyIdStr.startsWith("Time ")) {
2433                     try {
2434                         date = new Date(
2435                                 Long.parseLong(keyIdStr.substring(5)));
2436                     } catch (Exception e) {
2437                         date = null;
2438                     }
2439                 }
2440                 if (date == null) {
2441                     date = new Date();
2442                 }
2443                 entry.date = date;
2444 
2445                 if (bagItem instanceof PrivateKeyEntry) {


2476                         new CertEntry(cert, keyId, alias, trustedKeyUsage,
2477                             attributes);
2478                     entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
2479                 } else {
2480                     certEntries.add(new CertEntry(cert, keyId, alias));
2481                 }
2482                 X500Principal subjectDN = cert.getSubjectX500Principal();
2483                 if (subjectDN != null) {
2484                     if (!certsMap.containsKey(subjectDN)) {
2485                         certsMap.put(subjectDN, cert);
2486                     }
2487                 }
2488             }
2489         }
2490     }
2491 
2492     private String getUnfriendlyName() {
2493         counter++;
2494         return (String.valueOf(counter));
2495     }
2496 
2497     // 8076190: Customizing the generation of a PKCS12 keystore
2498 
2499     private static String defaultCertProtectionAlgorithm() {
2500         String result = SecurityProperties.privilegedGetOverridable(
2501                 "keystore.pkcs12.certProtectionAlgorithm");
2502         return (result != null && !result.isEmpty())
2503                 ? result : "PBEWithSHA1AndRC2_40";
2504     }
2505 
2506     private static int defaultCertPbeIterationCount() {
2507         String result = SecurityProperties.privilegedGetOverridable(
2508                 "keystore.pkcs12.certPbeIterationCount");
2509         return (result != null && !result.isEmpty())
2510                 ? string2IC("certPbeIterationCount", result) : 50000;
2511     }
2512 
2513     // Read both "keystore.pkcs12.keyProtectionAlgorithm" and
2514     // "keystore.PKCS12.keyProtectionAlgorithm" for compatibility.
2515     private static String defaultKeyProtectionAlgorithm() {
2516         String result = AccessController.doPrivileged(new PrivilegedAction<String>() {
2517             public String run() {
2518                 String result;
2519                 String name1 = "keystore.pkcs12.keyProtectionAlgorithm";
2520                 String name2 = "keystore.PKCS12.keyProtectionAlgorithm";
2521                 result = System.getProperty(name1);
2522                 if (result != null) {
2523                     return result;
2524                 }
2525                 result = System.getProperty(name2);
2526                 if (result != null) {
2527                     return result;
2528                 }
2529                 result = Security.getProperty(name1);
2530                 if (result != null) {
2531                     return result;
2532                 }
2533                 return Security.getProperty(name2);
2534             }
2535         });
2536         return (result != null && !result.isEmpty())
2537                 ? result : "PBEWithSHA1AndDESede";
2538     }
2539 
2540     private static int defaultKeyPbeIterationCount() {
2541         String result = SecurityProperties.privilegedGetOverridable(
2542                 "keystore.pkcs12.keyPbeIterationCount");
2543         return (result != null && !result.isEmpty())
2544                 ? string2IC("keyPbeIterationCount", result) : 50000;
2545     }
2546 
2547     private static String defaultMacAlgorithm() {
2548         String result = SecurityProperties.privilegedGetOverridable(
2549                 "keystore.pkcs12.macAlgorithm");
2550         return (result != null && !result.isEmpty())
2551                 ? result : "HmacPBESHA1";
2552     }
2553 
2554     private static int defaultMacIterationCount() {
2555         String result = SecurityProperties.privilegedGetOverridable(
2556                 "keystore.pkcs12.macIterationCount");
2557         return (result != null && !result.isEmpty())
2558                 ? string2IC("macIterationCount", result) : 100000;
2559     }
2560 
2561     private static int string2IC(String type, String value) {
2562         int number;
2563         try {
2564             number = Integer.parseInt(value);
2565         } catch (NumberFormatException e) {
2566             throw new IllegalArgumentException("keystore.pkcs12." + type
2567                     + " is not a number: " + value);
2568         }
2569         if (number <= 0 || number > MAX_ITERATION_COUNT) {
2570             throw new IllegalArgumentException("Invalid keystore.pkcs12."
2571                     + type + ": " + value);
2572         }
2573         return number;
2574     }
2575 }
< prev index next >