mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8076190: Customizing the generation of a PKCS12 keystore
Reviewed-by: mullan
This commit is contained in:
parent
0b05ebed2e
commit
9136c7d1d0
19 changed files with 1782 additions and 350 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -26,9 +26,7 @@
|
|||
package com.sun.crypto.provider;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.crypto.MacSpi;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
|
@ -36,18 +34,65 @@ import java.security.*;
|
|||
import java.security.spec.*;
|
||||
|
||||
/**
|
||||
* This is an implementation of the HMAC-PBESHA1 algorithm as defined
|
||||
* in PKCS#12 v1.0 standard.
|
||||
* This is an implementation of the HMAC algorithms as defined
|
||||
* in PKCS#12 v1.1 standard (see RFC 7292 Appendix B.4).
|
||||
*
|
||||
* @author Valerie Peng
|
||||
*/
|
||||
public final class HmacPKCS12PBESHA1 extends HmacCore {
|
||||
abstract class HmacPKCS12PBECore extends HmacCore {
|
||||
|
||||
public static final class HmacPKCS12PBE_SHA1 extends HmacPKCS12PBECore {
|
||||
public HmacPKCS12PBE_SHA1() throws NoSuchAlgorithmException {
|
||||
super("SHA1", 64);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class HmacPKCS12PBE_SHA224 extends HmacPKCS12PBECore {
|
||||
public HmacPKCS12PBE_SHA224() throws NoSuchAlgorithmException {
|
||||
super("SHA-224", 64);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class HmacPKCS12PBE_SHA256 extends HmacPKCS12PBECore {
|
||||
public HmacPKCS12PBE_SHA256() throws NoSuchAlgorithmException {
|
||||
super("SHA-256", 64);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class HmacPKCS12PBE_SHA384 extends HmacPKCS12PBECore {
|
||||
public HmacPKCS12PBE_SHA384() throws NoSuchAlgorithmException {
|
||||
super("SHA-384", 128);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class HmacPKCS12PBE_SHA512 extends HmacPKCS12PBECore {
|
||||
public HmacPKCS12PBE_SHA512() throws NoSuchAlgorithmException {
|
||||
super("SHA-512", 128);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class HmacPKCS12PBE_SHA512_224 extends HmacPKCS12PBECore {
|
||||
public HmacPKCS12PBE_SHA512_224() throws NoSuchAlgorithmException {
|
||||
super("SHA-512/224", 128);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class HmacPKCS12PBE_SHA512_256 extends HmacPKCS12PBECore {
|
||||
public HmacPKCS12PBE_SHA512_256() throws NoSuchAlgorithmException {
|
||||
super("SHA-512/256", 128);
|
||||
}
|
||||
}
|
||||
|
||||
private final String algorithm;
|
||||
private final int bl;
|
||||
|
||||
/**
|
||||
* Standard constructor, creates a new HmacSHA1 instance.
|
||||
*/
|
||||
public HmacPKCS12PBESHA1() throws NoSuchAlgorithmException {
|
||||
super("SHA1", 64);
|
||||
public HmacPKCS12PBECore(String algorithm, int bl) throws NoSuchAlgorithmException {
|
||||
super(algorithm, bl);
|
||||
this.algorithm = algorithm;
|
||||
this.bl = bl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,7 +177,8 @@ public final class HmacPKCS12PBESHA1 extends HmacCore {
|
|||
("IterationCount must be a positive number");
|
||||
}
|
||||
derivedKey = PKCS12PBECipherCore.derive(passwdChars, salt,
|
||||
iCount, engineGetMacLength(), PKCS12PBECipherCore.MAC_KEY);
|
||||
iCount, engineGetMacLength(), PKCS12PBECipherCore.MAC_KEY,
|
||||
algorithm, bl);
|
||||
} finally {
|
||||
Arrays.fill(passwdChars, '\0');
|
||||
}
|
|
@ -314,41 +314,48 @@ abstract class PBES2Parameters extends AlgorithmParametersSpi {
|
|||
+ "not an ASN.1 OCTET STRING tag");
|
||||
}
|
||||
iCount = pBKDF2_params.data.getInteger();
|
||||
|
||||
DerValue prf = null;
|
||||
// keyLength INTEGER (1..MAX) OPTIONAL,
|
||||
if (pBKDF2_params.data.available() > 0) {
|
||||
DerValue keyLength = pBKDF2_params.data.getDerValue();
|
||||
if (keyLength.tag == DerValue.tag_Integer) {
|
||||
keysize = keyLength.getInteger() * 8; // keysize (in bits)
|
||||
} else {
|
||||
// Should be the prf
|
||||
prf = keyLength;
|
||||
}
|
||||
}
|
||||
// prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
|
||||
String kdfAlgo = "HmacSHA1";
|
||||
if (pBKDF2_params.data.available() > 0) {
|
||||
if (pBKDF2_params.tag == DerValue.tag_Sequence) {
|
||||
DerValue prf = pBKDF2_params.data.getDerValue();
|
||||
kdfAlgo_OID = prf.data.getOID();
|
||||
if (hmacWithSHA1_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA1";
|
||||
} else if (hmacWithSHA224_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA224";
|
||||
} else if (hmacWithSHA256_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA256";
|
||||
} else if (hmacWithSHA384_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA384";
|
||||
} else if (hmacWithSHA512_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA512";
|
||||
} else {
|
||||
if (prf == null) {
|
||||
if (pBKDF2_params.data.available() > 0) {
|
||||
prf = pBKDF2_params.data.getDerValue();
|
||||
}
|
||||
}
|
||||
if (prf != null) {
|
||||
kdfAlgo_OID = prf.data.getOID();
|
||||
if (hmacWithSHA1_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA1";
|
||||
} else if (hmacWithSHA224_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA224";
|
||||
} else if (hmacWithSHA256_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA256";
|
||||
} else if (hmacWithSHA384_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA384";
|
||||
} else if (hmacWithSHA512_OID.equals(kdfAlgo_OID)) {
|
||||
kdfAlgo = "HmacSHA512";
|
||||
} else {
|
||||
throw new IOException("PBE parameter parsing error: "
|
||||
+ "expecting the object identifier for a HmacSHA key "
|
||||
+ "derivation function");
|
||||
}
|
||||
if (prf.data.available() != 0) {
|
||||
// parameter is 'NULL' for all HmacSHA KDFs
|
||||
DerValue parameter = prf.data.getDerValue();
|
||||
if (parameter.tag != DerValue.tag_Null) {
|
||||
throw new IOException("PBE parameter parsing error: "
|
||||
+ "expecting the object identifier for a HmacSHA key "
|
||||
+ "derivation function");
|
||||
}
|
||||
if (prf.data.available() != 0) {
|
||||
// parameter is 'NULL' for all HmacSHA KDFs
|
||||
DerValue parameter = prf.data.getDerValue();
|
||||
if (parameter.tag != DerValue.tag_Null) {
|
||||
throw new IOException("PBE parameter parsing error: "
|
||||
+ "not an ASN.1 NULL tag");
|
||||
}
|
||||
+ "not an ASN.1 NULL tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -684,8 +684,29 @@ public final class SunJCE extends Provider {
|
|||
ps("Mac", "HmacSHA512/256",
|
||||
"com.sun.crypto.provider.HmacCore$HmacSHA512_256",
|
||||
null, attrs);
|
||||
ps("Mac", "HmacPBESHA1", "com.sun.crypto.provider.HmacPKCS12PBESHA1",
|
||||
ps("Mac", "HmacPBESHA1",
|
||||
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA1",
|
||||
null, attrs);
|
||||
ps("Mac", "HmacPBESHA224",
|
||||
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA224",
|
||||
null, attrs);
|
||||
ps("Mac", "HmacPBESHA256",
|
||||
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA256",
|
||||
null, attrs);
|
||||
ps("Mac", "HmacPBESHA384",
|
||||
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA384",
|
||||
null, attrs);
|
||||
ps("Mac", "HmacPBESHA512",
|
||||
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512",
|
||||
null, attrs);
|
||||
ps("Mac", "HmacPBESHA512/224",
|
||||
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_224",
|
||||
null, attrs);
|
||||
ps("Mac", "HmacPBESHA512/256",
|
||||
"com.sun.crypto.provider.HmacPKCS12PBECore$HmacPKCS12PBE_SHA512_256",
|
||||
null, attrs);
|
||||
|
||||
|
||||
// PBMAC1
|
||||
ps("Mac", "PBEWithHmacSHA1",
|
||||
"com.sun.crypto.provider.PBMAC1Core$HmacSHA1", null, attrs);
|
||||
|
|
|
@ -314,14 +314,7 @@ public class KeyStore {
|
|||
/**
|
||||
* Gets the name of the protection algorithm.
|
||||
* If none was set then the keystore provider will use its default
|
||||
* protection algorithm. The name of the default protection algorithm
|
||||
* for a given keystore type is set using the
|
||||
* {@code 'keystore.<type>.keyProtectionAlgorithm'} security property.
|
||||
* For example, the
|
||||
* {@code keystore.PKCS12.keyProtectionAlgorithm} property stores the
|
||||
* name of the default key protection algorithm used for PKCS12
|
||||
* keystores. If the security property is not set, an
|
||||
* implementation-specific algorithm will be used.
|
||||
* protection algorithm.
|
||||
*
|
||||
* @return the algorithm name, or {@code null} if none was set
|
||||
*
|
||||
|
@ -1813,8 +1806,8 @@ public class KeyStore {
|
|||
}
|
||||
}
|
||||
|
||||
throw new KeyStoreException("This keystore does not support probing "
|
||||
+ "and must be loaded with a specified type");
|
||||
throw new KeyStoreException("Unrecognized keystore format. "
|
||||
+ "Please load it with a specified type");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -53,7 +53,6 @@ import java.util.*;
|
|||
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
@ -70,6 +69,7 @@ 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;
|
||||
import sun.security.provider.JavaKeyStore.JKS;
|
||||
|
@ -81,9 +81,11 @@ import sun.security.util.KeyStoreDelegator;
|
|||
* 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
|
||||
* 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 :
|
||||
*
|
||||
|
@ -126,11 +128,7 @@ import sun.security.util.KeyStoreDelegator;
|
|||
* @author Jeff Nisewanger
|
||||
* @author Jan Luehe
|
||||
*
|
||||
* @see KeyProtector
|
||||
* @see java.security.KeyStoreSpi
|
||||
* @see KeyTool
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class PKCS12KeyStore extends KeyStoreSpi {
|
||||
|
||||
|
@ -143,14 +141,7 @@ 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
|
||||
|
@ -171,10 +162,6 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
|
||||
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
|
||||
/*
|
||||
|
@ -185,17 +172,15 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
{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 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;
|
||||
|
||||
|
@ -210,6 +195,17 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
// 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;
|
||||
|
||||
|
@ -221,16 +217,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
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
|
||||
throw new AssertionError("OID not initialized", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,7 +383,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
ic = pbeSpec.getIterationCount();
|
||||
|
||||
if (ic > MAX_ITERATION_COUNT) {
|
||||
throw new IOException("PBE iteration count too large");
|
||||
throw new IOException("key PBE iteration count too large");
|
||||
}
|
||||
} else {
|
||||
ic = 0;
|
||||
|
@ -424,7 +416,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
if (debug != null) {
|
||||
debug.println("Retrieved a protected private key at alias" +
|
||||
" '" + alias + "' (" +
|
||||
new AlgorithmId(algOid).getName() +
|
||||
mapPBEParamsToAlgorithm(algOid, algParams) +
|
||||
" iterations: " + ic + ")");
|
||||
}
|
||||
return tmp;
|
||||
|
@ -449,7 +441,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
if (debug != null) {
|
||||
debug.println("Retrieved a protected secret key at alias " +
|
||||
"'" + alias + "' (" +
|
||||
new AlgorithmId(algOid).getName() +
|
||||
mapPBEParamsToAlgorithm(algOid, algParams) +
|
||||
" iterations: " + ic + ")");
|
||||
}
|
||||
return tmp;
|
||||
|
@ -701,7 +693,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
|
||||
|
||||
} catch (Exception nsae) {
|
||||
throw new KeyStoreException("Key protection " +
|
||||
throw new KeyStoreException("Key protection" +
|
||||
" algorithm not found: " + nsae, nsae);
|
||||
}
|
||||
}
|
||||
|
@ -801,14 +793,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
/*
|
||||
* Generate PBE Algorithm Parameters
|
||||
*/
|
||||
private AlgorithmParameters getPBEAlgorithmParameters(String algorithm)
|
||||
throws IOException
|
||||
{
|
||||
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(), PBE_ITERATION_COUNT);
|
||||
new PBEParameterSpec(getSalt(), iterationCount);
|
||||
try {
|
||||
algParams = AlgorithmParameters.getInstance(algorithm);
|
||||
algParams.init(paramSpec);
|
||||
|
@ -871,13 +862,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
}
|
||||
|
||||
/*
|
||||
* Encrypt private key using Password-based encryption (PBE)
|
||||
* 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 encoded as EncryptedPrivateKeyInfo
|
||||
* @return encrypted private key or secret key encoded as
|
||||
* EncryptedPrivateKeyInfo
|
||||
*/
|
||||
private byte[] encryptPrivateKey(byte[] data,
|
||||
KeyStore.PasswordProtection passwordProtection)
|
||||
|
@ -899,27 +891,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
algParams = AlgorithmParameters.getInstance(algorithm);
|
||||
algParams.init(algParamSpec);
|
||||
} else {
|
||||
algParams = getPBEAlgorithmParameters(algorithm);
|
||||
algParams = getPBEAlgorithmParameters(algorithm,
|
||||
defaultKeyPbeIterationCount());
|
||||
}
|
||||
} 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);
|
||||
algorithm = defaultKeyProtectionAlgorithm();
|
||||
algParams = getPBEAlgorithmParameters(algorithm,
|
||||
defaultKeyPbeIterationCount());
|
||||
}
|
||||
|
||||
ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm);
|
||||
|
@ -977,7 +956,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
if (algorithm.equals(pbes2_OID) && algParams != null) {
|
||||
return algParams.toString();
|
||||
}
|
||||
return algorithm.toString();
|
||||
return new AlgorithmId(algorithm).getName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1209,10 +1188,6 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
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();
|
||||
|
@ -1245,16 +1220,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
// -- 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);
|
||||
ContentInfo encrContentInfo =
|
||||
new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
|
||||
if (!certProtectionAlgorithm.equalsIgnoreCase("NONE")) {
|
||||
ContentInfo encrContentInfo =
|
||||
new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID,
|
||||
new DerValue(encrData));
|
||||
encrContentInfo.encode(authSafeContentInfo);
|
||||
encrContentInfo.encode(authSafeContentInfo);
|
||||
} else {
|
||||
ContentInfo dataContentInfo = new ContentInfo(encrData);
|
||||
dataContentInfo.encode(authSafeContentInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// wrap as SequenceOf ContentInfos
|
||||
|
@ -1269,9 +1256,16 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
pfx.write(authSafeData);
|
||||
|
||||
// -- MAC
|
||||
byte[] macData = calculateMac(password, authenticatedSafe);
|
||||
pfx.write(macData);
|
||||
|
||||
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);
|
||||
|
@ -1481,24 +1475,6 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
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)
|
||||
*
|
||||
|
@ -1509,16 +1485,16 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
throws IOException
|
||||
{
|
||||
byte[] mData = null;
|
||||
String algName = "SHA1";
|
||||
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("HmacPBESHA1");
|
||||
Mac m = Mac.getInstance(macAlgorithm);
|
||||
PBEParameterSpec params =
|
||||
new PBEParameterSpec(salt, MAC_ITERATION_COUNT);
|
||||
new PBEParameterSpec(salt, macIterationCount);
|
||||
SecretKey key = getPBEKey(passwd);
|
||||
m.init(key, params);
|
||||
m.update(data);
|
||||
|
@ -1526,7 +1502,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
|
||||
// encode as MacData
|
||||
MacData macData = new MacData(algName, macResult, salt,
|
||||
MAC_ITERATION_COUNT);
|
||||
macIterationCount);
|
||||
DerOutputStream bytes = new DerOutputStream();
|
||||
bytes.write(macData.getEncoded());
|
||||
mData = bytes.toByteArray();
|
||||
|
@ -1799,15 +1775,19 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
byte[] safeBagData = safeBagValue.toByteArray();
|
||||
|
||||
// encrypt the content (EncryptedContentInfo)
|
||||
byte[] encrContentInfo = encryptContent(safeBagData, password);
|
||||
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();
|
||||
// -- 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1916,47 +1896,52 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
|
||||
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 {
|
||||
// create AlgorithmParameters
|
||||
AlgorithmParameters algParams = getPBEAlgorithmParameters(
|
||||
certProtectionAlgorithm, certPbeIterationCount);
|
||||
DerOutputStream bytes = new DerOutputStream();
|
||||
|
||||
// Use JCE
|
||||
SecretKey skey = getPBEKey(password);
|
||||
Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40");
|
||||
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);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1979,10 +1964,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
public synchronized void engineLoad(InputStream stream, char[] password)
|
||||
throws IOException, NoSuchAlgorithmException, CertificateException
|
||||
{
|
||||
DataInputStream dis;
|
||||
CertificateFactory cf = null;
|
||||
ByteArrayInputStream bais = null;
|
||||
byte[] encoded = null;
|
||||
|
||||
// Reset config when loading a different keystore.
|
||||
certProtectionAlgorithm = null;
|
||||
certPbeIterationCount = -1;
|
||||
macAlgorithm = null;
|
||||
macIterationCount = -1;
|
||||
|
||||
if (stream == null)
|
||||
return;
|
||||
|
@ -2022,6 +2009,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
secretKeyCount = 0;
|
||||
certificateCount = 0;
|
||||
|
||||
boolean seeEncBag = false;
|
||||
|
||||
/*
|
||||
* Spin over the ContentInfos.
|
||||
*/
|
||||
|
@ -2047,6 +2036,21 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -2084,13 +2088,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
ic = pbeSpec.getIterationCount();
|
||||
|
||||
if (ic > MAX_ITERATION_COUNT) {
|
||||
throw new IOException("PBE iteration count too large");
|
||||
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 " +
|
||||
"(" + new AlgorithmId(algOid).getName() +
|
||||
"(" + mapPBEParamsToAlgorithm(algOid, algParams) +
|
||||
" iterations: " + ic + ")");
|
||||
}
|
||||
|
||||
|
@ -2115,48 +2124,62 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
}
|
||||
}
|
||||
|
||||
// No ENCRYPTED_DATA_OID but see certificate. Must be passwordless.
|
||||
if (!seeEncBag && certificateCount > 0) {
|
||||
certProtectionAlgorithm = "NONE";
|
||||
}
|
||||
|
||||
// The MacData is optional.
|
||||
if (password != null && s.available() > 0) {
|
||||
MacData macData = new MacData(s);
|
||||
int ic = macData.getIterations();
|
||||
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) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"MAC iteration count too large: " + ic);
|
||||
try {
|
||||
if (ic > MAX_ITERATION_COUNT) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"MAC iteration count too large: " + ic);
|
||||
}
|
||||
|
||||
String algName =
|
||||
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);
|
||||
m.init(key, params);
|
||||
m.update(authSafeData);
|
||||
byte[] macResult = m.doFinal();
|
||||
|
||||
if (debug != null) {
|
||||
debug.println("Checking keystore integrity " +
|
||||
"(" + m.getAlgorithm() + " iterations: " + ic + ")");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
String algName =
|
||||
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);
|
||||
m.init(key, params);
|
||||
m.update(authSafeData);
|
||||
byte[] macResult = m.doFinal();
|
||||
|
||||
if (debug != null) {
|
||||
debug.println("Checking keystore integrity " +
|
||||
"(" + m.getAlgorithm() + " iterations: " + ic + ")");
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2196,8 +2219,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
cert = certsMap.get(issuerDN);
|
||||
}
|
||||
/* Update existing KeyEntry in entries table */
|
||||
if (chain.size() > 0)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2221,6 +2250,46 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
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
|
||||
|
@ -2368,8 +2437,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
if (bagItem instanceof KeyEntry) {
|
||||
KeyEntry entry = (KeyEntry)bagItem;
|
||||
|
||||
if (bagItem instanceof PrivateKeyEntry) {
|
||||
if (keyId == null) {
|
||||
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
|
||||
|
@ -2379,6 +2448,9 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// keyId in a SecretKeyEntry is not significant
|
||||
keyId = "00".getBytes("UTF8");
|
||||
}
|
||||
}
|
||||
entry.keyId = keyId;
|
||||
|
@ -2518,4 +2590,83 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ import java.security.cert.X509CRLSelector;
|
|||
import javax.security.auth.x500.X500Principal;
|
||||
import java.util.Base64;
|
||||
|
||||
import sun.security.pkcs12.PKCS12KeyStore;
|
||||
import sun.security.util.ECKeySizeParameterSpec;
|
||||
import sun.security.util.KeyUtil;
|
||||
import sun.security.util.NamedCurve;
|
||||
|
@ -79,6 +80,7 @@ import sun.security.pkcs10.PKCS10Attribute;
|
|||
import sun.security.provider.X509Factory;
|
||||
import sun.security.provider.certpath.ssl.SSLServerCertStore;
|
||||
import sun.security.util.Password;
|
||||
import sun.security.util.SecurityProperties;
|
||||
import sun.security.util.SecurityProviderConstants;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
|
@ -188,6 +190,7 @@ public final class Main {
|
|||
|
||||
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
|
||||
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
|
||||
private boolean isPasswordlessKeyStore = false;
|
||||
|
||||
enum Command {
|
||||
CERTREQ("Generates.a.certificate.request",
|
||||
|
@ -919,6 +922,9 @@ public final class Main {
|
|||
storetype == null && !inplaceImport) {
|
||||
keyStore = KeyStore.getInstance(ksfile, storePass);
|
||||
storetype = keyStore.getType();
|
||||
if (storetype.equalsIgnoreCase("pkcs12")) {
|
||||
isPasswordlessKeyStore = PKCS12KeyStore.isPasswordless(ksfile);
|
||||
}
|
||||
} else {
|
||||
if (storetype == null) {
|
||||
storetype = KeyStore.getDefaultType();
|
||||
|
@ -928,6 +934,15 @@ public final class Main {
|
|||
} else {
|
||||
keyStore = KeyStore.getInstance(storetype, providerName);
|
||||
}
|
||||
// When creating a new pkcs12 file, Do not prompt for storepass
|
||||
// if certProtectionAlgorithm and macAlgorithm are both NONE.
|
||||
if (storetype.equalsIgnoreCase("pkcs12")) {
|
||||
isPasswordlessKeyStore =
|
||||
"NONE".equals(SecurityProperties.privilegedGetOverridable(
|
||||
"keystore.pkcs12.certProtectionAlgorithm"))
|
||||
&& "NONE".equals(SecurityProperties.privilegedGetOverridable(
|
||||
"keystore.pkcs12.macAlgorithm"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the keystore data.
|
||||
|
@ -979,11 +994,10 @@ public final class Main {
|
|||
("Keystore.password.must.be.at.least.6.characters"));
|
||||
}
|
||||
} else if (storePass == null) {
|
||||
|
||||
// only prompt if (protectedPath == false)
|
||||
|
||||
if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) &&
|
||||
(command == CERTREQ ||
|
||||
if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype)
|
||||
&& isKeyStoreRelated(command)
|
||||
&& !isPasswordlessKeyStore) {
|
||||
if (command == CERTREQ ||
|
||||
command == DELETE ||
|
||||
command == GENKEYPAIR ||
|
||||
command == GENSECKEY ||
|
||||
|
@ -995,59 +1009,58 @@ public final class Main {
|
|||
command == SELFCERT ||
|
||||
command == STOREPASSWD ||
|
||||
command == KEYPASSWD ||
|
||||
command == IDENTITYDB)) {
|
||||
int count = 0;
|
||||
do {
|
||||
if (command == IMPORTKEYSTORE) {
|
||||
System.err.print
|
||||
(rb.getString("Enter.destination.keystore.password."));
|
||||
} else {
|
||||
System.err.print
|
||||
(rb.getString("Enter.keystore.password."));
|
||||
}
|
||||
System.err.flush();
|
||||
storePass = Password.readPassword(System.in);
|
||||
passwords.add(storePass);
|
||||
command == IDENTITYDB) {
|
||||
int count = 0;
|
||||
do {
|
||||
if (command == IMPORTKEYSTORE) {
|
||||
System.err.print
|
||||
(rb.getString("Enter.destination.keystore.password."));
|
||||
} else {
|
||||
System.err.print
|
||||
(rb.getString("Enter.keystore.password."));
|
||||
}
|
||||
System.err.flush();
|
||||
storePass = Password.readPassword(System.in);
|
||||
passwords.add(storePass);
|
||||
|
||||
// If we are creating a new non nullStream-based keystore,
|
||||
// insist that the password be at least 6 characters
|
||||
if (!nullStream && (storePass == null || storePass.length < 6)) {
|
||||
System.err.println(rb.getString
|
||||
("Keystore.password.is.too.short.must.be.at.least.6.characters"));
|
||||
storePass = null;
|
||||
}
|
||||
|
||||
// If the keystore file does not exist and needs to be
|
||||
// created, the storepass should be prompted twice.
|
||||
if (storePass != null && !nullStream && ksStream == null) {
|
||||
System.err.print(rb.getString("Re.enter.new.password."));
|
||||
char[] storePassAgain = Password.readPassword(System.in);
|
||||
passwords.add(storePassAgain);
|
||||
if (!Arrays.equals(storePass, storePassAgain)) {
|
||||
System.err.println
|
||||
(rb.getString("They.don.t.match.Try.again"));
|
||||
// If we are creating a new non nullStream-based keystore,
|
||||
// insist that the password be at least 6 characters
|
||||
if (!nullStream && (storePass == null || storePass.length < 6)) {
|
||||
System.err.println(rb.getString
|
||||
("Keystore.password.is.too.short.must.be.at.least.6.characters"));
|
||||
storePass = null;
|
||||
}
|
||||
|
||||
// If the keystore file does not exist and needs to be
|
||||
// created, the storepass should be prompted twice.
|
||||
if (storePass != null && !nullStream && ksStream == null) {
|
||||
System.err.print(rb.getString("Re.enter.new.password."));
|
||||
char[] storePassAgain = Password.readPassword(System.in);
|
||||
passwords.add(storePassAgain);
|
||||
if (!Arrays.equals(storePass, storePassAgain)) {
|
||||
System.err.println
|
||||
(rb.getString("They.don.t.match.Try.again"));
|
||||
storePass = null;
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
} while ((storePass == null) && count < 3);
|
||||
|
||||
|
||||
if (storePass == null) {
|
||||
System.err.println
|
||||
(rb.getString("Too.many.failures.try.later"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// here we have EXPORTCERT and LIST (info valid until STOREPASSWD)
|
||||
if (command != PRINTCRL) {
|
||||
System.err.print(rb.getString("Enter.keystore.password."));
|
||||
System.err.flush();
|
||||
storePass = Password.readPassword(System.in);
|
||||
passwords.add(storePass);
|
||||
}
|
||||
|
||||
count++;
|
||||
} while ((storePass == null) && count < 3);
|
||||
|
||||
|
||||
if (storePass == null) {
|
||||
System.err.println
|
||||
(rb.getString("Too.many.failures.try.later"));
|
||||
return;
|
||||
}
|
||||
} else if (!protectedPath
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(storetype)
|
||||
&& isKeyStoreRelated(command)) {
|
||||
// here we have EXPORTCERT and LIST (info valid until STOREPASSWD)
|
||||
if (command != PRINTCRL) {
|
||||
System.err.print(rb.getString("Enter.keystore.password."));
|
||||
System.err.flush();
|
||||
storePass = Password.readPassword(System.in);
|
||||
passwords.add(storePass);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1233,7 +1246,8 @@ public final class Main {
|
|||
kssave = true;
|
||||
} else if (command == LIST) {
|
||||
if (storePass == null
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(storetype)
|
||||
&& !isPasswordlessKeyStore) {
|
||||
printNoIntegrityWarning();
|
||||
}
|
||||
|
||||
|
@ -1602,7 +1616,8 @@ public final class Main {
|
|||
throws Exception
|
||||
{
|
||||
if (storePass == null
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(storetype)
|
||||
&& !isPasswordlessKeyStore) {
|
||||
printNoIntegrityWarning();
|
||||
}
|
||||
if (alias == null) {
|
||||
|
@ -1633,7 +1648,7 @@ public final class Main {
|
|||
* @param origPass the password to copy from if user press ENTER
|
||||
*/
|
||||
private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{
|
||||
if (P12KEYSTORE.equalsIgnoreCase(storetype)) {
|
||||
if (origPass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) {
|
||||
return origPass;
|
||||
} else if (!token && !protectedPath) {
|
||||
// Prompt for key password
|
||||
|
@ -1642,22 +1657,25 @@ public final class Main {
|
|||
MessageFormat form = new MessageFormat(rb.getString
|
||||
("Enter.key.password.for.alias."));
|
||||
Object[] source = {alias};
|
||||
System.err.println(form.format(source));
|
||||
if (orig == null) {
|
||||
System.err.print(rb.getString
|
||||
(".RETURN.if.same.as.keystore.password."));
|
||||
} else {
|
||||
form = new MessageFormat(rb.getString
|
||||
(".RETURN.if.same.as.for.otherAlias."));
|
||||
Object[] src = {orig};
|
||||
System.err.print(form.format(src));
|
||||
System.err.print(form.format(source));
|
||||
if (origPass != null) {
|
||||
System.err.println();
|
||||
if (orig == null) {
|
||||
System.err.print(rb.getString
|
||||
(".RETURN.if.same.as.keystore.password."));
|
||||
} else {
|
||||
form = new MessageFormat(rb.getString
|
||||
(".RETURN.if.same.as.for.otherAlias."));
|
||||
Object[] src = {orig};
|
||||
System.err.print(form.format(src));
|
||||
}
|
||||
}
|
||||
System.err.flush();
|
||||
char[] entered = Password.readPassword(System.in);
|
||||
passwords.add(entered);
|
||||
if (entered == null) {
|
||||
if (entered == null && origPass != null) {
|
||||
return origPass;
|
||||
} else if (entered.length >= 6) {
|
||||
} else if (entered != null && entered.length >= 6) {
|
||||
System.err.print(rb.getString("Re.enter.new.password."));
|
||||
char[] passAgain = Password.readPassword(System.in);
|
||||
passwords.add(passAgain);
|
||||
|
@ -2066,6 +2084,9 @@ public final class Main {
|
|||
getCertFingerPrint("SHA-256", chain[0]));
|
||||
checkWeak(label, chain);
|
||||
}
|
||||
} else {
|
||||
out.println(rb.getString
|
||||
("Certificate.chain.length.") + 0);
|
||||
}
|
||||
} else if (keyStore.entryInstanceOf(alias,
|
||||
KeyStore.TrustedCertificateEntry.class)) {
|
||||
|
@ -2130,6 +2151,7 @@ public final class Main {
|
|||
|
||||
InputStream is = null;
|
||||
File srcksfile = null;
|
||||
boolean srcIsPasswordless = false;
|
||||
|
||||
if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
|
||||
KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
|
||||
|
@ -2151,6 +2173,9 @@ public final class Main {
|
|||
srcstoretype == null) {
|
||||
store = KeyStore.getInstance(srcksfile, srcstorePass);
|
||||
srcstoretype = store.getType();
|
||||
if (srcstoretype.equalsIgnoreCase("pkcs12")) {
|
||||
srcIsPasswordless = PKCS12KeyStore.isPasswordless(srcksfile);
|
||||
}
|
||||
} else {
|
||||
if (srcstoretype == null) {
|
||||
srcstoretype = KeyStore.getDefaultType();
|
||||
|
@ -2164,7 +2189,8 @@ public final class Main {
|
|||
|
||||
if (srcstorePass == null
|
||||
&& !srcprotectedPath
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)
|
||||
&& !srcIsPasswordless) {
|
||||
System.err.print(rb.getString("Enter.source.keystore.password."));
|
||||
System.err.flush();
|
||||
srcstorePass = Password.readPassword(System.in);
|
||||
|
@ -2191,6 +2217,7 @@ public final class Main {
|
|||
}
|
||||
|
||||
if (srcstorePass == null
|
||||
&& !srcIsPasswordless
|
||||
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
|
||||
// anti refactoring, copied from printNoIntegrityWarning(),
|
||||
// but change 2 lines
|
||||
|
@ -3537,25 +3564,25 @@ public final class Main {
|
|||
|
||||
if (keyPass == null) {
|
||||
// Try to recover the key using the keystore password
|
||||
try {
|
||||
key = keyStore.getKey(alias, storePass);
|
||||
|
||||
keyPass = storePass;
|
||||
passwords.add(keyPass);
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
// Did not work out, so prompt user for key password
|
||||
if (!token) {
|
||||
keyPass = getKeyPasswd(alias, null, null);
|
||||
key = keyStore.getKey(alias, keyPass);
|
||||
} else {
|
||||
throw e;
|
||||
if (storePass != null) {
|
||||
try {
|
||||
key = keyStore.getKey(alias, storePass);
|
||||
passwords.add(storePass);
|
||||
return Pair.of(key, storePass);
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
if (token) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
// prompt user for key password
|
||||
keyPass = getKeyPasswd(alias, null, null);
|
||||
key = keyStore.getKey(alias, keyPass);
|
||||
return Pair.of(key, keyPass);
|
||||
} else {
|
||||
key = keyStore.getKey(alias, keyPass);
|
||||
return Pair.of(key, keyPass);
|
||||
}
|
||||
|
||||
return Pair.of(key, keyPass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3570,68 +3597,59 @@ public final class Main {
|
|||
char[] pstore,
|
||||
char[] pkey) throws Exception {
|
||||
|
||||
if (ks.containsAlias(alias) == false) {
|
||||
MessageFormat form = new MessageFormat
|
||||
(rb.getString("Alias.alias.does.not.exist"));
|
||||
if (!ks.containsAlias(alias)) {
|
||||
MessageFormat form = new MessageFormat(
|
||||
rb.getString("Alias.alias.does.not.exist"));
|
||||
Object[] source = {alias};
|
||||
throw new Exception(form.format(source));
|
||||
}
|
||||
|
||||
PasswordProtection pp = null;
|
||||
Entry entry;
|
||||
|
||||
// Step 1: First attempt to access entry without key password
|
||||
// (PKCS11 entry or trusted certificate entry, for example).
|
||||
// If fail, go next.
|
||||
try {
|
||||
// First attempt to access entry without key password
|
||||
// (PKCS11 entry or trusted certificate entry, for example)
|
||||
|
||||
entry = ks.getEntry(alias, pp);
|
||||
pkey = null;
|
||||
Entry entry = ks.getEntry(alias, null);
|
||||
return Pair.of(entry, null);
|
||||
} catch (UnrecoverableEntryException une) {
|
||||
|
||||
if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) ||
|
||||
KeyStoreUtil.isWindowsKeyStore(ks.getType())) {
|
||||
KeyStoreUtil.isWindowsKeyStore(ks.getType())) {
|
||||
// should not happen, but a possibility
|
||||
throw une;
|
||||
}
|
||||
}
|
||||
|
||||
// entry is protected
|
||||
// entry is protected
|
||||
|
||||
if (pkey != null) {
|
||||
// Step 2: try pkey if not null. If fail, fail.
|
||||
if (pkey != null) {
|
||||
PasswordProtection pp = new PasswordProtection(pkey);
|
||||
Entry entry = ks.getEntry(alias, pp);
|
||||
return Pair.of(entry, pkey);
|
||||
}
|
||||
|
||||
// try provided key password
|
||||
|
||||
pp = new PasswordProtection(pkey);
|
||||
entry = ks.getEntry(alias, pp);
|
||||
|
||||
} else {
|
||||
|
||||
// try store pass
|
||||
|
||||
try {
|
||||
pp = new PasswordProtection(pstore);
|
||||
entry = ks.getEntry(alias, pp);
|
||||
pkey = pstore;
|
||||
} catch (UnrecoverableEntryException une2) {
|
||||
if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {
|
||||
|
||||
// P12 keystore currently does not support separate
|
||||
// store and entry passwords
|
||||
|
||||
throw une2;
|
||||
} else {
|
||||
|
||||
// prompt for entry password
|
||||
|
||||
pkey = getKeyPasswd(alias, null, null);
|
||||
pp = new PasswordProtection(pkey);
|
||||
entry = ks.getEntry(alias, pp);
|
||||
}
|
||||
// Step 3: try pstore if not null. If fail, go next.
|
||||
if (pstore != null) {
|
||||
try {
|
||||
PasswordProtection pp = new PasswordProtection(pstore);
|
||||
Entry entry = ks.getEntry(alias, pp);
|
||||
return Pair.of(entry, pstore);
|
||||
} catch (UnrecoverableEntryException une) {
|
||||
if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) {
|
||||
// P12 keystore currently does not support separate
|
||||
// store and entry passwords. We will not prompt for
|
||||
// entry password.
|
||||
throw une;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: prompt for entry password
|
||||
pkey = getKeyPasswd(alias, null, null);
|
||||
PasswordProtection pp = new PasswordProtection(pkey);
|
||||
Entry entry = ks.getEntry(alias, pp);
|
||||
return Pair.of(entry, pkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the requested finger print of the certificate.
|
||||
*/
|
||||
|
|
|
@ -771,8 +771,14 @@ public class AlgorithmId implements Serializable, DerEncoder {
|
|||
ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 10});
|
||||
public static final ObjectIdentifier pbeWithSHA1AndRC2_oid =
|
||||
ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 11});
|
||||
public static ObjectIdentifier pbeWithSHA1AndRC4_128_oid =
|
||||
ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 1});
|
||||
public static ObjectIdentifier pbeWithSHA1AndRC4_40_oid =
|
||||
ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 2});
|
||||
public static ObjectIdentifier pbeWithSHA1AndDESede_oid =
|
||||
ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 3});
|
||||
public static ObjectIdentifier pbeWithSHA1AndRC2_128_oid =
|
||||
ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 5});
|
||||
public static ObjectIdentifier pbeWithSHA1AndRC2_40_oid =
|
||||
ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 6});
|
||||
|
||||
|
@ -961,7 +967,10 @@ public class AlgorithmId implements Serializable, DerEncoder {
|
|||
nameTable.put(pbeWithMD5AndRC2_oid, "PBEWithMD5AndRC2");
|
||||
nameTable.put(pbeWithSHA1AndDES_oid, "PBEWithSHA1AndDES");
|
||||
nameTable.put(pbeWithSHA1AndRC2_oid, "PBEWithSHA1AndRC2");
|
||||
nameTable.put(pbeWithSHA1AndRC4_128_oid, "PBEWithSHA1AndRC4_128");
|
||||
nameTable.put(pbeWithSHA1AndRC4_40_oid, "PBEWithSHA1AndRC4_40");
|
||||
nameTable.put(pbeWithSHA1AndDESede_oid, "PBEWithSHA1AndDESede");
|
||||
nameTable.put(pbeWithSHA1AndRC2_128_oid, "PBEWithSHA1AndRC2_128");
|
||||
nameTable.put(pbeWithSHA1AndRC2_40_oid, "PBEWithSHA1AndRC2_40");
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue