package sun.security.pkcs12;
import java.io.*;
import java.security.AccessController;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreSpi;
import java.security.KeyStoreException;
import java.security.PKCS12Attribute;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
import java.security.UnrecoverableEntryException;
import java.security.UnrecoverableKeyException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import java.security.AlgorithmParameters;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.security.auth.DestroyFailedException;
import javax.security.auth.x500.X500Principal;
import sun.security.util.Debug;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;
import sun.security.pkcs.ContentInfo;
import sun.security.x509.AlgorithmId;
import sun.security.pkcs.EncryptedPrivateKeyInfo;
public final class PKCS12KeyStore extends KeyStoreSpi {
public static final int VERSION_3 = 3;
private static final String[] KEY_PROTECTION_ALGORITHM = {
"keystore.pkcs12.keyProtectionAlgorithm",
"keystore.PKCS12.keyProtectionAlgorithm"
};
private static final String[] CORE_ATTRIBUTES = {
"1.2.840.113549.1.9.20",
"1.2.840.113549.1.9.21",
"2.16.840.1.113894.746875.1.1"
};
private static final Debug debug = Debug.getInstance("pkcs12");
private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2};
private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3};
private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5};
private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20};
private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21};
private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1};
private static final int pbeWithSHAAnd40BitRC2CBC[] =
{1, 2, 840, 113549, 1, 12, 1, 6};
private static final int pbeWithSHAAnd3KeyTripleDESCBC[] =
{1, 2, 840, 113549, 1, 12, 1, 3};
private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13};
private static final int TrustedKeyUsage[] =
{2, 16, 840, 1, 113894, 746875, 1, 1};
private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0};
private static ObjectIdentifier PKCS8ShroudedKeyBag_OID;
private static ObjectIdentifier CertBag_OID;
private static ObjectIdentifier SecretBag_OID;
private static ObjectIdentifier PKCS9FriendlyName_OID;
private static ObjectIdentifier PKCS9LocalKeyId_OID;
private static ObjectIdentifier PKCS9CertType_OID;
private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID;
private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID;
private static ObjectIdentifier pbes2_OID;
private static ObjectIdentifier TrustedKeyUsage_OID;
private static ObjectIdentifier[] AnyUsage;
private int counter = 0;
private static final int iterationCount = 1024;
private static final int SALT_LEN = 20;
private int privateKeyCount = 0;
private int secretKeyCount = 0;
private int certificateCount = 0;
private SecureRandom random;
static {
try {
PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag);
CertBag_OID = new ObjectIdentifier(certBag);
SecretBag_OID = new ObjectIdentifier(secretBag);
PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name);
PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId);
PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType);
pbeWithSHAAnd40BitRC2CBC_OID =
new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC);
pbeWithSHAAnd3KeyTripleDESCBC_OID =
new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC);
pbes2_OID = new ObjectIdentifier(pbes2);
TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage);
AnyUsage = new ObjectIdentifier[]{
new ObjectIdentifier(AnyExtendedKeyUsage)};
} catch (IOException ioe) {
}
}
private static class Entry {
Date date;
String alias;
byte[] keyId;
Set<KeyStore.Entry.Attribute> attributes;
}
private static class KeyEntry extends Entry {
}
private static class PrivateKeyEntry extends KeyEntry {
byte[] protectedPrivKey;
Certificate chain[];
};
private static class SecretKeyEntry extends KeyEntry {
byte[] protectedSecretKey;
};
private static class CertEntry extends Entry {
final X509Certificate cert;
ObjectIdentifier[] trustedKeyUsage;
CertEntry(X509Certificate cert, byte[] keyId, String alias) {
this(cert, keyId, alias, null, null);
}
CertEntry(X509Certificate cert, byte[] keyId, String alias,
ObjectIdentifier[] trustedKeyUsage,
Set<? extends KeyStore.Entry.Attribute> attributes) {
this.date = new Date();
this.cert = cert;
this.keyId = keyId;
this.alias = alias;
this.trustedKeyUsage = trustedKeyUsage;
this.attributes = new HashSet<>();
if (attributes != null) {
this.attributes.addAll(attributes);
}
}
}
private Map<String, Entry> entries =
Collections.synchronizedMap(new LinkedHashMap<String, Entry>());
private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>();
private LinkedHashMap<X500Principal, X509Certificate> certsMap =
new LinkedHashMap<X500Principal, X509Certificate>();
private ArrayList<CertEntry> certEntries = new ArrayList<CertEntry>();
public Key engineGetKey(String alias, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException
{
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
Key key = null;
if (entry == null || (!(entry instanceof KeyEntry))) {
return null;
}
byte[] encrBytes = null;
if (entry instanceof PrivateKeyEntry) {
encrBytes = ((PrivateKeyEntry) entry).protectedPrivKey;
} else if (entry instanceof SecretKeyEntry) {
encrBytes = ((SecretKeyEntry) entry).protectedSecretKey;
} else {
throw new UnrecoverableKeyException("Error locating key");
}
byte[] encryptedKey;
AlgorithmParameters algParams;
ObjectIdentifier algOid;
try {
EncryptedPrivateKeyInfo encrInfo =
new EncryptedPrivateKeyInfo(encrBytes);
encryptedKey = encrInfo.getEncryptedData();
DerValue val = new DerValue(encrInfo.getAlgorithm().encode());
DerInputStream in = val.toDerInputStream();
algOid = in.getOID();
algParams = parseAlgParameters(algOid, in);
} catch (IOException ioe) {
UnrecoverableKeyException uke =
new UnrecoverableKeyException("Private key not stored as "
+ "PKCS#8 EncryptedPrivateKeyInfo: " + ioe);
uke.initCause(ioe);
throw uke;
}
try {
byte[] keyInfo;
while (true) {
try {
SecretKey skey = getPBEKey(password);
Cipher cipher = Cipher.getInstance(
mapPBEParamsToAlgorithm(algOid, algParams));
cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
keyInfo = cipher.doFinal(encryptedKey);
break;
} catch (Exception e) {
if (password.length == 0) {
password = new char[1];
continue;
}
throw e;
}
}
DerValue val = new DerValue(keyInfo);
DerInputStream in = val.toDerInputStream();
int i = in.getInteger();
DerValue[] value = in.getSequence(2);
AlgorithmId algId = new AlgorithmId(value[0].getOID());
String keyAlgo = algId.getName();
if (entry instanceof PrivateKeyEntry) {
KeyFactory kfac = KeyFactory.getInstance(keyAlgo);
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo);
key = kfac.generatePrivate(kspec);
if (debug != null) {
debug.println("Retrieved a protected private key (" +
key.getClass().getName() + ") at alias '" + alias +
"'");
}
} else {
SecretKeyFactory sKeyFactory =
SecretKeyFactory.getInstance(keyAlgo);
byte[] keyBytes = in.getOctetString();
SecretKeySpec secretKeySpec =
new SecretKeySpec(keyBytes, keyAlgo);
if (keyAlgo.startsWith("PBE")) {
KeySpec pbeKeySpec =
sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class);
key = sKeyFactory.generateSecret(pbeKeySpec);
} else {
key = sKeyFactory.generateSecret(secretKeySpec);
}
if (debug != null) {
debug.println("Retrieved a protected secret key (" +
key.getClass().getName() + ") at alias '" + alias +
"'");
}
}
} catch (Exception e) {
UnrecoverableKeyException uke =
new UnrecoverableKeyException("Get Key failed: " +
e.getMessage());
uke.initCause(e);
throw uke;
}
return key;
}
public Certificate[] engineGetCertificateChain(String alias) {
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null && entry instanceof PrivateKeyEntry) {
if (((PrivateKeyEntry) entry).chain == null) {
return null;
} else {
if (debug != null) {
debug.println("Retrieved a " +
((PrivateKeyEntry) entry).chain.length +
"-certificate chain at alias '" + alias + "'");
}
return ((PrivateKeyEntry) entry).chain.clone();
}
} else {
return null;
}
}
public Certificate engineGetCertificate(String alias) {
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry == null) {
return null;
}
if (entry instanceof CertEntry &&
((CertEntry) entry).trustedKeyUsage != null) {
if (debug != null) {
if (Arrays.equals(AnyUsage,
((CertEntry) entry).trustedKeyUsage)) {
debug.println("Retrieved a certificate at alias '" + alias +
"' (trusted for any purpose)");
} else {
debug.println("Retrieved a certificate at alias '" + alias +
"' (trusted for limited purposes)");
}
}
return ((CertEntry) entry).cert;
} else if (entry instanceof PrivateKeyEntry) {
if (((PrivateKeyEntry) entry).chain == null) {
return null;
} else {
if (debug != null) {
debug.println("Retrieved a certificate at alias '" + alias +
"'");
}
return ((PrivateKeyEntry) entry).chain[0];
}
} else {
return null;
}
}
public Date engineGetCreationDate(String alias) {
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
return new Date(entry.date.getTime());
} else {
return null;
}
}
public synchronized void engineSetKeyEntry(String alias, Key key,
char[] password, Certificate[] chain)
throws KeyStoreException
{
KeyStore.PasswordProtection passwordProtection =
new KeyStore.PasswordProtection(password);
try {
setKeyEntry(alias, key, passwordProtection, chain, null);
} finally {
try {
passwordProtection.destroy();
} catch (DestroyFailedException dfe) {
}
}
}
private void setKeyEntry(String alias, Key key,
KeyStore.PasswordProtection passwordProtection, Certificate[] chain,
Set<KeyStore.Entry.Attribute> attributes)
throws KeyStoreException
{
try {
Entry entry;
if (key instanceof PrivateKey) {
PrivateKeyEntry keyEntry = new PrivateKeyEntry();
keyEntry.date = new Date();
if ((key.getFormat().equals("PKCS#8")) ||
(key.getFormat().equals("PKCS8"))) {
if (debug != null) {
debug.println("Setting a protected private key (" +
key.getClass().getName() + ") at alias '" + alias +
"'");
}
keyEntry.protectedPrivKey =
encryptPrivateKey(key.getEncoded(), passwordProtection);
} else {
throw new KeyStoreException("Private key is not encoded" +
"as PKCS#8");
}
if (chain != null) {
if ((chain.length > 1) && (!validateChain(chain)))
throw new KeyStoreException("Certificate chain is " +
"not valid");
keyEntry.chain = chain.clone();
certificateCount += chain.length;
if (debug != null) {
debug.println("Setting a " + chain.length +
"-certificate chain at alias '" + alias + "'");
}
}
privateKeyCount++;
entry = keyEntry;
} else if (key instanceof SecretKey) {
SecretKeyEntry keyEntry = new SecretKeyEntry();
keyEntry.date = new Date();
DerOutputStream pkcs8 = new DerOutputStream();
DerOutputStream secretKeyInfo = new DerOutputStream();
secretKeyInfo.putInteger(0);
AlgorithmId algId = AlgorithmId.get(key.getAlgorithm());
algId.encode(secretKeyInfo);
secretKeyInfo.putOctetString(key.getEncoded());
pkcs8.write(DerValue.tag_Sequence, secretKeyInfo);
keyEntry.protectedSecretKey =
encryptPrivateKey(pkcs8.toByteArray(), passwordProtection);
if (debug != null) {
debug.println("Setting a protected secret key (" +
key.getClass().getName() + ") at alias '" + alias +
"'");
}
secretKeyCount++;
entry = keyEntry;
} else {
throw new KeyStoreException("Unsupported Key type");
}
entry.attributes = new HashSet<>();
if (attributes != null) {
entry.attributes.addAll(attributes);
}
entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
entry.alias = alias.toLowerCase(Locale.ENGLISH);
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
} catch (Exception nsae) {
throw new KeyStoreException("Key protection " +
" algorithm not found: " + nsae, nsae);
}
}
public synchronized void engineSetKeyEntry(String alias, byte[] key,
Certificate[] chain)
throws KeyStoreException
{
try {
new EncryptedPrivateKeyInfo(key);
} catch (IOException ioe) {
throw new KeyStoreException("Private key is not stored"
+ " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe);
}
PrivateKeyEntry entry = new PrivateKeyEntry();
entry.date = new Date();
if (debug != null) {
debug.println("Setting a protected private key at alias '" +
alias + "'");
}
try {
entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
} catch (UnsupportedEncodingException ex) {
}
entry.alias = alias.toLowerCase(Locale.ENGLISH);
entry.protectedPrivKey = key.clone();
if (chain != null) {
entry.chain = chain.clone();
certificateCount += chain.length;
if (debug != null) {
debug.println("Setting a " + entry.chain.length +
"-certificate chain at alias '" + alias + "'");
}
}
privateKeyCount++;
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
}
private byte[] getSalt()
{
byte[] salt = new byte[SALT_LEN];
if (random == null) {
random = new SecureRandom();
}
random.nextBytes(salt);
return salt;
}
private AlgorithmParameters getAlgorithmParameters(String algorithm)
throws IOException
{
AlgorithmParameters algParams = null;
PBEParameterSpec paramSpec =
new PBEParameterSpec(getSalt(), iterationCount);
try {
algParams = AlgorithmParameters.getInstance(algorithm);
algParams.init(paramSpec);
} catch (Exception e) {
throw new IOException("getAlgorithmParameters failed: " +
e.getMessage(), e);
}
return algParams;
}
private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm,
DerInputStream in) throws IOException
{
AlgorithmParameters algParams = null;
try {
DerValue params;
if (in.available() == 0) {
params = null;
} else {
params = in.getDerValue();
if (params.tag == DerValue.tag_Null) {
params = null;
}
}
if (params != null) {
if (algorithm.equals((Object)pbes2_OID)) {
algParams = AlgorithmParameters.getInstance("PBES2");
} else {
algParams = AlgorithmParameters.getInstance("PBE");
}
algParams.init(params.toByteArray());
}
} catch (Exception e) {
throw new IOException("parseAlgParameters failed: " +
e.getMessage(), e);
}
return algParams;
}
private SecretKey getPBEKey(char[] password) throws IOException
{
SecretKey skey = null;
try {
PBEKeySpec keySpec = new PBEKeySpec(password);
SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE");
skey = skFac.generateSecret(keySpec);
keySpec.clearPassword();
} catch (Exception e) {
throw new IOException("getSecretKey failed: " +
e.getMessage(), e);
}
return skey;
}
private byte[] encryptPrivateKey(byte[] data,
KeyStore.PasswordProtection passwordProtection)
throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
{
byte[] key = null;
try {
String algorithm;
AlgorithmParameters algParams;
AlgorithmId algid;
algorithm = passwordProtection.getProtectionAlgorithm();
if (algorithm != null) {
AlgorithmParameterSpec algParamSpec =
passwordProtection.getProtectionParameters();
if (algParamSpec != null) {
algParams = AlgorithmParameters.getInstance(algorithm);
algParams.init(algParamSpec);
} else {
algParams = getAlgorithmParameters(algorithm);
}
} else {
algorithm = AccessController.doPrivileged(
new PrivilegedAction<String>() {
public String run() {
String prop =
Security.getProperty(
KEY_PROTECTION_ALGORITHM[0]);
if (prop == null) {
prop = Security.getProperty(
KEY_PROTECTION_ALGORITHM[1]);
}
return prop;
}
});
if (algorithm == null || algorithm.isEmpty()) {
algorithm = "PBEWithSHA1AndDESede";
}
algParams = getAlgorithmParameters(algorithm);
}
ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm);
if (pbeOID == null) {
throw new IOException("PBE algorithm '" + algorithm +
" 'is not supported for key entry protection");
}
SecretKey skey = getPBEKey(passwordProtection.getPassword());
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
byte[] encryptedKey = cipher.doFinal(data);
algid = new AlgorithmId(pbeOID, cipher.getParameters());
if (debug != null) {
debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() +
")");
}
EncryptedPrivateKeyInfo encrInfo =
new EncryptedPrivateKeyInfo(algid, encryptedKey);
key = encrInfo.getEncoded();
} catch (Exception e) {
UnrecoverableKeyException uke =
new UnrecoverableKeyException("Encrypt Private Key failed: "
+ e.getMessage());
uke.initCause(e);
throw uke;
}
return key;
}
private static ObjectIdentifier mapPBEAlgorithmToOID(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm.toLowerCase().startsWith("pbewithhmacsha")) {
return pbes2_OID;
}
return AlgorithmId.get(algorithm).getOID();
}
private static String mapPBEParamsToAlgorithm(ObjectIdentifier algorithm,
AlgorithmParameters algParams) throws NoSuchAlgorithmException {
if (algorithm.equals((Object)pbes2_OID) && algParams != null) {
return algParams.toString();
}
return algorithm.toString();
}
public synchronized void engineSetCertificateEntry(String alias,
Certificate cert) throws KeyStoreException
{
setCertEntry(alias, cert, null);
}
private void setCertEntry(String alias, Certificate cert,
Set<KeyStore.Entry.Attribute> attributes) throws KeyStoreException {
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null && entry instanceof KeyEntry) {
throw new KeyStoreException("Cannot overwrite own certificate");
}
CertEntry certEntry =
new CertEntry((X509Certificate) cert, null, alias, AnyUsage,
attributes);
certificateCount++;
entries.put(alias, certEntry);
if (debug != null) {
debug.println("Setting a trusted certificate at alias '" + alias +
"'");
}
}
public synchronized void engineDeleteEntry(String alias)
throws KeyStoreException
{
if (debug != null) {
debug.println("Removing entry at alias '" + alias + "'");
}
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry instanceof PrivateKeyEntry) {
PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
if (keyEntry.chain != null) {
certificateCount -= keyEntry.chain.length;
}
privateKeyCount--;
} else if (entry instanceof CertEntry) {
certificateCount--;
} else if (entry instanceof SecretKeyEntry) {
secretKeyCount--;
}
entries.remove(alias.toLowerCase(Locale.ENGLISH));
}
public Enumeration<String> engineAliases() {
return Collections.enumeration(entries.keySet());
}
public boolean engineContainsAlias(String alias) {
return entries.containsKey(alias.toLowerCase(Locale.ENGLISH));
}
public int engineSize() {
return entries.size();
}
public boolean engineIsKeyEntry(String alias) {
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null && entry instanceof KeyEntry) {
return true;
} else {
return false;
}
}
public boolean engineIsCertificateEntry(String alias) {
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null && entry instanceof CertEntry &&
((CertEntry) entry).trustedKeyUsage != null) {
return true;
} else {
return false;
}
}
public String engineGetCertificateAlias(Certificate cert) {
Certificate certElem = null;
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
Entry entry = entries.get(alias);
if (entry instanceof PrivateKeyEntry) {
if (((PrivateKeyEntry) entry).chain != null) {
certElem = ((PrivateKeyEntry) entry).chain[0];
}
} else if (entry instanceof CertEntry &&
((CertEntry) entry).trustedKeyUsage != null) {
certElem = ((CertEntry) entry).cert;
} else {
continue;
}
if (certElem.equals(cert)) {
return alias;
}
}
return null;
}
public synchronized void engineStore(OutputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
if (password == null) {
throw new IllegalArgumentException("password can't be null");
}
DerOutputStream pfx = new DerOutputStream();
DerOutputStream version = new DerOutputStream();
version.putInteger(VERSION_3);
byte[] pfxVersion = version.toByteArray();
pfx.write(pfxVersion);
DerOutputStream authSafe = new DerOutputStream();
DerOutputStream authSafeContentInfo = new DerOutputStream();
if (privateKeyCount > 0 || secretKeyCount > 0) {
if (debug != null) {
debug.println("Storing " + (privateKeyCount + secretKeyCount) +
" protected key(s) in a PKCS#7 data content-type");
}
byte[] safeContentData = createSafeContent();
ContentInfo dataContentInfo = new ContentInfo(safeContentData);
dataContentInfo.encode(authSafeContentInfo);
}
if (certificateCount > 0) {
if (debug != null) {
debug.println("Storing " + certificateCount +
" certificate(s) in a PKCS#7 encryptedData content-type");
}
byte[] encrData = createEncryptedData(password);
ContentInfo encrContentInfo =
new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
new DerValue(encrData));
encrContentInfo.encode(authSafeContentInfo);
}
DerOutputStream cInfo = new DerOutputStream();
cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
byte[] authenticatedSafe = cInfo.toByteArray();
ContentInfo contentInfo = new ContentInfo(authenticatedSafe);
contentInfo.encode(authSafe);
byte[] authSafeData = authSafe.toByteArray();
pfx.write(authSafeData);
byte[] macData = calculateMac(password, authenticatedSafe);
pfx.write(macData);
DerOutputStream pfxout = new DerOutputStream();
pfxout.write(DerValue.tag_Sequence, pfx);
byte[] pfxData = pfxout.toByteArray();
stream.write(pfxData);
stream.flush();
}
@Override
public KeyStore.Entry engineGetEntry(String alias,
KeyStore.ProtectionParameter protParam)
throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableEntryException {
if (!engineContainsAlias(alias)) {
return null;
}
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (protParam == null) {
if (engineIsCertificateEntry(alias)) {
if (entry instanceof CertEntry &&
((CertEntry) entry).trustedKeyUsage != null) {
if (debug != null) {
debug.println("Retrieved a trusted certificate at " +
"alias '" + alias + "'");
}
return new KeyStore.TrustedCertificateEntry(
((CertEntry)entry).cert, getAttributes(entry));
}
} else {
throw new UnrecoverableKeyException
("requested entry requires a password");
}
}
if (protParam instanceof KeyStore.PasswordProtection) {
if (engineIsCertificateEntry(alias)) {
throw new UnsupportedOperationException
("trusted certificate entries are not password-protected");
} else if (engineIsKeyEntry(alias)) {
KeyStore.PasswordProtection pp =
(KeyStore.PasswordProtection)protParam;
char[] password = pp.getPassword();
Key key = engineGetKey(alias, password);
if (key instanceof PrivateKey) {
Certificate[] chain = engineGetCertificateChain(alias);
return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain,
getAttributes(entry));
} else if (key instanceof SecretKey) {
return new KeyStore.SecretKeyEntry((SecretKey)key,
getAttributes(entry));
}
} else if (!engineIsKeyEntry(alias)) {
throw new UnsupportedOperationException
("untrusted certificate entries are not " +
"password-protected");
}
}
throw new UnsupportedOperationException();
}
@Override
public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
KeyStore.ProtectionParameter protParam) throws KeyStoreException {
if (protParam != null &&
!(protParam instanceof KeyStore.PasswordProtection)) {
throw new KeyStoreException("unsupported protection parameter");
}
KeyStore.PasswordProtection pProtect = null;
if (protParam != null) {
pProtect = (KeyStore.PasswordProtection)protParam;
}
if (entry instanceof KeyStore.TrustedCertificateEntry) {
if (protParam != null && pProtect.getPassword() != null) {
throw new KeyStoreException
("trusted certificate entries are not password-protected");
} else {
KeyStore.TrustedCertificateEntry tce =
(KeyStore.TrustedCertificateEntry)entry;
setCertEntry(alias, tce.getTrustedCertificate(),
tce.getAttributes());
return;
}
} else if (entry instanceof KeyStore.PrivateKeyEntry) {
if (pProtect == null || pProtect.getPassword() == null) {
throw new KeyStoreException
("non-null password required to create PrivateKeyEntry");
} else {
KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry;
setKeyEntry(alias, pke.getPrivateKey(), pProtect,
pke.getCertificateChain(), pke.getAttributes());
return;
}
} else if (entry instanceof KeyStore.SecretKeyEntry) {
if (pProtect == null || pProtect.getPassword() == null) {
throw new KeyStoreException
("non-null password required to create SecretKeyEntry");
} else {
KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry;
setKeyEntry(alias, ske.getSecretKey(), pProtect,
(Certificate[])null, ske.getAttributes());
return;
}
}
throw new KeyStoreException
("unsupported entry type: " + entry.getClass().getName());
}
private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) {
if (entry.attributes == null) {
entry.attributes = new HashSet<>();
}
entry.attributes.add(new PKCS12Attribute(
PKCS9FriendlyName_OID.toString(), entry.alias));
byte[] keyIdValue = entry.keyId;
if (keyIdValue != null) {
entry.attributes.add(new PKCS12Attribute(
PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue)));
}
if (entry instanceof CertEntry) {
ObjectIdentifier[] trustedKeyUsageValue =
((CertEntry) entry).trustedKeyUsage;
if (trustedKeyUsageValue != null) {
if (trustedKeyUsageValue.length == 1) {
entry.attributes.add(new PKCS12Attribute(
TrustedKeyUsage_OID.toString(),
trustedKeyUsageValue[0].toString()));
} else {
entry.attributes.add(new PKCS12Attribute(
TrustedKeyUsage_OID.toString(),
Arrays.toString(trustedKeyUsageValue)));
}
}
}
return entry.attributes;
}
private byte[] generateHash(byte[] data) throws IOException
{
byte[] digest = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(data);
digest = md.digest();
} catch (Exception e) {
throw new IOException("generateHash failed: " + e, e);
}
return digest;
}
private byte[] calculateMac(char[] passwd, byte[] data)
throws IOException
{
byte[] mData = null;
String algName = "SHA1";
try {
byte[] salt = getSalt();
Mac m = Mac.getInstance("HmacPBESHA1");
PBEParameterSpec params =
new PBEParameterSpec(salt, iterationCount);
SecretKey key = getPBEKey(passwd);
m.init(key, params);
m.update(data);
byte[] macResult = m.doFinal();
MacData macData = new MacData(algName, macResult, salt,
iterationCount);
DerOutputStream bytes = new DerOutputStream();
bytes.write(macData.getEncoded());
mData = bytes.toByteArray();
} catch (Exception e) {
throw new IOException("calculateMac failed: " + e, e);
}
return mData;
}
private boolean validateChain(Certificate[] certChain)
{
for (int i = 0; i < certChain.length-1; i++) {
X500Principal issuerDN =
((X509Certificate)certChain[i]).getIssuerX500Principal();
X500Principal subjectDN =
((X509Certificate)certChain[i+1]).getSubjectX500Principal();
if (!(issuerDN.equals(subjectDN)))
return false;
}
return true;
}
private byte[] getBagAttributes(String alias, byte[] keyId,
Set<KeyStore.Entry.Attribute> attributes) throws IOException {
return getBagAttributes(alias, keyId, null, attributes);
}
private byte[] getBagAttributes(String alias, byte[] keyId,
ObjectIdentifier[] trustedUsage,
Set<KeyStore.Entry.Attribute> attributes) throws IOException {
byte[] localKeyID = null;
byte[] friendlyName = null;
byte[] trustedKeyUsage = null;
if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) {
return null;
}
DerOutputStream bagAttrs = new DerOutputStream();
if (alias != null) {
DerOutputStream bagAttr1 = new DerOutputStream();
bagAttr1.putOID(PKCS9FriendlyName_OID);
DerOutputStream bagAttrContent1 = new DerOutputStream();
DerOutputStream bagAttrValue1 = new DerOutputStream();
bagAttrContent1.putBMPString(alias);
bagAttr1.write(DerValue.tag_Set, bagAttrContent1);
bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1);
friendlyName = bagAttrValue1.toByteArray();
}
if (keyId != null) {
DerOutputStream bagAttr2 = new DerOutputStream();
bagAttr2.putOID(PKCS9LocalKeyId_OID);
DerOutputStream bagAttrContent2 = new DerOutputStream();
DerOutputStream bagAttrValue2 = new DerOutputStream();
bagAttrContent2.putOctetString(keyId);
bagAttr2.write(DerValue.tag_Set, bagAttrContent2);
bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2);
localKeyID = bagAttrValue2.toByteArray();
}
if (trustedUsage != null) {
DerOutputStream bagAttr3 = new DerOutputStream();
bagAttr3.putOID(TrustedKeyUsage_OID);
DerOutputStream bagAttrContent3 = new DerOutputStream();
DerOutputStream bagAttrValue3 = new DerOutputStream();
for (ObjectIdentifier usage : trustedUsage) {
bagAttrContent3.putOID(usage);
}
bagAttr3.write(DerValue.tag_Set, bagAttrContent3);
bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3);
trustedKeyUsage = bagAttrValue3.toByteArray();
}
DerOutputStream attrs = new DerOutputStream();
if (friendlyName != null) {
attrs.write(friendlyName);
}
if (localKeyID != null) {
attrs.write(localKeyID);
}
if (trustedKeyUsage != null) {
attrs.write(trustedKeyUsage);
}
if (attributes != null) {
for (KeyStore.Entry.Attribute attribute : attributes) {
String attributeName = attribute.getName();
if (CORE_ATTRIBUTES[0].equals(attributeName) ||
CORE_ATTRIBUTES[1].equals(attributeName) ||
CORE_ATTRIBUTES[2].equals(attributeName)) {
continue;
}
attrs.write(((PKCS12Attribute) attribute).getEncoded());
}
}
bagAttrs.write(DerValue.tag_Set, attrs);
return bagAttrs.toByteArray();
}
private byte[] createEncryptedData(char[] password)
throws CertificateException, IOException
{
DerOutputStream out = new DerOutputStream();
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
Entry entry = entries.get(alias);
int chainLen = 1;
Certificate[] certs = null;
if (entry instanceof PrivateKeyEntry) {
PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry;
if (keyEntry.chain == null) {
chainLen = 0;
} else {
chainLen = keyEntry.chain.length;
}
certs = keyEntry.chain;
} else if (entry instanceof CertEntry) {
certs = new Certificate[]{((CertEntry) entry).cert};
}
for (int i = 0; i < chainLen; i++) {
DerOutputStream safeBag = new DerOutputStream();
safeBag.putOID(CertBag_OID);
DerOutputStream certBag = new DerOutputStream();
certBag.putOID(PKCS9CertType_OID);
DerOutputStream certValue = new DerOutputStream();
X509Certificate cert = (X509Certificate) certs[i];
certValue.putOctetString(cert.getEncoded());
certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), certValue);
DerOutputStream certout = new DerOutputStream();
certout.write(DerValue.tag_Sequence, certBag);
byte[] certBagValue = certout.toByteArray();
DerOutputStream bagValue = new DerOutputStream();
bagValue.write(certBagValue);
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), bagValue);
byte[] bagAttrs = null;
if (i == 0) {
if (entry instanceof KeyEntry) {
KeyEntry keyEntry = (KeyEntry) entry;
bagAttrs =
getBagAttributes(keyEntry.alias, keyEntry.keyId,
keyEntry.attributes);
} else {
CertEntry certEntry = (CertEntry) entry;
bagAttrs =
getBagAttributes(certEntry.alias, certEntry.keyId,
certEntry.trustedKeyUsage,
certEntry.attributes);
}
} else {
bagAttrs = getBagAttributes(
cert.getSubjectX500Principal().getName(), null,
entry.attributes);
}
if (bagAttrs != null) {
safeBag.write(bagAttrs);
}
out.write(DerValue.tag_Sequence, safeBag);
}
}
DerOutputStream safeBagValue = new DerOutputStream();
safeBagValue.write(DerValue.tag_SequenceOf, out);
byte[] safeBagData = safeBagValue.toByteArray();
byte[] encrContentInfo = encryptContent(safeBagData, password);
DerOutputStream encrData = new DerOutputStream();
DerOutputStream encrDataContent = new DerOutputStream();
encrData.putInteger(0);
encrData.write(encrContentInfo);
encrDataContent.write(DerValue.tag_Sequence, encrData);
return encrDataContent.toByteArray();
}
private byte[] createSafeContent()
throws CertificateException, IOException {
DerOutputStream out = new DerOutputStream();
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
Entry entry = entries.get(alias);
if (entry == null || (!(entry instanceof KeyEntry))) {
continue;
}
DerOutputStream safeBag = new DerOutputStream();
KeyEntry keyEntry = (KeyEntry) entry;
if (keyEntry instanceof PrivateKeyEntry) {
safeBag.putOID(PKCS8ShroudedKeyBag_OID);
byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey;
EncryptedPrivateKeyInfo encrInfo = null;
try {
encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
} catch (IOException ioe) {
throw new IOException("Private key not stored as "
+ "PKCS#8 EncryptedPrivateKeyInfo"
+ ioe.getMessage());
}
DerOutputStream bagValue = new DerOutputStream();
bagValue.write(encrInfo.getEncoded());
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), bagValue);
} else if (keyEntry instanceof SecretKeyEntry) {
safeBag.putOID(SecretBag_OID);
DerOutputStream secretBag = new DerOutputStream();
secretBag.putOID(PKCS8ShroudedKeyBag_OID);
DerOutputStream secretKeyValue = new DerOutputStream();
secretKeyValue.putOctetString(
((SecretKeyEntry) keyEntry).protectedSecretKey);
secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), secretKeyValue);
DerOutputStream secretBagSeq = new DerOutputStream();
secretBagSeq.write(DerValue.tag_Sequence, secretBag);
byte[] secretBagValue = secretBagSeq.toByteArray();
DerOutputStream bagValue = new DerOutputStream();
bagValue.write(secretBagValue);
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), bagValue);
} else {
continue;
}
byte[] bagAttrs =
getBagAttributes(alias, entry.keyId, entry.attributes);
safeBag.write(bagAttrs);
out.write(DerValue.tag_Sequence, safeBag);
}
DerOutputStream safeBagValue = new DerOutputStream();
safeBagValue.write(DerValue.tag_Sequence, out);
return safeBagValue.toByteArray();
}
private byte[] encryptContent(byte[] data, char[] password)
throws IOException {
byte[] encryptedData = null;
AlgorithmParameters algParams =
getAlgorithmParameters("PBEWithSHA1AndRC2_40");
DerOutputStream bytes = new DerOutputStream();
AlgorithmId algId =
new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);
algId.encode(bytes);
byte[] encodedAlgId = bytes.toByteArray();
try {
SecretKey skey = getPBEKey(password);
Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");
cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
encryptedData = cipher.doFinal(data);
if (debug != null) {
debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() +
")");
}
} catch (Exception e) {
throw new IOException("Failed to encrypt" +
" safe contents entry: " + e, e);
}
DerOutputStream bytes2 = new DerOutputStream();
bytes2.putOID(ContentInfo.DATA_OID);
bytes2.write(encodedAlgId);
DerOutputStream tmpout2 = new DerOutputStream();
tmpout2.putOctetString(encryptedData);
bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
false, (byte)0), tmpout2);
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, bytes2);
return out.toByteArray();
}
public synchronized void engineLoad(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
DataInputStream dis;
CertificateFactory cf = null;
ByteArrayInputStream bais = null;
byte[] encoded = null;
if (stream == null)
return;
counter = 0;
DerValue val = new DerValue(stream);
DerInputStream s = val.toDerInputStream();
int version = s.getInteger();
if (version != VERSION_3) {
throw new IOException("PKCS12 keystore not in version 3 format");
}
entries.clear();
byte[] authSafeData;
ContentInfo authSafe = new ContentInfo(s);
ObjectIdentifier contentType = authSafe.getContentType();
if (contentType.equals((Object)ContentInfo.DATA_OID)) {
authSafeData = authSafe.getData();
} else {
throw new IOException("public key protected PKCS12 not supported");
}
DerInputStream as = new DerInputStream(authSafeData);
DerValue[] safeContentsArray = as.getSequence(2);
int count = safeContentsArray.length;
privateKeyCount = 0;
secretKeyCount = 0;
certificateCount = 0;
for (int i = 0; i < count; i++) {
byte[] safeContentsData;
ContentInfo safeContents;
DerInputStream sci;
byte[] eAlgId = null;
sci = new DerInputStream(safeContentsArray[i].toByteArray());
safeContents = new ContentInfo(sci);
contentType = safeContents.getContentType();
safeContentsData = null;
if (contentType.equals((Object)ContentInfo.DATA_OID)) {
if (debug != null) {
debug.println("Loading PKCS#7 data content-type");
}
safeContentsData = safeContents.getData();
} else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) {
if (password == null) {
continue;
}
if (debug != null) {
debug.println("Loading PKCS#7 encryptedData content-type");
}
DerInputStream edi =
safeContents.getContent().toDerInputStream();
int edVersion = edi.getInteger();
DerValue[] seq = edi.getSequence(2);
ObjectIdentifier edContentType = seq[0].getOID();
eAlgId = seq[1].toByteArray();
if (!seq[2].isContextSpecific((byte)0)) {
throw new IOException("encrypted content not present!");
}
byte newTag = DerValue.tag_OctetString;
if (seq[2].isConstructed())
newTag |= 0x20;
seq[2].resetTag(newTag);
safeContentsData = seq[2].getOctetString();
DerInputStream in = seq[1].toDerInputStream();
ObjectIdentifier algOid = in.getOID();
AlgorithmParameters algParams = parseAlgParameters(algOid, in);
while (true) {
try {
SecretKey skey = getPBEKey(password);
Cipher cipher = Cipher.getInstance(algOid.toString());
cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
safeContentsData = cipher.doFinal(safeContentsData);
break;
} catch (Exception e) {
if (password.length == 0) {
password = new char[1];
continue;
}
throw new IOException(
"failed to decrypt safe contents entry: " + e, e);
}
}
} else {
throw new IOException("public key protected PKCS12" +
" not supported");
}
DerInputStream sc = new DerInputStream(safeContentsData);
loadSafeContents(sc, password);
}
if (password != null && s.available() > 0) {
MacData macData = new MacData(s);
try {
String algName =
macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
algName = algName.replace("-", "");
Mac m = Mac.getInstance("HmacPBE" + algName);
PBEParameterSpec params =
new PBEParameterSpec(macData.getSalt(),
macData.getIterations());
SecretKey key = getPBEKey(password);
m.init(key, params);
m.update(authSafeData);
byte[] macResult = m.doFinal();
if (debug != null) {
debug.println("Checking keystore integrity " +
"(MAC algorithm: " + m.getAlgorithm() + ")");
}
if (!Arrays.equals(macData.getDigest(), macResult)) {
throw new SecurityException("Failed PKCS12" +
" integrity checking");
}
} catch (Exception e) {
throw new IOException("Integrity check failed: " + e, e);
}
}
PrivateKeyEntry[] list =
keyList.toArray(new PrivateKeyEntry[keyList.size()]);
for (int m = 0; m < list.length; m++) {
PrivateKeyEntry entry = list[m];
if (entry.keyId != null) {
ArrayList<X509Certificate> chain =
new ArrayList<X509Certificate>();
X509Certificate cert = findMatchedCertificate(entry);
while (cert != null) {
chain.add(cert);
X500Principal issuerDN = cert.getIssuerX500Principal();
if (issuerDN.equals(cert.getSubjectX500Principal())) {
break;
}
cert = certsMap.get(issuerDN);
}
if (chain.size() > 0)
entry.chain = chain.toArray(new Certificate[chain.size()]);
}
}
if (debug != null) {
if (privateKeyCount > 0) {
debug.println("Loaded " + privateKeyCount +
" protected private key(s)");
}
if (secretKeyCount > 0) {
debug.println("Loaded " + secretKeyCount +
" protected secret key(s)");
}
if (certificateCount > 0) {
debug.println("Loaded " + certificateCount +
" certificate(s)");
}
}
certEntries.clear();
certsMap.clear();
keyList.clear();
}
private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
CertEntry keyIdMatch = null;
CertEntry aliasMatch = null;
for (CertEntry ce: certEntries) {
if (Arrays.equals(entry.keyId, ce.keyId)) {
keyIdMatch = ce;
if (entry.alias.equalsIgnoreCase(ce.alias)) {
return ce.cert;
}
} else if (entry.alias.equalsIgnoreCase(ce.alias)) {
aliasMatch = ce;
}
}
if (keyIdMatch != null) return keyIdMatch.cert;
else if (aliasMatch != null) return aliasMatch.cert;
else return null;
}
private void loadSafeContents(DerInputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{