Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java
38919 views
/*1* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.pkcs12;2627import java.io.*;28import java.security.AccessController;29import java.security.MessageDigest;30import java.security.NoSuchAlgorithmException;31import java.security.Key;32import java.security.KeyFactory;33import java.security.KeyStore;34import java.security.KeyStoreSpi;35import java.security.KeyStoreException;36import java.security.PKCS12Attribute;37import java.security.PrivateKey;38import java.security.PrivilegedAction;39import java.security.UnrecoverableEntryException;40import java.security.UnrecoverableKeyException;41import java.security.SecureRandom;42import java.security.Security;43import java.security.cert.Certificate;44import java.security.cert.CertificateFactory;45import java.security.cert.X509Certificate;46import java.security.cert.CertificateException;47import java.security.spec.AlgorithmParameterSpec;48import java.security.spec.InvalidParameterSpecException;49import java.security.spec.KeySpec;50import java.security.spec.PKCS8EncodedKeySpec;51import java.util.*;5253import java.security.AlgorithmParameters;54import java.security.InvalidAlgorithmParameterException;55import javax.crypto.spec.PBEParameterSpec;56import javax.crypto.spec.PBEKeySpec;57import javax.crypto.spec.SecretKeySpec;58import javax.crypto.SecretKeyFactory;59import javax.crypto.SecretKey;60import javax.crypto.Cipher;61import javax.crypto.Mac;62import javax.security.auth.DestroyFailedException;63import javax.security.auth.x500.X500Principal;6465import sun.security.util.Debug;66import sun.security.util.DerInputStream;67import sun.security.util.DerOutputStream;68import sun.security.util.DerValue;69import sun.security.util.ObjectIdentifier;70import sun.security.pkcs.ContentInfo;71import sun.security.x509.AlgorithmId;72import sun.security.pkcs.EncryptedPrivateKeyInfo;737475/**76* This class provides the keystore implementation referred to as "PKCS12".77* Implements the PKCS#12 PFX protected using the Password privacy mode.78* The contents are protected using Password integrity mode.79*80* Currently we support following PBE algorithms:81* - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys82* - pbeWithSHAAnd40BitRC2CBC to encrypt certificates83*84* Supported encryption of various implementations :85*86* Software and mode. Certificate encryption Private key encryption87* ---------------------------------------------------------------------88* MSIE4 (domestic 40 bit RC2. 40 bit RC289* and xport versions)90* PKCS#12 export.91*92* MSIE4, 5 (domestic 40 bit RC2, 40 bit RC2,93* and export versions) 3 key triple DES 3 key triple DES94* PKCS#12 import.95*96* MSIE5 40 bit RC2 3 key triple DES,97* PKCS#12 export. with SHA1 (168 bits)98*99* Netscape Communicator 40 bit RC2 3 key triple DES,100* (domestic and export with SHA1 (168 bits)101* versions) PKCS#12 export102*103* Netscape Communicator 40 bit ciphers only All.104* (export version)105* PKCS#12 import.106*107* Netscape Communicator All. All.108* (domestic or fortified109* version) PKCS#12 import.110*111* OpenSSL PKCS#12 code. All. All.112* ---------------------------------------------------------------------113*114* NOTE: PKCS12 KeyStore supports PrivateKeyEntry and TrustedCertficateEntry.115* PKCS#12 is mainly used to deliver private keys with their associated116* certificate chain and aliases. In a PKCS12 keystore, entries are117* identified by the alias, and a localKeyId is required to match the118* private key with the certificate. Trusted certificate entries are identified119* by the presence of an trustedKeyUsage attribute.120*121* @author Seema Malkani122* @author Jeff Nisewanger123* @author Jan Luehe124*125* @see KeyProtector126* @see java.security.KeyStoreSpi127* @see KeyTool128*129*130*/131public final class PKCS12KeyStore extends KeyStoreSpi {132133public static final int VERSION_3 = 3;134135private static final String[] KEY_PROTECTION_ALGORITHM = {136"keystore.pkcs12.keyProtectionAlgorithm",137"keystore.PKCS12.keyProtectionAlgorithm"138};139140private static final int MAX_ITERATION_COUNT = 5000000;141private static final int PBE_ITERATION_COUNT = 50000; // default142private static final int MAC_ITERATION_COUNT = 100000; // default143private static final int SALT_LEN = 20;144145// friendlyName, localKeyId, trustedKeyUsage146private static final String[] CORE_ATTRIBUTES = {147"1.2.840.113549.1.9.20",148"1.2.840.113549.1.9.21",149"2.16.840.1.113894.746875.1.1"150};151152private static final Debug debug = Debug.getInstance("pkcs12");153154private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2};155private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3};156private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5};157158private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20};159private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21};160161private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1};162163private static final int pbeWithSHAAnd40BitRC2CBC[] =164{1, 2, 840, 113549, 1, 12, 1, 6};165private static final int pbeWithSHAAnd3KeyTripleDESCBC[] =166{1, 2, 840, 113549, 1, 12, 1, 3};167private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13};168// TODO: temporary Oracle OID169/*170* { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894)171* jdk(746875) crypto(1) id-at-trustedKeyUsage(1) }172*/173private static final int TrustedKeyUsage[] =174{2, 16, 840, 1, 113894, 746875, 1, 1};175private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0};176177private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;178private static ObjectIdentifier CertBag_OID;179private static ObjectIdentifier SecretBag_OID;180private static ObjectIdentifier PKCS9FriendlyName_OID;181private static ObjectIdentifier PKCS9LocalKeyId_OID;182private static ObjectIdentifier PKCS9CertType_OID;183private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID;184private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;185private static ObjectIdentifier pbes2_OID;186private static ObjectIdentifier TrustedKeyUsage_OID;187private static ObjectIdentifier[] AnyUsage;188189private int counter = 0;190191// private key count192// Note: This is a workaround to allow null localKeyID attribute193// in pkcs12 with one private key entry and associated cert-chain194private int privateKeyCount = 0;195196// secret key count197private int secretKeyCount = 0;198199// certificate count200private int certificateCount = 0;201202// the source of randomness203private SecureRandom random;204205static {206try {207PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);208CertBag_OID = new ObjectIdentifier(certBag);209SecretBag_OID = new ObjectIdentifier(secretBag);210PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name);211PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId);212PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType);213pbeWithSHAAnd40BitRC2CBC_OID =214new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC);215pbeWithSHAAnd3KeyTripleDESCBC_OID =216new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);217pbes2_OID = new ObjectIdentifier(pbes2);218TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage);219AnyUsage = new ObjectIdentifier[]{220new ObjectIdentifier(AnyExtendedKeyUsage)};221} catch (IOException ioe) {222// should not happen223}224}225226// A keystore entry and associated attributes227private static class Entry {228Date date; // the creation date of this entry229String alias;230byte[] keyId;231Set<KeyStore.Entry.Attribute> attributes;232}233234// A key entry235private static class KeyEntry extends Entry {236}237238// A private key entry and its supporting certificate chain239private static class PrivateKeyEntry extends KeyEntry {240byte[] protectedPrivKey;241Certificate chain[];242};243244// A secret key245private static class SecretKeyEntry extends KeyEntry {246byte[] protectedSecretKey;247};248249// A certificate entry250private static class CertEntry extends Entry {251final X509Certificate cert;252ObjectIdentifier[] trustedKeyUsage;253254CertEntry(X509Certificate cert, byte[] keyId, String alias) {255this(cert, keyId, alias, null, null);256}257258CertEntry(X509Certificate cert, byte[] keyId, String alias,259ObjectIdentifier[] trustedKeyUsage,260Set<? extends KeyStore.Entry.Attribute> attributes) {261this.date = new Date();262this.cert = cert;263this.keyId = keyId;264this.alias = alias;265this.trustedKeyUsage = trustedKeyUsage;266this.attributes = new HashSet<>();267if (attributes != null) {268this.attributes.addAll(attributes);269}270}271}272273/**274* Private keys and certificates are stored in a map.275* Map entries are keyed by alias names.276*/277private Map<String, Entry> entries =278Collections.synchronizedMap(new LinkedHashMap<String, Entry>());279280private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>();281private LinkedHashMap<X500Principal, X509Certificate> certsMap =282new LinkedHashMap<X500Principal, X509Certificate>();283private ArrayList<CertEntry> certEntries = new ArrayList<CertEntry>();284285/**286* Returns the key associated with the given alias, using the given287* password to recover it.288*289* @param alias the alias name290* @param password the password for recovering the key291*292* @return the requested key, or null if the given alias does not exist293* or does not identify a <i>key entry</i>.294*295* @exception NoSuchAlgorithmException if the algorithm for recovering the296* key cannot be found297* @exception UnrecoverableKeyException if the key cannot be recovered298* (e.g., the given password is wrong).299*/300public Key engineGetKey(String alias, char[] password)301throws NoSuchAlgorithmException, UnrecoverableKeyException302{303Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));304Key key = null;305306if (entry == null || (!(entry instanceof KeyEntry))) {307return null;308}309310// get the encoded private key or secret key311byte[] encrBytes = null;312if (entry instanceof PrivateKeyEntry) {313encrBytes = ((PrivateKeyEntry) entry).protectedPrivKey;314} else if (entry instanceof SecretKeyEntry) {315encrBytes = ((SecretKeyEntry) entry).protectedSecretKey;316} else {317throw new UnrecoverableKeyException("Error locating key");318}319320byte[] encryptedKey;321AlgorithmParameters algParams;322ObjectIdentifier algOid;323324try {325// get the encrypted private key326EncryptedPrivateKeyInfo encrInfo =327new EncryptedPrivateKeyInfo(encrBytes);328encryptedKey = encrInfo.getEncryptedData();329330// parse Algorithm parameters331DerValue val = new DerValue(encrInfo.getAlgorithm().encode());332DerInputStream in = val.toDerInputStream();333algOid = in.getOID();334algParams = parseAlgParameters(algOid, in);335336} catch (IOException ioe) {337UnrecoverableKeyException uke =338new UnrecoverableKeyException("Private key not stored as "339+ "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);340uke.initCause(ioe);341throw uke;342}343344try {345PBEParameterSpec pbeSpec;346int ic = 0;347348if (algParams != null) {349try {350pbeSpec =351algParams.getParameterSpec(PBEParameterSpec.class);352} catch (InvalidParameterSpecException ipse) {353throw new IOException("Invalid PBE algorithm parameters");354}355ic = pbeSpec.getIterationCount();356357if (ic > MAX_ITERATION_COUNT) {358throw new IOException("PBE iteration count too large");359}360}361362byte[] keyInfo;363while (true) {364try {365// Use JCE366SecretKey skey = getPBEKey(password);367Cipher cipher = Cipher.getInstance(368mapPBEParamsToAlgorithm(algOid, algParams));369cipher.init(Cipher.DECRYPT_MODE, skey, algParams);370keyInfo = cipher.doFinal(encryptedKey);371break;372} catch (Exception e) {373if (password.length == 0) {374// Retry using an empty password375// without a NULL terminator.376password = new char[1];377continue;378}379throw e;380}381}382383/*384* Parse the key algorithm and then use a JCA key factory385* to re-create the key.386*/387DerValue val = new DerValue(keyInfo);388DerInputStream in = val.toDerInputStream();389int i = in.getInteger();390DerValue[] value = in.getSequence(2);391if (value.length < 1 || value.length > 2) {392throw new IOException("Invalid length for AlgorithmIdentifier");393}394AlgorithmId algId = new AlgorithmId(value[0].getOID());395String keyAlgo = algId.getName();396397// decode private key398if (entry instanceof PrivateKeyEntry) {399KeyFactory kfac = KeyFactory.getInstance(keyAlgo);400PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo);401key = kfac.generatePrivate(kspec);402403if (debug != null) {404debug.println("Retrieved a protected private key at alias" +405" '" + alias + "' (" +406new AlgorithmId(algOid).getName() +407" iterations: " + ic + ")");408}409410// decode secret key411} else {412byte[] keyBytes = in.getOctetString();413SecretKeySpec secretKeySpec =414new SecretKeySpec(keyBytes, keyAlgo);415416// Special handling required for PBE: needs a PBEKeySpec417if (keyAlgo.startsWith("PBE")) {418SecretKeyFactory sKeyFactory =419SecretKeyFactory.getInstance(keyAlgo);420KeySpec pbeKeySpec =421sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class);422key = sKeyFactory.generateSecret(pbeKeySpec);423} else {424key = secretKeySpec;425}426427if (debug != null) {428debug.println("Retrieved a protected secret key at alias " +429"'" + alias + "' (" +430new AlgorithmId(algOid).getName() +431" iterations: " + ic + ")");432}433}434} catch (Exception e) {435UnrecoverableKeyException uke =436new UnrecoverableKeyException("Get Key failed: " +437e.getMessage());438uke.initCause(e);439throw uke;440}441return key;442}443444/**445* Returns the certificate chain associated with the given alias.446*447* @param alias the alias name448*449* @return the certificate chain (ordered with the user's certificate first450* and the root certificate authority last), or null if the given alias451* does not exist or does not contain a certificate chain (i.e., the given452* alias identifies either a <i>trusted certificate entry</i> or a453* <i>key entry</i> without a certificate chain).454*/455public Certificate[] engineGetCertificateChain(String alias) {456Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));457if (entry != null && entry instanceof PrivateKeyEntry) {458if (((PrivateKeyEntry) entry).chain == null) {459return null;460} else {461462if (debug != null) {463debug.println("Retrieved a " +464((PrivateKeyEntry) entry).chain.length +465"-certificate chain at alias '" + alias + "'");466}467468return ((PrivateKeyEntry) entry).chain.clone();469}470} else {471return null;472}473}474475/**476* Returns the certificate associated with the given alias.477*478* <p>If the given alias name identifies a479* <i>trusted certificate entry</i>, the certificate associated with that480* entry is returned. If the given alias name identifies a481* <i>key entry</i>, the first element of the certificate chain of that482* entry is returned, or null if that entry does not have a certificate483* chain.484*485* @param alias the alias name486*487* @return the certificate, or null if the given alias does not exist or488* does not contain a certificate.489*/490public Certificate engineGetCertificate(String alias) {491Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));492if (entry == null) {493return null;494}495if (entry instanceof CertEntry &&496((CertEntry) entry).trustedKeyUsage != null) {497498if (debug != null) {499if (Arrays.equals(AnyUsage,500((CertEntry) entry).trustedKeyUsage)) {501debug.println("Retrieved a certificate at alias '" + alias +502"' (trusted for any purpose)");503} else {504debug.println("Retrieved a certificate at alias '" + alias +505"' (trusted for limited purposes)");506}507}508509return ((CertEntry) entry).cert;510511} else if (entry instanceof PrivateKeyEntry) {512if (((PrivateKeyEntry) entry).chain == null) {513return null;514} else {515516if (debug != null) {517debug.println("Retrieved a certificate at alias '" + alias +518"'");519}520521return ((PrivateKeyEntry) entry).chain[0];522}523524} else {525return null;526}527}528529/**530* Returns the creation date of the entry identified by the given alias.531*532* @param alias the alias name533*534* @return the creation date of this entry, or null if the given alias does535* not exist536*/537public Date engineGetCreationDate(String alias) {538Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));539if (entry != null) {540return new Date(entry.date.getTime());541} else {542return null;543}544}545546/**547* Assigns the given key to the given alias, protecting it with the given548* password.549*550* <p>If the given key is of type <code>java.security.PrivateKey</code>,551* it must be accompanied by a certificate chain certifying the552* corresponding public key.553*554* <p>If the given alias already exists, the keystore information555* associated with it is overridden by the given key (and possibly556* certificate chain).557*558* @param alias the alias name559* @param key the key to be associated with the alias560* @param password the password to protect the key561* @param chain the certificate chain for the corresponding public562* key (only required if the given key is of type563* <code>java.security.PrivateKey</code>).564*565* @exception KeyStoreException if the given key cannot be protected, or566* this operation fails for some other reason567*/568public synchronized void engineSetKeyEntry(String alias, Key key,569char[] password, Certificate[] chain)570throws KeyStoreException571{572KeyStore.PasswordProtection passwordProtection =573new KeyStore.PasswordProtection(password);574575try {576setKeyEntry(alias, key, passwordProtection, chain, null);577578} finally {579try {580passwordProtection.destroy();581} catch (DestroyFailedException dfe) {582// ignore583}584}585}586587/*588* Sets a key entry (with attributes, when present)589*/590private void setKeyEntry(String alias, Key key,591KeyStore.PasswordProtection passwordProtection, Certificate[] chain,592Set<KeyStore.Entry.Attribute> attributes)593throws KeyStoreException594{595try {596Entry entry;597598if (key instanceof PrivateKey) {599PrivateKeyEntry keyEntry = new PrivateKeyEntry();600keyEntry.date = new Date();601602if ((key.getFormat().equals("PKCS#8")) ||603(key.getFormat().equals("PKCS8"))) {604605if (debug != null) {606debug.println(607"Setting a protected private key at alias '" +608alias + "'");609}610611// Encrypt the private key612keyEntry.protectedPrivKey =613encryptPrivateKey(key.getEncoded(), passwordProtection);614} else {615throw new KeyStoreException("Private key is not encoded" +616"as PKCS#8");617}618619// clone the chain620if (chain != null) {621// validate cert-chain622if ((chain.length > 1) && (!validateChain(chain)))623throw new KeyStoreException("Certificate chain is " +624"not valid");625keyEntry.chain = chain.clone();626certificateCount += chain.length;627628if (debug != null) {629debug.println("Setting a " + chain.length +630"-certificate chain at alias '" + alias + "'");631}632}633privateKeyCount++;634entry = keyEntry;635636} else if (key instanceof SecretKey) {637SecretKeyEntry keyEntry = new SecretKeyEntry();638keyEntry.date = new Date();639640// Encode secret key in a PKCS#8641DerOutputStream pkcs8 = new DerOutputStream();642DerOutputStream secretKeyInfo = new DerOutputStream();643secretKeyInfo.putInteger(0);644AlgorithmId algId = AlgorithmId.get(key.getAlgorithm());645algId.encode(secretKeyInfo);646secretKeyInfo.putOctetString(key.getEncoded());647pkcs8.write(DerValue.tag_Sequence, secretKeyInfo);648649// Encrypt the secret key (using same PBE as for private keys)650keyEntry.protectedSecretKey =651encryptPrivateKey(pkcs8.toByteArray(), passwordProtection);652653if (debug != null) {654debug.println("Setting a protected secret key at alias '" +655alias + "'");656}657secretKeyCount++;658entry = keyEntry;659660} else {661throw new KeyStoreException("Unsupported Key type");662}663664entry.attributes = new HashSet<>();665if (attributes != null) {666entry.attributes.addAll(attributes);667}668// set the keyId to current date669entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");670// set the alias671entry.alias = alias.toLowerCase(Locale.ENGLISH);672// add the entry673entries.put(alias.toLowerCase(Locale.ENGLISH), entry);674675} catch (Exception nsae) {676throw new KeyStoreException("Key protection " +677" algorithm not found: " + nsae, nsae);678}679}680681/**682* Assigns the given key (that has already been protected) to the given683* alias.684*685* <p>If the protected key is of type686* <code>java.security.PrivateKey</code>, it must be accompanied by a687* certificate chain certifying the corresponding public key. If the688* underlying keystore implementation is of type <code>jks</code>,689* <code>key</code> must be encoded as an690* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.691*692* <p>If the given alias already exists, the keystore information693* associated with it is overridden by the given key (and possibly694* certificate chain).695*696* @param alias the alias name697* @param key the key (in protected format) to be associated with the alias698* @param chain the certificate chain for the corresponding public699* key (only useful if the protected key is of type700* <code>java.security.PrivateKey</code>).701*702* @exception KeyStoreException if this operation fails.703*/704public synchronized void engineSetKeyEntry(String alias, byte[] key,705Certificate[] chain)706throws KeyStoreException707{708// Private key must be encoded as EncryptedPrivateKeyInfo709// as defined in PKCS#8710try {711new EncryptedPrivateKeyInfo(key);712} catch (IOException ioe) {713throw new KeyStoreException("Private key is not stored"714+ " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe);715}716717PrivateKeyEntry entry = new PrivateKeyEntry();718entry.date = new Date();719720if (debug != null) {721debug.println("Setting a protected private key at alias '" +722alias + "'");723}724725try {726// set the keyId to current date727entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");728} catch (UnsupportedEncodingException ex) {729// Won't happen730}731// set the alias732entry.alias = alias.toLowerCase(Locale.ENGLISH);733734entry.protectedPrivKey = key.clone();735if (chain != null) {736// validate cert-chain737if ((chain.length > 1) && (!validateChain(chain))) {738throw new KeyStoreException("Certificate chain is "739+ "not valid");740}741entry.chain = chain.clone();742certificateCount += chain.length;743744if (debug != null) {745debug.println("Setting a " + entry.chain.length +746"-certificate chain at alias '" + alias + "'");747}748}749750// add the entry751privateKeyCount++;752entries.put(alias.toLowerCase(Locale.ENGLISH), entry);753}754755756/*757* Generate random salt758*/759private byte[] getSalt()760{761// Generate a random salt.762byte[] salt = new byte[SALT_LEN];763if (random == null) {764random = new SecureRandom();765}766random.nextBytes(salt);767return salt;768}769770/*771* Generate PBE Algorithm Parameters772*/773private AlgorithmParameters getPBEAlgorithmParameters(String algorithm)774throws IOException775{776AlgorithmParameters algParams = null;777778// create PBE parameters from salt and iteration count779PBEParameterSpec paramSpec =780new PBEParameterSpec(getSalt(), PBE_ITERATION_COUNT);781try {782algParams = AlgorithmParameters.getInstance(algorithm);783algParams.init(paramSpec);784} catch (Exception e) {785throw new IOException("getPBEAlgorithmParameters failed: " +786e.getMessage(), e);787}788return algParams;789}790791/*792* parse Algorithm Parameters793*/794private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm,795DerInputStream in) throws IOException796{797AlgorithmParameters algParams = null;798try {799DerValue params;800if (in.available() == 0) {801params = null;802} else {803params = in.getDerValue();804if (params.tag == DerValue.tag_Null) {805params = null;806}807}808if (params != null) {809if (algorithm.equals((Object)pbes2_OID)) {810algParams = AlgorithmParameters.getInstance("PBES2");811} else {812algParams = AlgorithmParameters.getInstance("PBE");813}814algParams.init(params.toByteArray());815}816} catch (Exception e) {817throw new IOException("parseAlgParameters failed: " +818e.getMessage(), e);819}820return algParams;821}822823/*824* Generate PBE key825*/826private SecretKey getPBEKey(char[] password) throws IOException827{828SecretKey skey = null;829830try {831PBEKeySpec keySpec = new PBEKeySpec(password);832SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");833skey = skFac.generateSecret(keySpec);834keySpec.clearPassword();835} catch (Exception e) {836throw new IOException("getSecretKey failed: " +837e.getMessage(), e);838}839return skey;840}841842/*843* Encrypt private key using Password-based encryption (PBE)844* as defined in PKCS#5.845*846* NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is847* used to derive the key and IV.848*849* @return encrypted private key encoded as EncryptedPrivateKeyInfo850*/851private byte[] encryptPrivateKey(byte[] data,852KeyStore.PasswordProtection passwordProtection)853throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException854{855byte[] key = null;856857try {858String algorithm;859AlgorithmParameters algParams;860AlgorithmId algid;861862// Initialize PBE algorithm and parameters863algorithm = passwordProtection.getProtectionAlgorithm();864if (algorithm != null) {865AlgorithmParameterSpec algParamSpec =866passwordProtection.getProtectionParameters();867if (algParamSpec != null) {868algParams = AlgorithmParameters.getInstance(algorithm);869algParams.init(algParamSpec);870} else {871algParams = getPBEAlgorithmParameters(algorithm);872}873} else {874// Check default key protection algorithm for PKCS12 keystores875algorithm = AccessController.doPrivileged(876new PrivilegedAction<String>() {877public String run() {878String prop =879Security.getProperty(880KEY_PROTECTION_ALGORITHM[0]);881if (prop == null) {882prop = Security.getProperty(883KEY_PROTECTION_ALGORITHM[1]);884}885return prop;886}887});888if (algorithm == null || algorithm.isEmpty()) {889algorithm = "PBEWithSHA1AndDESede";890}891algParams = getPBEAlgorithmParameters(algorithm);892}893894ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm);895if (pbeOID == null) {896throw new IOException("PBE algorithm '" + algorithm +897" 'is not supported for key entry protection");898}899900// Use JCE901SecretKey skey = getPBEKey(passwordProtection.getPassword());902Cipher cipher = Cipher.getInstance(algorithm);903cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);904byte[] encryptedKey = cipher.doFinal(data);905algid = new AlgorithmId(pbeOID, cipher.getParameters());906907if (debug != null) {908debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() +909")");910}911912// wrap encrypted private key in EncryptedPrivateKeyInfo913// as defined in PKCS#8914EncryptedPrivateKeyInfo encrInfo =915new EncryptedPrivateKeyInfo(algid, encryptedKey);916key = encrInfo.getEncoded();917} catch (Exception e) {918UnrecoverableKeyException uke =919new UnrecoverableKeyException("Encrypt Private Key failed: "920+ e.getMessage());921uke.initCause(e);922throw uke;923}924925return key;926}927928/*929* Map a PBE algorithm name onto its object identifier930*/931private static ObjectIdentifier mapPBEAlgorithmToOID(String algorithm)932throws NoSuchAlgorithmException {933// Check for PBES2 algorithms934if (algorithm.toLowerCase(Locale.ENGLISH).startsWith("pbewithhmacsha")) {935return pbes2_OID;936}937return AlgorithmId.get(algorithm).getOID();938}939940/*941* Map a PBE algorithm parameters onto its algorithm name942*/943private static String mapPBEParamsToAlgorithm(ObjectIdentifier algorithm,944AlgorithmParameters algParams) throws NoSuchAlgorithmException {945// Check for PBES2 algorithms946if (algorithm.equals((Object)pbes2_OID) && algParams != null) {947return algParams.toString();948}949return algorithm.toString();950}951952/**953* Assigns the given certificate to the given alias.954*955* <p>If the given alias already exists in this keystore and identifies a956* <i>trusted certificate entry</i>, the certificate associated with it is957* overridden by the given certificate.958*959* @param alias the alias name960* @param cert the certificate961*962* @exception KeyStoreException if the given alias already exists and does963* not identify a <i>trusted certificate entry</i>, or this operation fails964* for some other reason.965*/966public synchronized void engineSetCertificateEntry(String alias,967Certificate cert) throws KeyStoreException968{969setCertEntry(alias, cert, null);970}971972/*973* Sets a trusted cert entry (with attributes, when present)974*/975private void setCertEntry(String alias, Certificate cert,976Set<KeyStore.Entry.Attribute> attributes) throws KeyStoreException {977978Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));979if (entry != null && entry instanceof KeyEntry) {980throw new KeyStoreException("Cannot overwrite own certificate");981}982983CertEntry certEntry =984new CertEntry((X509Certificate) cert, null, alias, AnyUsage,985attributes);986certificateCount++;987entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);988989if (debug != null) {990debug.println("Setting a trusted certificate at alias '" + alias +991"'");992}993}994995/**996* Deletes the entry identified by the given alias from this keystore.997*998* @param alias the alias name999*1000* @exception KeyStoreException if the entry cannot be removed.1001*/1002public synchronized void engineDeleteEntry(String alias)1003throws KeyStoreException1004{1005if (debug != null) {1006debug.println("Removing entry at alias '" + alias + "'");1007}10081009Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));1010if (entry instanceof PrivateKeyEntry) {1011PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;1012if (keyEntry.chain != null) {1013certificateCount -= keyEntry.chain.length;1014}1015privateKeyCount--;1016} else if (entry instanceof CertEntry) {1017certificateCount--;1018} else if (entry instanceof SecretKeyEntry) {1019secretKeyCount--;1020}1021entries.remove(alias.toLowerCase(Locale.ENGLISH));1022}10231024/**1025* Lists all the alias names of this keystore.1026*1027* @return enumeration of the alias names1028*/1029public Enumeration<String> engineAliases() {1030return Collections.enumeration(entries.keySet());1031}10321033/**1034* Checks if the given alias exists in this keystore.1035*1036* @param alias the alias name1037*1038* @return true if the alias exists, false otherwise1039*/1040public boolean engineContainsAlias(String alias) {1041return entries.containsKey(alias.toLowerCase(Locale.ENGLISH));1042}10431044/**1045* Retrieves the number of entries in this keystore.1046*1047* @return the number of entries in this keystore1048*/1049public int engineSize() {1050return entries.size();1051}10521053/**1054* Returns true if the entry identified by the given alias is a1055* <i>key entry</i>, and false otherwise.1056*1057* @return true if the entry identified by the given alias is a1058* <i>key entry</i>, false otherwise.1059*/1060public boolean engineIsKeyEntry(String alias) {1061Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));1062if (entry != null && entry instanceof KeyEntry) {1063return true;1064} else {1065return false;1066}1067}10681069/**1070* Returns true if the entry identified by the given alias is a1071* <i>trusted certificate entry</i>, and false otherwise.1072*1073* @return true if the entry identified by the given alias is a1074* <i>trusted certificate entry</i>, false otherwise.1075*/1076public boolean engineIsCertificateEntry(String alias) {1077Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));1078if (entry != null && entry instanceof CertEntry &&1079((CertEntry) entry).trustedKeyUsage != null) {1080return true;1081} else {1082return false;1083}1084}10851086/**1087* Determines if the keystore {@code Entry} for the specified1088* {@code alias} is an instance or subclass of the specified1089* {@code entryClass}.1090*1091* @param alias the alias name1092* @param entryClass the entry class1093*1094* @return true if the keystore {@code Entry} for the specified1095* {@code alias} is an instance or subclass of the1096* specified {@code entryClass}, false otherwise1097*1098* @since 1.51099*/1100@Override1101public boolean1102engineEntryInstanceOf(String alias,1103Class<? extends KeyStore.Entry> entryClass)1104{1105if (entryClass == KeyStore.TrustedCertificateEntry.class) {1106return engineIsCertificateEntry(alias);1107}11081109Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));1110if (entryClass == KeyStore.PrivateKeyEntry.class) {1111return (entry != null && entry instanceof PrivateKeyEntry);1112}1113if (entryClass == KeyStore.SecretKeyEntry.class) {1114return (entry != null && entry instanceof SecretKeyEntry);1115}1116return false;1117}11181119/**1120* Returns the (alias) name of the first keystore entry whose certificate1121* matches the given certificate.1122*1123* <p>This method attempts to match the given certificate with each1124* keystore entry. If the entry being considered1125* is a <i>trusted certificate entry</i>, the given certificate is1126* compared to that entry's certificate. If the entry being considered is1127* a <i>key entry</i>, the given certificate is compared to the first1128* element of that entry's certificate chain (if a chain exists).1129*1130* @param cert the certificate to match with.1131*1132* @return the (alias) name of the first entry with matching certificate,1133* or null if no such entry exists in this keystore.1134*/1135public String engineGetCertificateAlias(Certificate cert) {1136Certificate certElem = null;11371138for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {1139String alias = e.nextElement();1140Entry entry = entries.get(alias);1141if (entry instanceof PrivateKeyEntry) {1142if (((PrivateKeyEntry) entry).chain != null) {1143certElem = ((PrivateKeyEntry) entry).chain[0];1144}1145} else if (entry instanceof CertEntry &&1146((CertEntry) entry).trustedKeyUsage != null) {1147certElem = ((CertEntry) entry).cert;1148} else {1149continue;1150}1151if (certElem != null && certElem.equals(cert)) {1152return alias;1153}1154}1155return null;1156}11571158/**1159* Stores this keystore to the given output stream, and protects its1160* integrity with the given password.1161*1162* @param stream the output stream to which this keystore is written.1163* @param password the password to generate the keystore integrity check1164*1165* @exception IOException if there was an I/O problem with data1166* @exception NoSuchAlgorithmException if the appropriate data integrity1167* algorithm could not be found1168* @exception CertificateException if any of the certificates included in1169* the keystore data could not be stored1170*/1171public synchronized void engineStore(OutputStream stream, char[] password)1172throws IOException, NoSuchAlgorithmException, CertificateException1173{1174// password is mandatory when storing1175if (password == null) {1176throw new IllegalArgumentException("password can't be null");1177}11781179// -- Create PFX1180DerOutputStream pfx = new DerOutputStream();11811182// PFX version (always write the latest version)1183DerOutputStream version = new DerOutputStream();1184version.putInteger(VERSION_3);1185byte[] pfxVersion = version.toByteArray();1186pfx.write(pfxVersion);11871188// -- Create AuthSafe1189DerOutputStream authSafe = new DerOutputStream();11901191// -- Create ContentInfos1192DerOutputStream authSafeContentInfo = new DerOutputStream();11931194// -- create safeContent Data ContentInfo1195if (privateKeyCount > 0 || secretKeyCount > 0) {11961197if (debug != null) {1198debug.println("Storing " + (privateKeyCount + secretKeyCount) +1199" protected key(s) in a PKCS#7 data");1200}12011202byte[] safeContentData = createSafeContent();1203ContentInfo dataContentInfo = new ContentInfo(safeContentData);1204dataContentInfo.encode(authSafeContentInfo);1205}12061207// -- create EncryptedContentInfo1208if (certificateCount > 0) {12091210if (debug != null) {1211debug.println("Storing " + certificateCount +1212" certificate(s) in a PKCS#7 encryptedData");1213}12141215byte[] encrData = createEncryptedData(password);1216ContentInfo encrContentInfo =1217new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,1218new DerValue(encrData));1219encrContentInfo.encode(authSafeContentInfo);1220}12211222// wrap as SequenceOf ContentInfos1223DerOutputStream cInfo = new DerOutputStream();1224cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);1225byte[] authenticatedSafe = cInfo.toByteArray();12261227// Create Encapsulated ContentInfo1228ContentInfo contentInfo = new ContentInfo(authenticatedSafe);1229contentInfo.encode(authSafe);1230byte[] authSafeData = authSafe.toByteArray();1231pfx.write(authSafeData);12321233// -- MAC1234byte[] macData = calculateMac(password, authenticatedSafe);1235pfx.write(macData);12361237// write PFX to output stream1238DerOutputStream pfxout = new DerOutputStream();1239pfxout.write(DerValue.tag_Sequence, pfx);1240byte[] pfxData = pfxout.toByteArray();1241stream.write(pfxData);1242stream.flush();1243}12441245/**1246* Gets a <code>KeyStore.Entry</code> for the specified alias1247* with the specified protection parameter.1248*1249* @param alias get the <code>KeyStore.Entry</code> for this alias1250* @param protParam the <code>ProtectionParameter</code>1251* used to protect the <code>Entry</code>,1252* which may be <code>null</code>1253*1254* @return the <code>KeyStore.Entry</code> for the specified alias,1255* or <code>null</code> if there is no such entry1256*1257* @exception KeyStoreException if the operation failed1258* @exception NoSuchAlgorithmException if the algorithm for recovering the1259* entry cannot be found1260* @exception UnrecoverableEntryException if the specified1261* <code>protParam</code> were insufficient or invalid1262* @exception UnrecoverableKeyException if the entry is a1263* <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>1264* and the specified <code>protParam</code> does not contain1265* the information needed to recover the key (e.g. wrong password)1266*1267* @since 1.51268*/1269@Override1270public KeyStore.Entry engineGetEntry(String alias,1271KeyStore.ProtectionParameter protParam)1272throws KeyStoreException, NoSuchAlgorithmException,1273UnrecoverableEntryException {12741275if (!engineContainsAlias(alias)) {1276return null;1277}12781279Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));1280if (protParam == null) {1281if (engineIsCertificateEntry(alias)) {1282if (entry instanceof CertEntry &&1283((CertEntry) entry).trustedKeyUsage != null) {12841285if (debug != null) {1286debug.println("Retrieved a trusted certificate at " +1287"alias '" + alias + "'");1288}12891290return new KeyStore.TrustedCertificateEntry(1291((CertEntry)entry).cert, getAttributes(entry));1292}1293} else {1294throw new UnrecoverableKeyException1295("requested entry requires a password");1296}1297}12981299if (protParam instanceof KeyStore.PasswordProtection) {1300if (engineIsCertificateEntry(alias)) {1301throw new UnsupportedOperationException1302("trusted certificate entries are not password-protected");1303} else if (engineIsKeyEntry(alias)) {1304KeyStore.PasswordProtection pp =1305(KeyStore.PasswordProtection)protParam;1306char[] password = pp.getPassword();13071308Key key = engineGetKey(alias, password);1309if (key instanceof PrivateKey) {1310Certificate[] chain = engineGetCertificateChain(alias);13111312return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain,1313getAttributes(entry));13141315} else if (key instanceof SecretKey) {13161317return new KeyStore.SecretKeyEntry((SecretKey)key,1318getAttributes(entry));1319}1320} else if (!engineIsKeyEntry(alias)) {1321throw new UnsupportedOperationException1322("untrusted certificate entries are not " +1323"password-protected");1324}1325}13261327throw new UnsupportedOperationException();1328}13291330/**1331* Saves a <code>KeyStore.Entry</code> under the specified alias.1332* The specified protection parameter is used to protect the1333* <code>Entry</code>.1334*1335* <p> If an entry already exists for the specified alias,1336* it is overridden.1337*1338* @param alias save the <code>KeyStore.Entry</code> under this alias1339* @param entry the <code>Entry</code> to save1340* @param protParam the <code>ProtectionParameter</code>1341* used to protect the <code>Entry</code>,1342* which may be <code>null</code>1343*1344* @exception KeyStoreException if this operation fails1345*1346* @since 1.51347*/1348@Override1349public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,1350KeyStore.ProtectionParameter protParam) throws KeyStoreException {13511352// get password1353if (protParam != null &&1354!(protParam instanceof KeyStore.PasswordProtection)) {1355throw new KeyStoreException("unsupported protection parameter");1356}1357KeyStore.PasswordProtection pProtect = null;1358if (protParam != null) {1359pProtect = (KeyStore.PasswordProtection)protParam;1360}13611362// set entry1363if (entry instanceof KeyStore.TrustedCertificateEntry) {1364if (protParam != null && pProtect.getPassword() != null) {1365// pre-1.5 style setCertificateEntry did not allow password1366throw new KeyStoreException1367("trusted certificate entries are not password-protected");1368} else {1369KeyStore.TrustedCertificateEntry tce =1370(KeyStore.TrustedCertificateEntry)entry;1371setCertEntry(alias, tce.getTrustedCertificate(),1372tce.getAttributes());13731374return;1375}1376} else if (entry instanceof KeyStore.PrivateKeyEntry) {1377if (pProtect == null || pProtect.getPassword() == null) {1378// pre-1.5 style setKeyEntry required password1379throw new KeyStoreException1380("non-null password required to create PrivateKeyEntry");1381} else {1382KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry;1383setKeyEntry(alias, pke.getPrivateKey(), pProtect,1384pke.getCertificateChain(), pke.getAttributes());13851386return;1387}1388} else if (entry instanceof KeyStore.SecretKeyEntry) {1389if (pProtect == null || pProtect.getPassword() == null) {1390// pre-1.5 style setKeyEntry required password1391throw new KeyStoreException1392("non-null password required to create SecretKeyEntry");1393} else {1394KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;1395setKeyEntry(alias, ske.getSecretKey(), pProtect,1396(Certificate[])null, ske.getAttributes());13971398return;1399}1400}14011402throw new KeyStoreException1403("unsupported entry type: " + entry.getClass().getName());1404}14051406/*1407* Assemble the entry attributes1408*/1409private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) {14101411if (entry.attributes == null) {1412entry.attributes = new HashSet<>();1413}14141415// friendlyName1416entry.attributes.add(new PKCS12Attribute(1417PKCS9FriendlyName_OID.toString(), entry.alias));14181419// localKeyID1420byte[] keyIdValue = entry.keyId;1421if (keyIdValue != null) {1422entry.attributes.add(new PKCS12Attribute(1423PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue)));1424}14251426// trustedKeyUsage1427if (entry instanceof CertEntry) {1428ObjectIdentifier[] trustedKeyUsageValue =1429((CertEntry) entry).trustedKeyUsage;1430if (trustedKeyUsageValue != null) {1431if (trustedKeyUsageValue.length == 1) { // omit brackets1432entry.attributes.add(new PKCS12Attribute(1433TrustedKeyUsage_OID.toString(),1434trustedKeyUsageValue[0].toString()));1435} else { // multi-valued1436entry.attributes.add(new PKCS12Attribute(1437TrustedKeyUsage_OID.toString(),1438Arrays.toString(trustedKeyUsageValue)));1439}1440}1441}14421443return entry.attributes;1444}14451446/*1447* Generate Hash.1448*/1449private byte[] generateHash(byte[] data) throws IOException1450{1451byte[] digest = null;14521453try {1454MessageDigest md = MessageDigest.getInstance("SHA1");1455md.update(data);1456digest = md.digest();1457} catch (Exception e) {1458throw new IOException("generateHash failed: " + e, e);1459}1460return digest;1461}146214631464/*1465* Calculate MAC using HMAC algorithm (required for password integrity)1466*1467* Hash-based MAC algorithm combines secret key with message digest to1468* create a message authentication code (MAC)1469*/1470private byte[] calculateMac(char[] passwd, byte[] data)1471throws IOException1472{1473byte[] mData = null;1474String algName = "SHA1";14751476try {1477// Generate a random salt.1478byte[] salt = getSalt();14791480// generate MAC (MAC key is generated within JCE)1481Mac m = Mac.getInstance("HmacPBESHA1");1482PBEParameterSpec params =1483new PBEParameterSpec(salt, MAC_ITERATION_COUNT);1484SecretKey key = getPBEKey(passwd);1485m.init(key, params);1486m.update(data);1487byte[] macResult = m.doFinal();14881489// encode as MacData1490MacData macData = new MacData(algName, macResult, salt,1491MAC_ITERATION_COUNT);1492DerOutputStream bytes = new DerOutputStream();1493bytes.write(macData.getEncoded());1494mData = bytes.toByteArray();1495} catch (Exception e) {1496throw new IOException("calculateMac failed: " + e, e);1497}1498return mData;1499}150015011502/*1503* Validate Certificate Chain1504*/1505private boolean validateChain(Certificate[] certChain)1506{1507for (int i = 0; i < certChain.length-1; i++) {1508X500Principal issuerDN =1509((X509Certificate)certChain[i]).getIssuerX500Principal();1510X500Principal subjectDN =1511((X509Certificate)certChain[i+1]).getSubjectX500Principal();1512if (!(issuerDN.equals(subjectDN)))1513return false;1514}15151516// Check for loops in the chain. If there are repeated certs,1517// the Set of certs in the chain will contain fewer certs than1518// the chain1519Set<Certificate> set = new HashSet<>(Arrays.asList(certChain));1520return set.size() == certChain.length;1521}152215231524/*1525* Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage.1526*1527* Although attributes are optional, they could be required.1528* For e.g. localKeyId attribute is required to match the1529* private key with the associated end-entity certificate.1530* The trustedKeyUsage attribute is used to denote a trusted certificate.1531*1532* PKCS8ShroudedKeyBags include unique localKeyID and friendlyName.1533* CertBags may or may not include attributes depending on the type1534* of Certificate. In end-entity certificates, localKeyID should be1535* unique, and the corresponding private key should have the same1536* localKeyID. For trusted CA certs in the cert-chain, localKeyID1537* attribute is not required, hence most vendors don't include it.1538* NSS/Netscape require it to be unique or null, where as IE/OpenSSL1539* ignore it.1540*1541* Here is a list of pkcs12 attribute values in CertBags.1542*1543* PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE1544* --------------------------------------------------------------1545* LocalKeyId1546* (In EE cert only,1547* NULL in CA certs) true true true true1548*1549* friendlyName unique same/ same/ unique1550* unique unique/1551* null1552* trustedKeyUsage - - - true1553*1554* Note: OpenSSL adds friendlyName for end-entity cert only, and1555* removes the localKeyID and friendlyName for CA certs.1556* If the CertBag did not have a friendlyName, most vendors will1557* add it, and assign it to the DN of the cert.1558*/1559private byte[] getBagAttributes(String alias, byte[] keyId,1560Set<KeyStore.Entry.Attribute> attributes) throws IOException {1561return getBagAttributes(alias, keyId, null, attributes);1562}15631564private byte[] getBagAttributes(String alias, byte[] keyId,1565ObjectIdentifier[] trustedUsage,1566Set<KeyStore.Entry.Attribute> attributes) throws IOException {15671568byte[] localKeyID = null;1569byte[] friendlyName = null;1570byte[] trustedKeyUsage = null;15711572// return null if all three attributes are null1573if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) {1574return null;1575}15761577// SafeBag Attributes1578DerOutputStream bagAttrs = new DerOutputStream();15791580// Encode the friendlyname oid.1581if (alias != null) {1582DerOutputStream bagAttr1 = new DerOutputStream();1583bagAttr1.putOID(PKCS9FriendlyName_OID);1584DerOutputStream bagAttrContent1 = new DerOutputStream();1585DerOutputStream bagAttrValue1 = new DerOutputStream();1586bagAttrContent1.putBMPString(alias);1587bagAttr1.write(DerValue.tag_Set, bagAttrContent1);1588bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1);1589friendlyName = bagAttrValue1.toByteArray();1590}15911592// Encode the localkeyId oid.1593if (keyId != null) {1594DerOutputStream bagAttr2 = new DerOutputStream();1595bagAttr2.putOID(PKCS9LocalKeyId_OID);1596DerOutputStream bagAttrContent2 = new DerOutputStream();1597DerOutputStream bagAttrValue2 = new DerOutputStream();1598bagAttrContent2.putOctetString(keyId);1599bagAttr2.write(DerValue.tag_Set, bagAttrContent2);1600bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2);1601localKeyID = bagAttrValue2.toByteArray();1602}16031604// Encode the trustedKeyUsage oid.1605if (trustedUsage != null) {1606DerOutputStream bagAttr3 = new DerOutputStream();1607bagAttr3.putOID(TrustedKeyUsage_OID);1608DerOutputStream bagAttrContent3 = new DerOutputStream();1609DerOutputStream bagAttrValue3 = new DerOutputStream();1610for (ObjectIdentifier usage : trustedUsage) {1611bagAttrContent3.putOID(usage);1612}1613bagAttr3.write(DerValue.tag_Set, bagAttrContent3);1614bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3);1615trustedKeyUsage = bagAttrValue3.toByteArray();1616}16171618DerOutputStream attrs = new DerOutputStream();1619if (friendlyName != null) {1620attrs.write(friendlyName);1621}1622if (localKeyID != null) {1623attrs.write(localKeyID);1624}1625if (trustedKeyUsage != null) {1626attrs.write(trustedKeyUsage);1627}16281629if (attributes != null) {1630for (KeyStore.Entry.Attribute attribute : attributes) {1631String attributeName = attribute.getName();1632// skip friendlyName, localKeyId and trustedKeyUsage1633if (CORE_ATTRIBUTES[0].equals(attributeName) ||1634CORE_ATTRIBUTES[1].equals(attributeName) ||1635CORE_ATTRIBUTES[2].equals(attributeName)) {1636continue;1637}1638attrs.write(((PKCS12Attribute) attribute).getEncoded());1639}1640}16411642bagAttrs.write(DerValue.tag_Set, attrs);1643return bagAttrs.toByteArray();1644}16451646/*1647* Create EncryptedData content type, that contains EncryptedContentInfo.1648* Includes certificates in individual SafeBags of type CertBag.1649* Each CertBag may include pkcs12 attributes1650* (see comments in getBagAttributes)1651*/1652private byte[] createEncryptedData(char[] password)1653throws CertificateException, IOException1654{1655DerOutputStream out = new DerOutputStream();1656for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {16571658String alias = e.nextElement();1659Entry entry = entries.get(alias);16601661// certificate chain1662Certificate[] certs;16631664if (entry instanceof PrivateKeyEntry) {1665PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;1666if (keyEntry.chain != null) {1667certs = keyEntry.chain;1668} else {1669certs = new Certificate[0];1670}1671} else if (entry instanceof CertEntry) {1672certs = new Certificate[]{((CertEntry) entry).cert};1673} else {1674certs = new Certificate[0];1675}16761677for (int i = 0; i < certs.length; i++) {1678// create SafeBag of Type CertBag1679DerOutputStream safeBag = new DerOutputStream();1680safeBag.putOID(CertBag_OID);16811682// create a CertBag1683DerOutputStream certBag = new DerOutputStream();1684certBag.putOID(PKCS9CertType_OID);16851686// write encoded certs in a context-specific tag1687DerOutputStream certValue = new DerOutputStream();1688X509Certificate cert = (X509Certificate) certs[i];1689certValue.putOctetString(cert.getEncoded());1690certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,1691true, (byte) 0), certValue);16921693// wrap CertBag in a Sequence1694DerOutputStream certout = new DerOutputStream();1695certout.write(DerValue.tag_Sequence, certBag);1696byte[] certBagValue = certout.toByteArray();16971698// Wrap the CertBag encoding in a context-specific tag.1699DerOutputStream bagValue = new DerOutputStream();1700bagValue.write(certBagValue);1701// write SafeBag Value1702safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,1703true, (byte) 0), bagValue);17041705// write SafeBag Attributes1706// All Certs should have a unique friendlyName.1707// This change is made to meet NSS requirements.1708byte[] bagAttrs = null;1709if (i == 0) {1710// Only End-Entity Cert should have a localKeyId.1711if (entry instanceof KeyEntry) {1712KeyEntry keyEntry = (KeyEntry) entry;1713bagAttrs =1714getBagAttributes(keyEntry.alias, keyEntry.keyId,1715keyEntry.attributes);1716} else {1717CertEntry certEntry = (CertEntry) entry;1718bagAttrs =1719getBagAttributes(certEntry.alias, certEntry.keyId,1720certEntry.trustedKeyUsage,1721certEntry.attributes);1722}1723} else {1724// Trusted root CA certs and Intermediate CA certs do not1725// need to have a localKeyId, and hence localKeyId is null1726// This change is made to meet NSS/Netscape requirements.1727// NSS pkcs12 library requires trusted CA certs in the1728// certificate chain to have unique or null localKeyID.1729// However, IE/OpenSSL do not impose this restriction.1730bagAttrs = getBagAttributes(1731cert.getSubjectX500Principal().getName(), null,1732entry.attributes);1733}1734if (bagAttrs != null) {1735safeBag.write(bagAttrs);1736}17371738// wrap as Sequence1739out.write(DerValue.tag_Sequence, safeBag);1740} // for cert-chain1741}17421743// wrap as SequenceOf SafeBag1744DerOutputStream safeBagValue = new DerOutputStream();1745safeBagValue.write(DerValue.tag_SequenceOf, out);1746byte[] safeBagData = safeBagValue.toByteArray();17471748// encrypt the content (EncryptedContentInfo)1749byte[] encrContentInfo = encryptContent(safeBagData, password);17501751// -- SEQUENCE of EncryptedData1752DerOutputStream encrData = new DerOutputStream();1753DerOutputStream encrDataContent = new DerOutputStream();1754encrData.putInteger(0);1755encrData.write(encrContentInfo);1756encrDataContent.write(DerValue.tag_Sequence, encrData);1757return encrDataContent.toByteArray();1758}17591760/*1761* Create SafeContent Data content type.1762* Includes encrypted secret key in a SafeBag of type SecretBag.1763* Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.1764* Each PKCS8ShroudedKeyBag includes pkcs12 attributes1765* (see comments in getBagAttributes)1766*/1767private byte[] createSafeContent()1768throws CertificateException, IOException {17691770DerOutputStream out = new DerOutputStream();1771for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {17721773String alias = e.nextElement();1774Entry entry = entries.get(alias);1775if (entry == null || (!(entry instanceof KeyEntry))) {1776continue;1777}1778DerOutputStream safeBag = new DerOutputStream();1779KeyEntry keyEntry = (KeyEntry) entry;17801781// DER encode the private key1782if (keyEntry instanceof PrivateKeyEntry) {1783// Create SafeBag of type pkcs8ShroudedKeyBag1784safeBag.putOID(PKCS8ShroudedKeyBag_OID);17851786// get the encrypted private key1787byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey;1788EncryptedPrivateKeyInfo encrInfo = null;1789try {1790encrInfo = new EncryptedPrivateKeyInfo(encrBytes);17911792} catch (IOException ioe) {1793throw new IOException("Private key not stored as "1794+ "PKCS#8 EncryptedPrivateKeyInfo"1795+ ioe.getMessage());1796}17971798// Wrap the EncryptedPrivateKeyInfo in a context-specific tag.1799DerOutputStream bagValue = new DerOutputStream();1800bagValue.write(encrInfo.getEncoded());1801safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,1802true, (byte) 0), bagValue);18031804// DER encode the secret key1805} else if (keyEntry instanceof SecretKeyEntry) {1806// Create SafeBag of type SecretBag1807safeBag.putOID(SecretBag_OID);18081809// Create a SecretBag1810DerOutputStream secretBag = new DerOutputStream();1811secretBag.putOID(PKCS8ShroudedKeyBag_OID);18121813// Write secret key in a context-specific tag1814DerOutputStream secretKeyValue = new DerOutputStream();1815secretKeyValue.putOctetString(1816((SecretKeyEntry) keyEntry).protectedSecretKey);1817secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,1818true, (byte) 0), secretKeyValue);18191820// Wrap SecretBag in a Sequence1821DerOutputStream secretBagSeq = new DerOutputStream();1822secretBagSeq.write(DerValue.tag_Sequence, secretBag);1823byte[] secretBagValue = secretBagSeq.toByteArray();18241825// Wrap the secret bag in a context-specific tag.1826DerOutputStream bagValue = new DerOutputStream();1827bagValue.write(secretBagValue);18281829// Write SafeBag value1830safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,1831true, (byte) 0), bagValue);1832} else {1833continue; // skip this entry1834}18351836// write SafeBag Attributes1837byte[] bagAttrs =1838getBagAttributes(alias, entry.keyId, entry.attributes);1839safeBag.write(bagAttrs);18401841// wrap as Sequence1842out.write(DerValue.tag_Sequence, safeBag);1843}18441845// wrap as Sequence1846DerOutputStream safeBagValue = new DerOutputStream();1847safeBagValue.write(DerValue.tag_Sequence, out);1848return safeBagValue.toByteArray();1849}185018511852/*1853* Encrypt the contents using Password-based (PBE) encryption1854* as defined in PKCS #5.1855*1856* NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used1857* to derive the key and IV.1858*1859* @return encrypted contents encoded as EncryptedContentInfo1860*/1861private byte[] encryptContent(byte[] data, char[] password)1862throws IOException {18631864byte[] encryptedData = null;18651866// create AlgorithmParameters1867AlgorithmParameters algParams =1868getPBEAlgorithmParameters("PBEWithSHA1AndRC2_40");1869DerOutputStream bytes = new DerOutputStream();1870AlgorithmId algId =1871new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);1872algId.encode(bytes);1873byte[] encodedAlgId = bytes.toByteArray();18741875try {1876// Use JCE1877SecretKey skey = getPBEKey(password);1878Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");1879cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);1880encryptedData = cipher.doFinal(data);18811882if (debug != null) {1883debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() +1884")");1885}18861887} catch (Exception e) {1888throw new IOException("Failed to encrypt" +1889" safe contents entry: " + e, e);1890}18911892// create EncryptedContentInfo1893DerOutputStream bytes2 = new DerOutputStream();1894bytes2.putOID(ContentInfo.DATA_OID);1895bytes2.write(encodedAlgId);18961897// Wrap encrypted data in a context-specific tag.1898DerOutputStream tmpout2 = new DerOutputStream();1899tmpout2.putOctetString(encryptedData);1900bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,1901false, (byte)0), tmpout2);19021903// wrap EncryptedContentInfo in a Sequence1904DerOutputStream out = new DerOutputStream();1905out.write(DerValue.tag_Sequence, bytes2);1906return out.toByteArray();1907}19081909/**1910* Loads the keystore from the given input stream.1911*1912* <p>If a password is given, it is used to check the integrity of the1913* keystore data. Otherwise, the integrity of the keystore is not checked.1914*1915* @param stream the input stream from which the keystore is loaded1916* @param password the (optional) password used to check the integrity of1917* the keystore.1918*1919* @exception IOException if there is an I/O or format problem with the1920* keystore data1921* @exception NoSuchAlgorithmException if the algorithm used to check1922* the integrity of the keystore cannot be found1923* @exception CertificateException if any of the certificates in the1924* keystore could not be loaded1925*/1926public synchronized void engineLoad(InputStream stream, char[] password)1927throws IOException, NoSuchAlgorithmException, CertificateException1928{1929DataInputStream dis;1930CertificateFactory cf = null;1931ByteArrayInputStream bais = null;1932byte[] encoded = null;19331934if (stream == null)1935return;19361937// reset the counter1938counter = 0;19391940DerValue val = new DerValue(stream);1941DerInputStream s = val.toDerInputStream();1942int version = s.getInteger();19431944if (version != VERSION_3) {1945throw new IOException("PKCS12 keystore not in version 3 format");1946}19471948entries.clear();19491950/*1951* Read the authSafe.1952*/1953byte[] authSafeData;1954ContentInfo authSafe = new ContentInfo(s);1955ObjectIdentifier contentType = authSafe.getContentType();19561957if (contentType.equals((Object)ContentInfo.DATA_OID)) {1958authSafeData = authSafe.getData();1959} else /* signed data */ {1960throw new IOException("public key protected PKCS12 not supported");1961}19621963DerInputStream as = new DerInputStream(authSafeData);1964DerValue[] safeContentsArray = as.getSequence(2);1965int count = safeContentsArray.length;19661967// reset the counters at the start1968privateKeyCount = 0;1969secretKeyCount = 0;1970certificateCount = 0;19711972/*1973* Spin over the ContentInfos.1974*/1975for (int i = 0; i < count; i++) {1976byte[] safeContentsData;1977ContentInfo safeContents;1978DerInputStream sci;1979byte[] eAlgId = null;19801981sci = new DerInputStream(safeContentsArray[i].toByteArray());1982safeContents = new ContentInfo(sci);1983contentType = safeContents.getContentType();1984safeContentsData = null;1985if (contentType.equals((Object)ContentInfo.DATA_OID)) {19861987if (debug != null) {1988debug.println("Loading PKCS#7 data");1989}19901991safeContentsData = safeContents.getData();1992} else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) {1993if (password == null) {19941995if (debug != null) {1996debug.println("Warning: skipping PKCS#7 encryptedData" +1997" - no password was supplied");1998}1999continue;2000}20012002DerInputStream edi =2003safeContents.getContent().toDerInputStream();2004int edVersion = edi.getInteger();2005DerValue[] seq = edi.getSequence(3);2006if (seq.length != 3) {2007// We require the encryptedContent field, even though2008// it is optional2009throw new IOException("Invalid length for EncryptedContentInfo");2010}2011ObjectIdentifier edContentType = seq[0].getOID();2012eAlgId = seq[1].toByteArray();2013if (!seq[2].isContextSpecific((byte)0)) {2014throw new IOException("unsupported encrypted content type "2015+ seq[2].tag);2016}2017byte newTag = DerValue.tag_OctetString;2018if (seq[2].isConstructed())2019newTag |= 0x20;2020seq[2].resetTag(newTag);2021safeContentsData = seq[2].getOctetString();20222023// parse Algorithm parameters2024DerInputStream in = seq[1].toDerInputStream();2025ObjectIdentifier algOid = in.getOID();2026AlgorithmParameters algParams = parseAlgParameters(algOid, in);20272028PBEParameterSpec pbeSpec;2029int ic = 0;20302031if (algParams != null) {2032try {2033pbeSpec =2034algParams.getParameterSpec(PBEParameterSpec.class);2035} catch (InvalidParameterSpecException ipse) {2036throw new IOException(2037"Invalid PBE algorithm parameters");2038}2039ic = pbeSpec.getIterationCount();20402041if (ic > MAX_ITERATION_COUNT) {2042throw new IOException("PBE iteration count too large");2043}2044}20452046if (debug != null) {2047debug.println("Loading PKCS#7 encryptedData " +2048"(" + new AlgorithmId(algOid).getName() +2049" iterations: " + ic + ")");2050}20512052while (true) {2053try {2054// Use JCE2055SecretKey skey = getPBEKey(password);2056Cipher cipher = Cipher.getInstance(algOid.toString());2057cipher.init(Cipher.DECRYPT_MODE, skey, algParams);2058safeContentsData = cipher.doFinal(safeContentsData);2059break;2060} catch (Exception e) {2061if (password.length == 0) {2062// Retry using an empty password2063// without a NULL terminator.2064password = new char[1];2065continue;2066}2067throw new IOException("keystore password was incorrect",2068new UnrecoverableKeyException(2069"failed to decrypt safe contents entry: " + e));2070}2071}2072} else {2073throw new IOException("public key protected PKCS12" +2074" not supported");2075}2076DerInputStream sc = new DerInputStream(safeContentsData);2077loadSafeContents(sc, password);2078}20792080// The MacData is optional.2081if (password != null && s.available() > 0) {2082MacData macData = new MacData(s);2083int ic = macData.getIterations();20842085try {2086if (ic > MAX_ITERATION_COUNT) {2087throw new InvalidAlgorithmParameterException(2088"MAC iteration count too large: " + ic);2089}20902091String algName =2092macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);20932094// Change SHA-1 to SHA12095algName = algName.replace("-", "");20962097// generate MAC (MAC key is created within JCE)2098Mac m = Mac.getInstance("HmacPBE" + algName);2099PBEParameterSpec params =2100new PBEParameterSpec(macData.getSalt(), ic);2101SecretKey key = getPBEKey(password);2102m.init(key, params);2103m.update(authSafeData);2104byte[] macResult = m.doFinal();21052106if (debug != null) {2107debug.println("Checking keystore integrity " +2108"(" + m.getAlgorithm() + " iterations: " + ic + ")");2109}21102111if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {2112throw new UnrecoverableKeyException("Failed PKCS12" +2113" integrity checking");2114}2115} catch (Exception e) {2116throw new IOException("Integrity check failed: " + e, e);2117}2118}21192120/*2121* Match up private keys with certificate chains.2122*/2123PrivateKeyEntry[] list =2124keyList.toArray(new PrivateKeyEntry[keyList.size()]);2125for (int m = 0; m < list.length; m++) {2126PrivateKeyEntry entry = list[m];2127if (entry.keyId != null) {2128ArrayList<X509Certificate> chain =2129new ArrayList<X509Certificate>();2130X509Certificate cert = findMatchedCertificate(entry);21312132mainloop:2133while (cert != null) {2134// Check for loops in the certificate chain2135if (!chain.isEmpty()) {2136for (X509Certificate chainCert : chain) {2137if (cert.equals(chainCert)) {2138if (debug != null) {2139debug.println("Loop detected in " +2140"certificate chain. Skip adding " +2141"repeated cert to chain. Subject: " +2142cert.getSubjectX500Principal()2143.toString());2144}2145break mainloop;2146}2147}2148}2149chain.add(cert);2150X500Principal issuerDN = cert.getIssuerX500Principal();2151if (issuerDN.equals(cert.getSubjectX500Principal())) {2152break;2153}2154cert = certsMap.get(issuerDN);2155}2156/* Update existing KeyEntry in entries table */2157if (chain.size() > 0)2158entry.chain = chain.toArray(new Certificate[chain.size()]);2159}2160}21612162if (debug != null) {2163debug.println("PKCS12KeyStore load: private key count: " +2164privateKeyCount + ". secret key count: " + secretKeyCount +2165". certificate count: " + certificateCount);2166}21672168certEntries.clear();2169certsMap.clear();2170keyList.clear();2171}21722173/**2174* Locates a matched CertEntry from certEntries, and returns its cert.2175* @param entry the KeyEntry to match2176* @return a certificate, null if not found2177*/2178private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {2179CertEntry keyIdMatch = null;2180CertEntry aliasMatch = null;2181for (CertEntry ce: certEntries) {2182if (Arrays.equals(entry.keyId, ce.keyId)) {2183keyIdMatch = ce;2184if (entry.alias.equalsIgnoreCase(ce.alias)) {2185// Full match!2186return ce.cert;2187}2188} else if (entry.alias.equalsIgnoreCase(ce.alias)) {2189aliasMatch = ce;2190}2191}2192// keyId match first, for compatibility2193if (keyIdMatch != null) return keyIdMatch.cert;2194else if (aliasMatch != null) return aliasMatch.cert;2195else return null;2196}21972198private void loadSafeContents(DerInputStream stream, char[] password)2199throws IOException, NoSuchAlgorithmException, CertificateException2200{2201DerValue[] safeBags = stream.getSequence(2);2202int count = safeBags.length;22032204/*2205* Spin over the SafeBags.2206*/2207for (int i = 0; i < count; i++) {2208ObjectIdentifier bagId;2209DerInputStream sbi;2210DerValue bagValue;2211Object bagItem = null;22122213sbi = safeBags[i].toDerInputStream();2214bagId = sbi.getOID();2215bagValue = sbi.getDerValue();2216if (!bagValue.isContextSpecific((byte)0)) {2217throw new IOException("unsupported PKCS12 bag value type "2218+ bagValue.tag);2219}2220bagValue = bagValue.data.getDerValue();2221if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) {2222PrivateKeyEntry kEntry = new PrivateKeyEntry();2223kEntry.protectedPrivKey = bagValue.toByteArray();2224bagItem = kEntry;2225privateKeyCount++;2226} else if (bagId.equals((Object)CertBag_OID)) {2227DerInputStream cs = new DerInputStream(bagValue.toByteArray());2228DerValue[] certValues = cs.getSequence(2);2229if (certValues.length != 2) {2230throw new IOException("Invalid length for CertBag");2231}2232ObjectIdentifier certId = certValues[0].getOID();2233if (!certValues[1].isContextSpecific((byte)0)) {2234throw new IOException("unsupported PKCS12 cert value type "2235+ certValues[1].tag);2236}2237DerValue certValue = certValues[1].data.getDerValue();2238CertificateFactory cf = CertificateFactory.getInstance("X509");2239X509Certificate cert;2240cert = (X509Certificate)cf.generateCertificate2241(new ByteArrayInputStream(certValue.getOctetString()));2242bagItem = cert;2243certificateCount++;2244} else if (bagId.equals((Object)SecretBag_OID)) {2245DerInputStream ss = new DerInputStream(bagValue.toByteArray());2246DerValue[] secretValues = ss.getSequence(2);2247if (secretValues.length != 2) {2248throw new IOException("Invalid length for SecretBag");2249}2250ObjectIdentifier secretId = secretValues[0].getOID();2251if (!secretValues[1].isContextSpecific((byte)0)) {2252throw new IOException(2253"unsupported PKCS12 secret value type "2254+ secretValues[1].tag);2255}2256DerValue secretValue = secretValues[1].data.getDerValue();2257SecretKeyEntry kEntry = new SecretKeyEntry();2258kEntry.protectedSecretKey = secretValue.getOctetString();2259bagItem = kEntry;2260secretKeyCount++;2261} else {22622263if (debug != null) {2264debug.println("Unsupported PKCS12 bag type: " + bagId);2265}2266}22672268DerValue[] attrSet;2269try {2270attrSet = sbi.getSet(3);2271} catch (IOException e) {2272// entry does not have attributes2273// Note: CA certs can have no attributes2274// OpenSSL generates pkcs12 with no attr for CA certs.2275attrSet = null;2276}22772278String alias = null;2279byte[] keyId = null;2280ObjectIdentifier[] trustedKeyUsage = null;2281Set<PKCS12Attribute> attributes = new HashSet<>();22822283if (attrSet != null) {2284for (int j = 0; j < attrSet.length; j++) {2285byte[] encoded = attrSet[j].toByteArray();2286DerInputStream as = new DerInputStream(encoded);2287DerValue[] attrSeq = as.getSequence(2);2288if (attrSeq.length != 2) {2289throw new IOException("Invalid length for Attribute");2290}2291ObjectIdentifier attrId = attrSeq[0].getOID();2292DerInputStream vs =2293new DerInputStream(attrSeq[1].toByteArray());2294DerValue[] valSet;2295try {2296valSet = vs.getSet(1);2297} catch (IOException e) {2298throw new IOException("Attribute " + attrId +2299" should have a value " + e.getMessage());2300}2301if (attrId.equals((Object)PKCS9FriendlyName_OID)) {2302alias = valSet[0].getBMPString();2303} else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) {2304keyId = valSet[0].getOctetString();2305} else if2306(attrId.equals((Object)TrustedKeyUsage_OID)) {2307trustedKeyUsage = new ObjectIdentifier[valSet.length];2308for (int k = 0; k < valSet.length; k++) {2309trustedKeyUsage[k] = valSet[k].getOID();2310}2311} else {2312attributes.add(new PKCS12Attribute(encoded));2313}2314}2315}23162317/*2318* As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId)2319* are optional PKCS12 bagAttributes. But entries in the keyStore2320* are identified by their alias. Hence we need to have an2321* Unfriendlyname in the alias, if alias is null. The keyId2322* attribute is required to match the private key with the2323* certificate. If we get a bagItem of type KeyEntry with a2324* null keyId, we should skip it entirely.2325*/2326if (bagItem instanceof KeyEntry) {2327KeyEntry entry = (KeyEntry)bagItem;23282329if (bagItem instanceof PrivateKeyEntry) {2330if (keyId == null) {2331// Insert a localKeyID for the privateKey2332// Note: This is a workaround to allow null localKeyID2333// attribute in pkcs12 with one private key entry and2334// associated cert-chain2335if (privateKeyCount == 1) {2336keyId = "01".getBytes("UTF8");2337} else {2338continue;2339}2340}2341}2342entry.keyId = keyId;2343// restore date if it exists2344String keyIdStr = new String(keyId, "UTF8");2345Date date = null;2346if (keyIdStr.startsWith("Time ")) {2347try {2348date = new Date(2349Long.parseLong(keyIdStr.substring(5)));2350} catch (Exception e) {2351date = null;2352}2353}2354if (date == null) {2355date = new Date();2356}2357entry.date = date;23582359if (bagItem instanceof PrivateKeyEntry) {2360keyList.add((PrivateKeyEntry) entry);2361}2362if (entry.attributes == null) {2363entry.attributes = new HashSet<>();2364}2365entry.attributes.addAll(attributes);2366if (alias == null) {2367alias = getUnfriendlyName();2368}2369entry.alias = alias;2370entries.put(alias.toLowerCase(Locale.ENGLISH), entry);23712372} else if (bagItem instanceof X509Certificate) {2373X509Certificate cert = (X509Certificate)bagItem;2374// Insert a localKeyID for the corresponding cert2375// Note: This is a workaround to allow null localKeyID2376// attribute in pkcs12 with one private key entry and2377// associated cert-chain2378if ((keyId == null) && (privateKeyCount == 1)) {2379// insert localKeyID only for EE cert or self-signed cert2380if (i == 0) {2381keyId = "01".getBytes("UTF8");2382}2383}2384// Trusted certificate2385if (trustedKeyUsage != null) {2386if (alias == null) {2387alias = getUnfriendlyName();2388}2389CertEntry certEntry =2390new CertEntry(cert, keyId, alias, trustedKeyUsage,2391attributes);2392entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);2393} else {2394certEntries.add(new CertEntry(cert, keyId, alias));2395}2396X500Principal subjectDN = cert.getSubjectX500Principal();2397if (subjectDN != null) {2398if (!certsMap.containsKey(subjectDN)) {2399certsMap.put(subjectDN, cert);2400}2401}2402}2403}2404}24052406private String getUnfriendlyName() {2407counter++;2408return (String.valueOf(counter));2409}2410}241124122413