< prev index next >
src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java
Print this page
*** 67,88 ****
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;
/**
* This class provides the keystore implementation referred to as "PKCS12".
* Implements the PKCS#12 PFX protected using the Password privacy mode.
* The contents are protected using Password integrity mode.
*
! * Currently we support following PBE algorithms:
! * - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys
! * - pbeWithSHAAnd40BitRC2CBC to encrypt certificates
*
* Supported encryption of various implementations :
*
* Software and mode. Certificate encryption Private key encryption
* ---------------------------------------------------------------------
--- 67,91 ----
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.util.SecurityProperties;
import sun.security.x509.AlgorithmId;
import sun.security.pkcs.EncryptedPrivateKeyInfo;
/**
* This class provides the keystore implementation referred to as "PKCS12".
* Implements the PKCS#12 PFX protected using the Password privacy mode.
* The contents are protected using Password integrity mode.
*
! * Currently these PBE algorithms are used by default:
! * - PBEWithSHA1AndDESede to encrypt private keys, iteration count 50000.
! * - PBEWithSHA1AndRC2_40 to encrypt certificates, iteration count 50000.
! *
! * The default Mac algorithm is HmacPBESHA1, iteration count 100000.
*
* Supported encryption of various implementations :
*
* Software and mode. Certificate encryption Private key encryption
* ---------------------------------------------------------------------
*** 121,148 ****
*
* @author Seema Malkani
* @author Jeff Nisewanger
* @author Jan Luehe
*
- * @see KeyProtector
* @see java.security.KeyStoreSpi
- * @see KeyTool
- *
- *
*/
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 int MAX_ITERATION_COUNT = 5000000;
- private static final int PBE_ITERATION_COUNT = 50000; // default
- private static final int MAC_ITERATION_COUNT = 100000; // default
private static final int SALT_LEN = 20;
// friendlyName, localKeyId, trustedKeyUsage
private static final String[] CORE_ATTRIBUTES = {
"1.2.840.113549.1.9.20",
--- 124,140 ----
*** 159,193 ****
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};
// TODO: temporary Oracle OID
/*
* { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894)
* jdk(746875) crypto(1) id-at-trustedKeyUsage(1) }
*/
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 key count
// Note: This is a workaround to allow null localKeyID attribute
--- 151,179 ----
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 pbes2[] = {1, 2, 840, 113549, 1, 5, 13};
// TODO: temporary Oracle OID
/*
* { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894)
* jdk(746875) crypto(1) id-at-trustedKeyUsage(1) }
*/
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 final ObjectIdentifier PKCS8ShroudedKeyBag_OID;
! private static final ObjectIdentifier CertBag_OID;
! private static final ObjectIdentifier SecretBag_OID;
! private static final ObjectIdentifier PKCS9FriendlyName_OID;
! private static final ObjectIdentifier PKCS9LocalKeyId_OID;
! private static final ObjectIdentifier PKCS9CertType_OID;
! private static final ObjectIdentifier pbes2_OID;
! private static final ObjectIdentifier TrustedKeyUsage_OID;
! private static final ObjectIdentifier[] AnyUsage;
private int counter = 0;
// private key count
// Note: This is a workaround to allow null localKeyID attribute
*** 198,207 ****
--- 184,204 ----
private int secretKeyCount = 0;
// certificate count
private int certificateCount = 0;
+ // Alg/params used for *this* keystore. Initialized as -1 for ic and
+ // null for algorithm names. When an existing file is read, they will be
+ // assigned inside engineLoad() so storing an existing keystore uses the
+ // old alg/params. This makes sure if a keystore is created password-less
+ // it will be password-less forever. Otherwise, engineStore() will read
+ // the default values. These fields are always reset when load() is called.
+ private String certProtectionAlgorithm = null;
+ private int certPbeIterationCount = -1;
+ private String macAlgorithm = null;
+ private int macIterationCount = -1;
+
// the source of randomness
private SecureRandom random;
static {
try {
*** 209,228 ****
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) {
! // should not happen
}
}
// A keystore entry and associated attributes
private static class Entry {
--- 206,221 ----
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);
pbes2_OID = new ObjectIdentifier(pbes2);
TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage);
AnyUsage = new ObjectIdentifier[]{
new ObjectIdentifier(AnyExtendedKeyUsage)};
} catch (IOException ioe) {
! throw new AssertionError("OID not initialized", ioe);
}
}
// A keystore entry and associated attributes
private static class Entry {
*** 379,389 ****
throw new IOException("Invalid PBE algorithm parameters");
}
ic = pbeSpec.getIterationCount();
if (ic > MAX_ITERATION_COUNT) {
! throw new IOException("PBE iteration count too large");
}
} else {
ic = 0;
}
--- 372,382 ----
throw new IOException("Invalid PBE algorithm parameters");
}
ic = pbeSpec.getIterationCount();
if (ic > MAX_ITERATION_COUNT) {
! throw new IOException("key PBE iteration count too large");
}
} else {
ic = 0;
}
*** 415,425 ****
Key tmp = kfac.generatePrivate(kspec);
if (debug != null) {
debug.println("Retrieved a protected private key at alias" +
" '" + alias + "' (" +
! new AlgorithmId(algOid).getName() +
" iterations: " + ic + ")");
}
return tmp;
// decode secret key
} else {
--- 408,418 ----
Key tmp = kfac.generatePrivate(kspec);
if (debug != null) {
debug.println("Retrieved a protected private key at alias" +
" '" + alias + "' (" +
! mapPBEParamsToAlgorithm(algOid, algParams) +
" iterations: " + ic + ")");
}
return tmp;
// decode secret key
} else {
*** 440,450 ****
}
if (debug != null) {
debug.println("Retrieved a protected secret key at alias " +
"'" + alias + "' (" +
! new AlgorithmId(algOid).getName() +
" iterations: " + ic + ")");
}
return tmp;
}
}, password);
--- 433,443 ----
}
if (debug != null) {
debug.println("Retrieved a protected secret key at alias " +
"'" + alias + "' (" +
! mapPBEParamsToAlgorithm(algOid, algParams) +
" iterations: " + ic + ")");
}
return tmp;
}
}, password);
*** 689,699 ****
entry.alias = alias.toLowerCase(Locale.ENGLISH);
// add the entry
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
} catch (Exception nsae) {
! throw new KeyStoreException("Key protection " +
" algorithm not found: " + nsae, nsae);
}
}
/**
--- 682,692 ----
entry.alias = alias.toLowerCase(Locale.ENGLISH);
// add the entry
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
} catch (Exception nsae) {
! throw new KeyStoreException("Key protection" +
" algorithm not found: " + nsae, nsae);
}
}
/**
*** 786,803 ****
}
/*
* Generate PBE Algorithm Parameters
*/
! private AlgorithmParameters getPBEAlgorithmParameters(String algorithm)
! throws IOException
! {
AlgorithmParameters algParams = null;
// create PBE parameters from salt and iteration count
PBEParameterSpec paramSpec =
! new PBEParameterSpec(getSalt(), PBE_ITERATION_COUNT);
try {
algParams = AlgorithmParameters.getInstance(algorithm);
algParams.init(paramSpec);
} catch (Exception e) {
throw new IOException("getPBEAlgorithmParameters failed: " +
--- 779,795 ----
}
/*
* Generate PBE Algorithm Parameters
*/
! private AlgorithmParameters getPBEAlgorithmParameters(
! String algorithm, int iterationCount) throws IOException {
AlgorithmParameters algParams = null;
// create PBE parameters from salt and iteration count
PBEParameterSpec paramSpec =
! new PBEParameterSpec(getSalt(), iterationCount);
try {
algParams = AlgorithmParameters.getInstance(algorithm);
algParams.init(paramSpec);
} catch (Exception e) {
throw new IOException("getPBEAlgorithmParameters failed: " +
*** 856,872 ****
}
return skey;
}
/*
! * Encrypt private key using Password-based encryption (PBE)
* as defined in PKCS#5.
*
* NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
* used to derive the key and IV.
*
! * @return encrypted private key encoded as EncryptedPrivateKeyInfo
*/
private byte[] encryptPrivateKey(byte[] data,
KeyStore.PasswordProtection passwordProtection)
throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
{
--- 848,865 ----
}
return skey;
}
/*
! * Encrypt private key or secret key using Password-based encryption (PBE)
* as defined in PKCS#5.
*
* NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is
* used to derive the key and IV.
*
! * @return encrypted private key or secret key encoded as
! * EncryptedPrivateKeyInfo
*/
private byte[] encryptPrivateKey(byte[] data,
KeyStore.PasswordProtection passwordProtection)
throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException
{
*** 884,914 ****
passwordProtection.getProtectionParameters();
if (algParamSpec != null) {
algParams = AlgorithmParameters.getInstance(algorithm);
algParams.init(algParamSpec);
} else {
! algParams = getPBEAlgorithmParameters(algorithm);
}
} else {
// Check default key protection algorithm for PKCS12 keystores
! 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 = getPBEAlgorithmParameters(algorithm);
}
ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm);
if (pbeOID == null) {
throw new IOException("PBE algorithm '" + algorithm +
--- 877,894 ----
passwordProtection.getProtectionParameters();
if (algParamSpec != null) {
algParams = AlgorithmParameters.getInstance(algorithm);
algParams.init(algParamSpec);
} else {
! algParams = getPBEAlgorithmParameters(algorithm,
! defaultKeyPbeIterationCount());
}
} else {
// Check default key protection algorithm for PKCS12 keystores
! algorithm = defaultKeyProtectionAlgorithm();
! algParams = getPBEAlgorithmParameters(algorithm,
! defaultKeyPbeIterationCount());
}
ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm);
if (pbeOID == null) {
throw new IOException("PBE algorithm '" + algorithm +
*** 962,972 ****
AlgorithmParameters algParams) throws NoSuchAlgorithmException {
// Check for PBES2 algorithms
if (algorithm.equals((Object)pbes2_OID) && algParams != null) {
return algParams.toString();
}
! return algorithm.toString();
}
/**
* Assigns the given certificate to the given alias.
*
--- 942,952 ----
AlgorithmParameters algParams) throws NoSuchAlgorithmException {
// Check for PBES2 algorithms
if (algorithm.equals((Object)pbes2_OID) && algParams != null) {
return algParams.toString();
}
! return new AlgorithmId(algorithm).getName();
}
/**
* Assigns the given certificate to the given alias.
*
*** 1187,1200 ****
* the keystore data could not be stored
*/
public synchronized void engineStore(OutputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
- // password is mandatory when storing
- if (password == null) {
- throw new IllegalArgumentException("password can't be null");
- }
// -- Create PFX
DerOutputStream pfx = new DerOutputStream();
// PFX version (always write the latest version)
--- 1167,1176 ----
*** 1223,1242 ****
--- 1199,1230 ----
}
// -- create EncryptedContentInfo
if (certificateCount > 0) {
+ if (certProtectionAlgorithm == null) {
+ certProtectionAlgorithm = defaultCertProtectionAlgorithm();
+ }
+ if (certPbeIterationCount < 0) {
+ certPbeIterationCount = defaultCertPbeIterationCount();
+ }
+
if (debug != null) {
debug.println("Storing " + certificateCount +
" certificate(s) in a PKCS#7 encryptedData");
}
byte[] encrData = createEncryptedData(password);
+ if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
ContentInfo encrContentInfo =
new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
new DerValue(encrData));
encrContentInfo.encode(authSafeContentInfo);
+ } else {
+ ContentInfo dataContentInfo = new ContentInfo(encrData);
+ dataContentInfo.encode(authSafeContentInfo);
+ }
}
// wrap as SequenceOf ContentInfos
DerOutputStream cInfo = new DerOutputStream();
cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo);
*** 1247,1259 ****
contentInfo.encode(authSafe);
byte[] authSafeData = authSafe.toByteArray();
pfx.write(authSafeData);
// -- MAC
byte[] macData = calculateMac(password, authenticatedSafe);
pfx.write(macData);
!
// write PFX to output stream
DerOutputStream pfxout = new DerOutputStream();
pfxout.write(DerValue.tag_Sequence, pfx);
byte[] pfxData = pfxout.toByteArray();
stream.write(pfxData);
--- 1235,1254 ----
contentInfo.encode(authSafe);
byte[] authSafeData = authSafe.toByteArray();
pfx.write(authSafeData);
// -- MAC
+ if (macAlgorithm == null) {
+ macAlgorithm = defaultMacAlgorithm();
+ }
+ if (macIterationCount < 0) {
+ macIterationCount = defaultMacIterationCount();
+ }
+ if (!macAlgorithm.equalsIgnoreCase("NONE")) {
byte[] macData = calculateMac(password, authenticatedSafe);
pfx.write(macData);
! }
// write PFX to output stream
DerOutputStream pfxout = new DerOutputStream();
pfxout.write(DerValue.tag_Sequence, pfx);
byte[] pfxData = pfxout.toByteArray();
stream.write(pfxData);
*** 1460,1514 ****
return entry.attributes;
}
/*
- * Generate Hash.
- */
- 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;
- }
-
-
- /*
* Calculate MAC using HMAC algorithm (required for password integrity)
*
* Hash-based MAC algorithm combines secret key with message digest to
* create a message authentication code (MAC)
*/
private byte[] calculateMac(char[] passwd, byte[] data)
throws IOException
{
byte[] mData = null;
! String algName = "SHA1";
try {
// Generate a random salt.
byte[] salt = getSalt();
// generate MAC (MAC key is generated within JCE)
! Mac m = Mac.getInstance("HmacPBESHA1");
PBEParameterSpec params =
! new PBEParameterSpec(salt, MAC_ITERATION_COUNT);
SecretKey key = getPBEKey(passwd);
m.init(key, params);
m.update(data);
byte[] macResult = m.doFinal();
// encode as MacData
MacData macData = new MacData(algName, macResult, salt,
! MAC_ITERATION_COUNT);
DerOutputStream bytes = new DerOutputStream();
bytes.write(macData.getEncoded());
mData = bytes.toByteArray();
} catch (Exception e) {
throw new IOException("calculateMac failed: " + e, e);
--- 1455,1491 ----
return entry.attributes;
}
/*
* Calculate MAC using HMAC algorithm (required for password integrity)
*
* Hash-based MAC algorithm combines secret key with message digest to
* create a message authentication code (MAC)
*/
private byte[] calculateMac(char[] passwd, byte[] data)
throws IOException
{
byte[] mData = null;
! String algName = macAlgorithm.substring(7);
try {
// Generate a random salt.
byte[] salt = getSalt();
// generate MAC (MAC key is generated within JCE)
! Mac m = Mac.getInstance(macAlgorithm);
PBEParameterSpec params =
! new PBEParameterSpec(salt, macIterationCount);
SecretKey key = getPBEKey(passwd);
m.init(key, params);
m.update(data);
byte[] macResult = m.doFinal();
// encode as MacData
MacData macData = new MacData(algName, macResult, salt,
! macIterationCount);
DerOutputStream bytes = new DerOutputStream();
bytes.write(macData.getEncoded());
mData = bytes.toByteArray();
} catch (Exception e) {
throw new IOException("calculateMac failed: " + e, e);
*** 1762,1780 ****
--- 1739,1761 ----
DerOutputStream safeBagValue = new DerOutputStream();
safeBagValue.write(DerValue.tag_SequenceOf, out);
byte[] safeBagData = safeBagValue.toByteArray();
// encrypt the content (EncryptedContentInfo)
+ if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
byte[] encrContentInfo = encryptContent(safeBagData, password);
// -- SEQUENCE of EncryptedData
DerOutputStream encrData = new DerOutputStream();
DerOutputStream encrDataContent = new DerOutputStream();
encrData.putInteger(0);
encrData.write(encrContentInfo);
encrDataContent.write(DerValue.tag_Sequence, encrData);
return encrDataContent.toByteArray();
+ } else {
+ return safeBagData;
+ }
}
/*
* Create SafeContent Data content type.
* Includes encrypted secret key in a SafeBag of type SecretBag.
*** 1879,1929 ****
private byte[] encryptContent(byte[] data, char[] password)
throws IOException {
byte[] encryptedData = null;
// create AlgorithmParameters
! AlgorithmParameters algParams =
! getPBEAlgorithmParameters("PBEWithSHA1AndRC2_40");
DerOutputStream bytes = new DerOutputStream();
- AlgorithmId algId =
- new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams);
- algId.encode(bytes);
- byte[] encodedAlgId = bytes.toByteArray();
- try {
// Use JCE
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);
- }
-
// create EncryptedContentInfo
DerOutputStream bytes2 = new DerOutputStream();
bytes2.putOID(ContentInfo.DATA_OID);
bytes2.write(encodedAlgId);
// Wrap encrypted data in a context-specific tag.
DerOutputStream tmpout2 = new DerOutputStream();
tmpout2.putOctetString(encryptedData);
bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
! false, (byte)0), tmpout2);
// wrap EncryptedContentInfo in a Sequence
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, bytes2);
return out.toByteArray();
}
/**
* Loads the keystore from the given input stream.
*
--- 1860,1915 ----
private byte[] encryptContent(byte[] data, char[] password)
throws IOException {
byte[] encryptedData = null;
+
+ try {
// create AlgorithmParameters
! AlgorithmParameters algParams = getPBEAlgorithmParameters(
! certProtectionAlgorithm, certPbeIterationCount);
DerOutputStream bytes = new DerOutputStream();
// Use JCE
SecretKey skey = getPBEKey(password);
! Cipher cipher = Cipher.getInstance(certProtectionAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, skey, algParams);
encryptedData = cipher.doFinal(data);
+ AlgorithmId algId = new AlgorithmId(
+ mapPBEAlgorithmToOID(certProtectionAlgorithm),
+ cipher.getParameters());
+ // cipher.getParameters() now has IV
+ algId.encode(bytes);
+ byte[] encodedAlgId = bytes.toByteArray();
+
if (debug != null) {
debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() +
")");
}
// create EncryptedContentInfo
DerOutputStream bytes2 = new DerOutputStream();
bytes2.putOID(ContentInfo.DATA_OID);
bytes2.write(encodedAlgId);
// Wrap encrypted data in a context-specific tag.
DerOutputStream tmpout2 = new DerOutputStream();
tmpout2.putOctetString(encryptedData);
bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
! false, (byte) 0), tmpout2);
// wrap EncryptedContentInfo in a Sequence
DerOutputStream out = new DerOutputStream();
out.write(DerValue.tag_Sequence, bytes2);
return out.toByteArray();
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (Exception e) {
+ throw new IOException("Failed to encrypt" +
+ " safe contents entry: " + e, e);
+ }
}
/**
* Loads the keystore from the given input stream.
*
*** 1942,1955 ****
* keystore could not be loaded
*/
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;
// reset the counter
--- 1928,1943 ----
* keystore could not be loaded
*/
public synchronized void engineLoad(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
!
! // Reset config when loading a different keystore.
! certProtectionAlgorithm = null;
! certPbeIterationCount = -1;
! macAlgorithm = null;
! macIterationCount = -1;
if (stream == null)
return;
// reset the counter
*** 1985,1994 ****
--- 1973,1984 ----
// reset the counters at the start
privateKeyCount = 0;
secretKeyCount = 0;
certificateCount = 0;
+ boolean seeEncBag = false;
+
/*
* Spin over the ContentInfos.
*/
for (int i = 0; i < count; i++) {
ContentInfo safeContents;
*** 2010,2019 ****
--- 2000,2024 ----
if (debug != null) {
debug.println("Warning: skipping PKCS#7 encryptedData" +
" - no password was supplied");
}
+ // No password to decrypt ENCRYPTED_DATA_OID. *Skip it*.
+ // This means user will see a PrivateKeyEntry without
+ // certificates and a whole TrustedCertificateEntry will
+ // be lost. This is not a perfect solution but alternative
+ // solutions are more disruptive:
+ //
+ // We cannot just fail, since KeyStore.load(is, null)
+ // has been known to never fail because of a null password.
+ //
+ // We cannot just throw away the whole PrivateKeyEntry,
+ // this is too silent and no one will notice anything.
+ //
+ // We also cannot fail when getCertificate() on such a
+ // PrivateKeyEntry is called, since the method has not
+ // specified this behavior.
continue;
}
DerInputStream edi =
safeContents.getContent().toDerInputStream();
*** 2053,2069 ****
"Invalid PBE algorithm parameters");
}
ic = pbeSpec.getIterationCount();
if (ic > MAX_ITERATION_COUNT) {
! throw new IOException("PBE iteration count too large");
}
}
if (debug != null) {
debug.println("Loading PKCS#7 encryptedData " +
! "(" + new AlgorithmId(algOid).getName() +
" iterations: " + ic + ")");
}
try {
RetryWithZero.run(pass -> {
--- 2058,2079 ----
"Invalid PBE algorithm parameters");
}
ic = pbeSpec.getIterationCount();
if (ic > MAX_ITERATION_COUNT) {
! throw new IOException("cert PBE iteration count too large");
}
+
+ certProtectionAlgorithm
+ = mapPBEParamsToAlgorithm(algOid, algParams);
+ certPbeIterationCount = ic;
+ seeEncBag = true;
}
if (debug != null) {
debug.println("Loading PKCS#7 encryptedData " +
! "(" + mapPBEParamsToAlgorithm(algOid, algParams) +
" iterations: " + ic + ")");
}
try {
RetryWithZero.run(pass -> {
*** 2084,2095 ****
throw new IOException("public key protected PKCS12" +
" not supported");
}
}
// The MacData is optional.
! if (password != null && s.available() > 0) {
MacData macData = new MacData(s);
int ic = macData.getIterations();
try {
if (ic > MAX_ITERATION_COUNT) {
--- 2094,2113 ----
throw new IOException("public key protected PKCS12" +
" not supported");
}
}
+ // No ENCRYPTED_DATA_OID but see certificate. Must be passwordless.
+ if (!seeEncBag && certificateCount > 0) {
+ certProtectionAlgorithm = "NONE";
+ }
+
// The MacData is optional.
! if (s.available() > 0) {
! // If there is no password, we cannot fail. KeyStore.load(is, null)
! // has been known to never fail because of a null password.
! if (password != null) {
MacData macData = new MacData(s);
int ic = macData.getIterations();
try {
if (ic > MAX_ITERATION_COUNT) {
*** 2101,2112 ****
macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
// Change SHA-1 to SHA1
algName = algName.replace("-", "");
// generate MAC (MAC key is created within JCE)
! Mac m = Mac.getInstance("HmacPBE" + algName);
PBEParameterSpec params =
new PBEParameterSpec(macData.getSalt(), ic);
RetryWithZero.run(pass -> {
SecretKey key = getPBEKey(pass);
--- 2119,2133 ----
macData.getDigestAlgName().toUpperCase(Locale.ENGLISH);
// Change SHA-1 to SHA1
algName = algName.replace("-", "");
+ macAlgorithm = "HmacPBE" + algName;
+ macIterationCount = ic;
+
// generate MAC (MAC key is created within JCE)
! Mac m = Mac.getInstance(macAlgorithm);
PBEParameterSpec params =
new PBEParameterSpec(macData.getSalt(), ic);
RetryWithZero.run(pass -> {
SecretKey key = getPBEKey(pass);
*** 2121,2136 ****
if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
throw new UnrecoverableKeyException("Failed PKCS12" +
" integrity checking");
}
! return (Void)null;
}, password);
} catch (Exception e) {
throw new IOException("Integrity check failed: " + e, e);
}
}
/*
* Match up private keys with certificate chains.
*/
PrivateKeyEntry[] list =
--- 2142,2160 ----
if (!MessageDigest.isEqual(macData.getDigest(), macResult)) {
throw new UnrecoverableKeyException("Failed PKCS12" +
" integrity checking");
}
! return (Void) null;
}, password);
} catch (Exception e) {
throw new IOException("Integrity check failed: " + e, e);
}
}
+ } else {
+ macAlgorithm = "NONE";
+ }
/*
* Match up private keys with certificate chains.
*/
PrivateKeyEntry[] list =
*** 2165,2176 ****
break;
}
cert = certsMap.get(issuerDN);
}
/* Update existing KeyEntry in entries table */
! if (chain.size() > 0)
entry.chain = chain.toArray(new Certificate[chain.size()]);
}
}
if (debug != null) {
debug.println("PKCS12KeyStore load: private key count: " +
--- 2189,2206 ----
break;
}
cert = certsMap.get(issuerDN);
}
/* Update existing KeyEntry in entries table */
! if (chain.size() > 0) {
entry.chain = chain.toArray(new Certificate[chain.size()]);
+ } else {
+ // Remove private key entries where there is no associated
+ // certs. Most likely the keystore is loaded with a null
+ // password.
+ entries.remove(entry);
+ }
}
}
if (debug != null) {
debug.println("PKCS12KeyStore load: private key count: " +
*** 2182,2191 ****
--- 2212,2261 ----
certsMap.clear();
keyList.clear();
}
/**
+ * Returns if a pkcs12 file is password-less. This means no cert is
+ * encrypted and there is no Mac. Please note that the private key
+ * can be encrypted.
+ *
+ * This is a simplified version of {@link #engineLoad} that only looks
+ * at the ContentInfo types.
+ *
+ * @param f the pkcs12 file
+ * @return if it's password-less
+ * @throws IOException
+ */
+ public static boolean isPasswordless(File f) throws IOException {
+
+ try (FileInputStream stream = new FileInputStream(f)) {
+ DerValue val = new DerValue(stream);
+ DerInputStream s = val.toDerInputStream();
+
+ s.getInteger(); // skip version
+
+ ContentInfo authSafe = new ContentInfo(s);
+ DerInputStream as = new DerInputStream(authSafe.getData());
+ for (DerValue seq : as.getSequence(2)) {
+ DerInputStream sci = new DerInputStream(seq.toByteArray());
+ ContentInfo safeContents = new ContentInfo(sci);
+ if (safeContents.getContentType()
+ .equals(ContentInfo.ENCRYPTED_DATA_OID)) {
+ // Certificate encrypted
+ return false;
+ }
+ }
+
+ if (s.available() > 0) {
+ // The MacData exists.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Locates a matched CertEntry from certEntries, and returns its cert.
* @param entry the KeyEntry to match
* @return a certificate, null if not found
*/
private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
*** 2337,2357 ****
* null keyId, we should skip it entirely.
*/
if (bagItem instanceof KeyEntry) {
KeyEntry entry = (KeyEntry)bagItem;
- if (bagItem instanceof PrivateKeyEntry) {
if (keyId == null) {
// Insert a localKeyID for the privateKey
// Note: This is a workaround to allow null localKeyID
// attribute in pkcs12 with one private key entry and
// associated cert-chain
if (privateKeyCount == 1) {
keyId = "01".getBytes("UTF8");
} else {
continue;
}
}
}
entry.keyId = keyId;
// restore date if it exists
String keyIdStr = new String(keyId, "UTF8");
--- 2407,2430 ----
* null keyId, we should skip it entirely.
*/
if (bagItem instanceof KeyEntry) {
KeyEntry entry = (KeyEntry)bagItem;
if (keyId == null) {
+ if (bagItem instanceof PrivateKeyEntry) {
// Insert a localKeyID for the privateKey
// Note: This is a workaround to allow null localKeyID
// attribute in pkcs12 with one private key entry and
// associated cert-chain
if (privateKeyCount == 1) {
keyId = "01".getBytes("UTF8");
} else {
continue;
}
+ } else {
+ // keyId in a SecretKeyEntry is not significant
+ keyId = "00".getBytes("UTF8");
}
}
entry.keyId = keyId;
// restore date if it exists
String keyIdStr = new String(keyId, "UTF8");
*** 2418,2423 ****
--- 2491,2575 ----
private String getUnfriendlyName() {
counter++;
return (String.valueOf(counter));
}
+
+ // 8076190: Customizing the generation of a PKCS12 keystore
+
+ private static String defaultCertProtectionAlgorithm() {
+ String result = SecurityProperties.privilegedGetOverridable(
+ "keystore.pkcs12.certProtectionAlgorithm");
+ return (result != null && !result.isEmpty())
+ ? result : "PBEWithSHA1AndRC2_40";
+ }
+
+ private static int defaultCertPbeIterationCount() {
+ String result = SecurityProperties.privilegedGetOverridable(
+ "keystore.pkcs12.certPbeIterationCount");
+ return (result != null && !result.isEmpty())
+ ? string2IC("certPbeIterationCount", result) : 50000;
+ }
+
+ // Read both "keystore.pkcs12.keyProtectionAlgorithm" and
+ // "keystore.PKCS12.keyProtectionAlgorithm" for compatibility.
+ private static String defaultKeyProtectionAlgorithm() {
+ String result = AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ String result;
+ String name1 = "keystore.pkcs12.keyProtectionAlgorithm";
+ String name2 = "keystore.PKCS12.keyProtectionAlgorithm";
+ result = System.getProperty(name1);
+ if (result != null) {
+ return result;
+ }
+ result = System.getProperty(name2);
+ if (result != null) {
+ return result;
+ }
+ result = Security.getProperty(name1);
+ if (result != null) {
+ return result;
+ }
+ return Security.getProperty(name2);
+ }
+ });
+ return (result != null && !result.isEmpty())
+ ? result : "PBEWithSHA1AndDESede";
+ }
+
+ private static int defaultKeyPbeIterationCount() {
+ String result = SecurityProperties.privilegedGetOverridable(
+ "keystore.pkcs12.keyPbeIterationCount");
+ return (result != null && !result.isEmpty())
+ ? string2IC("keyPbeIterationCount", result) : 50000;
+ }
+
+ private static String defaultMacAlgorithm() {
+ String result = SecurityProperties.privilegedGetOverridable(
+ "keystore.pkcs12.macAlgorithm");
+ return (result != null && !result.isEmpty())
+ ? result : "HmacPBESHA1";
+ }
+
+ private static int defaultMacIterationCount() {
+ String result = SecurityProperties.privilegedGetOverridable(
+ "keystore.pkcs12.macIterationCount");
+ return (result != null && !result.isEmpty())
+ ? string2IC("macIterationCount", result) : 100000;
+ }
+
+ private static int string2IC(String type, String value) {
+ int number;
+ try {
+ number = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("keystore.pkcs12." + type
+ + " is not a number: " + value);
+ }
+ if (number <= 0 || number > MAX_ITERATION_COUNT) {
+ throw new IllegalArgumentException("Invalid keystore.pkcs12."
+ + type + ": " + value);
+ }
+ return number;
+ }
}
< prev index next >