提交 a77b812a 编写于 作者: V vinnie

8005408: KeyStore API enhancements

Reviewed-by: mullan
上级 b691337a
......@@ -26,6 +26,7 @@
package java.security;
import java.io.*;
import java.net.URI;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
......@@ -405,7 +406,44 @@ public class KeyStore {
*
* @since 1.5
*/
public static interface Entry { }
public static interface Entry {
/**
* Retrieves the attributes associated with an entry.
* <p>
* The default implementation returns an empty {@code Set}.
*
* @return an unmodifiable {@code Set} of attributes, possibly empty
*
* @since 1.8
*/
public default Set<Attribute> getAttributes() {
return Collections.<Attribute>emptySet();
}
/**
* An attribute associated with a keystore entry.
* It comprises a name and one or more values.
*
* @since 1.8
*/
public interface Attribute {
/**
* Returns the attribute's name.
*
* @return the attribute name
*/
public String getName();
/**
* Returns the attribute's value.
* Multi-valued attributes encode their values as a single string.
*
* @return the attribute value
*/
public String getValue();
}
}
/**
* A <code>KeyStore</code> entry that holds a <code>PrivateKey</code>
......@@ -417,6 +455,7 @@ public class KeyStore {
private final PrivateKey privKey;
private final Certificate[] chain;
private final Set<Attribute> attributes;
/**
* Constructs a <code>PrivateKeyEntry</code> with a
......@@ -443,7 +482,39 @@ public class KeyStore {
* in the end entity <code>Certificate</code> (at index 0)
*/
public PrivateKeyEntry(PrivateKey privateKey, Certificate[] chain) {
if (privateKey == null || chain == null) {
this(privateKey, chain, Collections.<Attribute>emptySet());
}
/**
* Constructs a {@code PrivateKeyEntry} with a {@code PrivateKey} and
* corresponding certificate chain and associated entry attributes.
*
* <p> The specified {@code chain} and {@code attributes} are cloned
* before they are stored in the new {@code PrivateKeyEntry} object.
*
* @param privateKey the {@code PrivateKey}
* @param chain an array of {@code Certificate}s
* representing the certificate chain.
* The chain must be ordered and contain a
* {@code Certificate} at index 0
* corresponding to the private key.
* @param attributes the attributes
*
* @exception NullPointerException if {@code privateKey}, {@code chain}
* or {@code attributes} is {@code null}
* @exception IllegalArgumentException if the specified chain has a
* length of 0, if the specified chain does not contain
* {@code Certificate}s of the same type,
* or if the {@code PrivateKey} algorithm
* does not match the algorithm of the {@code PublicKey}
* in the end entity {@code Certificate} (at index 0)
*
* @since 1.8
*/
public PrivateKeyEntry(PrivateKey privateKey, Certificate[] chain,
Set<Attribute> attributes) {
if (privateKey == null || chain == null || attributes == null) {
throw new NullPointerException("invalid null input");
}
if (chain.length == 0) {
......@@ -478,6 +549,9 @@ public class KeyStore {
} else {
this.chain = clonedChain;
}
this.attributes =
Collections.unmodifiableSet(new HashSet<>(attributes));
}
/**
......@@ -518,6 +592,19 @@ public class KeyStore {
return chain[0];
}
/**
* Retrieves the attributes associated with an entry.
* <p>
*
* @return an unmodifiable {@code Set} of attributes, possibly empty
*
* @since 1.8
*/
@Override
public Set<Attribute> getAttributes() {
return attributes;
}
/**
* Returns a string representation of this PrivateKeyEntry.
* @return a string representation of this PrivateKeyEntry.
......@@ -543,6 +630,7 @@ public class KeyStore {
public static final class SecretKeyEntry implements Entry {
private final SecretKey sKey;
private final Set<Attribute> attributes;
/**
* Constructs a <code>SecretKeyEntry</code> with a
......@@ -558,6 +646,32 @@ public class KeyStore {
throw new NullPointerException("invalid null input");
}
this.sKey = secretKey;
this.attributes = Collections.<Attribute>emptySet();
}
/**
* Constructs a {@code SecretKeyEntry} with a {@code SecretKey} and
* associated entry attributes.
*
* <p> The specified {@code attributes} is cloned before it is stored
* in the new {@code SecretKeyEntry} object.
*
* @param secretKey the {@code SecretKey}
* @param attributes the attributes
*
* @exception NullPointerException if {@code secretKey} or
* {@code attributes} is {@code null}
*
* @since 1.8
*/
public SecretKeyEntry(SecretKey secretKey, Set<Attribute> attributes) {
if (secretKey == null || attributes == null) {
throw new NullPointerException("invalid null input");
}
this.sKey = secretKey;
this.attributes =
Collections.unmodifiableSet(new HashSet<>(attributes));
}
/**
......@@ -569,6 +683,19 @@ public class KeyStore {
return sKey;
}
/**
* Retrieves the attributes associated with an entry.
* <p>
*
* @return an unmodifiable {@code Set} of attributes, possibly empty
*
* @since 1.8
*/
@Override
public Set<Attribute> getAttributes() {
return attributes;
}
/**
* Returns a string representation of this SecretKeyEntry.
* @return a string representation of this SecretKeyEntry.
......@@ -587,6 +714,7 @@ public class KeyStore {
public static final class TrustedCertificateEntry implements Entry {
private final Certificate cert;
private final Set<Attribute> attributes;
/**
* Constructs a <code>TrustedCertificateEntry</code> with a
......@@ -602,6 +730,32 @@ public class KeyStore {
throw new NullPointerException("invalid null input");
}
this.cert = trustedCert;
this.attributes = Collections.<Attribute>emptySet();
}
/**
* Constructs a {@code TrustedCertificateEntry} with a
* trusted {@code Certificate} and associated entry attributes.
*
* <p> The specified {@code attributes} is cloned before it is stored
* in the new {@code TrustedCertificateEntry} object.
*
* @param trustedCert the trusted {@code Certificate}
* @param attributes the attributes
*
* @exception NullPointerException if {@code trustedCert} or
* {@code attributes} is {@code null}
*
* @since 1.8
*/
public TrustedCertificateEntry(Certificate trustedCert,
Set<Attribute> attributes) {
if (trustedCert == null || attributes == null) {
throw new NullPointerException("invalid null input");
}
this.cert = trustedCert;
this.attributes =
Collections.unmodifiableSet(new HashSet<>(attributes));
}
/**
......@@ -613,6 +767,19 @@ public class KeyStore {
return cert;
}
/**
* Retrieves the attributes associated with an entry.
* <p>
*
* @return an unmodifiable {@code Set} of attributes, possibly empty
*
* @since 1.8
*/
@Override
public Set<Attribute> getAttributes() {
return attributes;
}
/**
* Returns a string representation of this TrustedCertificateEntry.
* @return a string representation of this TrustedCertificateEntry.
......
/*
* Copyright (c) 2013, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.security;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.regex.Pattern;
import sun.security.util.*;
/**
* An attribute associated with a PKCS12 keystore entry.
* The attribute name is an ASN.1 Object Identifier and the attribute
* value is a set of ASN.1 types.
*
* @since 1.8
*/
public final class PKCS12Attribute implements KeyStore.Entry.Attribute {
private static final Pattern COLON_SEPARATED_HEX_PAIRS =
Pattern.compile("^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+$");
private String name;
private String value;
private byte[] encoded;
private int hashValue = -1;
/**
* Constructs a PKCS12 attribute from its name and value.
* The name is an ASN.1 Object Identifier represented as a list of
* dot-separated integers.
* A string value is represented as the string itself.
* A binary value is represented as a string of colon-separated
* pairs of hexadecimal digits.
* Multi-valued attributes are represented as a comma-separated
* list of values, enclosed in square brackets. See
* {@link Arrays.toString}.
* <p>
* A string value will be DER-encoded as an ASN.1 UTF8String and a
* binary value will be DER-encoded as an ASN.1 Octet String.
*
* @param name the attribute's identifier
* @param value the attribute's value
*
* @exception NullPointerException if {@code name} or {@code value}
* is {@code null}
* @exception IllegalArgumentException if {@code name} or
* {@code value} is incorrectly formatted
*/
public PKCS12Attribute(String name, String value) {
if (name == null || value == null) {
throw new NullPointerException();
}
// Validate name
ObjectIdentifier type;
try {
type = new ObjectIdentifier(name);
} catch (IOException e) {
throw new IllegalArgumentException("Incorrect format: name", e);
}
this.name = name;
// Validate value
int length = value.length();
String[] values;
if (value.charAt(0) == '[' && value.charAt(length - 1) == ']') {
values = value.substring(1, length - 1).split(", ");
} else {
values = new String[]{ value };
}
this.value = value;
try {
this.encoded = encode(type, values);
} catch (IOException e) {
throw new IllegalArgumentException("Incorrect format: value", e);
}
}
/**
* Constructs a PKCS12 attribute from its ASN.1 DER encoding.
* The DER encoding is specified by the following ASN.1 definition:
* <pre>
*
* Attribute ::= SEQUENCE {
* type AttributeType,
* values SET OF AttributeValue
* }
* AttributeType ::= OBJECT IDENTIFIER
* AttributeValue ::= ANY defined by type
*
* </pre>
*
* @param encoded the attribute's ASN.1 DER encoding. It is cloned
* to prevent subsequent modificaion.
*
* @exception NullPointerException if {@code encoded} is
* {@code null}
* @exception IllegalArgumentException if {@code encoded} is
* incorrectly formatted
*/
public PKCS12Attribute(byte[] encoded) {
if (encoded == null) {
throw new NullPointerException();
}
this.encoded = encoded.clone();
try {
parse(encoded);
} catch (IOException e) {
throw new IllegalArgumentException("Incorrect format: encoded", e);
}
}
/**
* Returns the attribute's ASN.1 Object Identifier represented as a
* list of dot-separated integers.
*
* @return the attribute's identifier
*/
@Override
public String getName() {
return name;
}
/**
* Returns the attribute's ASN.1 DER-encoded value as a string.
* An ASN.1 DER-encoded value is returned in one of the following
* {@code String} formats:
* <ul>
* <li> the DER encoding of a basic ASN.1 type that has a natural
* string representation is returned as the string itself.
* Such types are currently limited to BOOLEAN, INTEGER,
* OBJECT IDENTIFIER, UTCTime, GeneralizedTime and the
* following six ASN.1 string types: UTF8String,
* PrintableString, T61String, IA5String, BMPString and
* GeneralString.
* <li> the DER encoding of any other ASN.1 type is not decoded but
* returned as a binary string of colon-separated pairs of
* hexadecimal digits.
* </ul>
* Multi-valued attributes are represented as a comma-separated
* list of values, enclosed in square brackets. See
* {@link Arrays.toString}.
*
* @return the attribute value's string encoding
*/
@Override
public String getValue() {
return value;
}
/**
* Returns the attribute's ASN.1 DER encoding.
*
* @return a clone of the attribute's DER encoding
*/
public byte[] getEncoded() {
return encoded.clone();
}
/**
* Compares this {@code PKCS12Attribute} and a specified object for
* equality.
*
* @param obj the comparison object
*
* @return true if {@code obj} is a {@code PKCS12Attribute} and
* their DER encodings are equal.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PKCS12Attribute)) {
return false;
}
return Arrays.equals(encoded, ((PKCS12Attribute) obj).getEncoded());
}
/**
* Returns the hashcode for this {@code PKCS12Attribute}.
* The hash code is computed from its DER encoding.
*
* @return the hash code
*/
@Override
public int hashCode() {
if (hashValue == -1) {
Arrays.hashCode(encoded);
}
return hashValue;
}
/**
* Returns a string representation of this {@code PKCS12Attribute}.
*
* @return a name/value pair separated by an 'equals' symbol
*/
@Override
public String toString() {
return (name + "=" + value);
}
private byte[] encode(ObjectIdentifier type, String[] values)
throws IOException {
DerOutputStream attribute = new DerOutputStream();
attribute.putOID(type);
DerOutputStream attrContent = new DerOutputStream();
for (String value : values) {
if (COLON_SEPARATED_HEX_PAIRS.matcher(value).matches()) {
byte[] bytes =
new BigInteger(value.replace(":", ""), 16).toByteArray();
if (bytes[0] == 0) {
bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
}
attrContent.putOctetString(bytes);
} else {
attrContent.putUTF8String(value);
}
}
attribute.write(DerValue.tag_Set, attrContent);
DerOutputStream attributeValue = new DerOutputStream();
attributeValue.write(DerValue.tag_Sequence, attribute);
return attributeValue.toByteArray();
}
private void parse(byte[] encoded) throws IOException {
DerInputStream attributeValue = new DerInputStream(encoded);
DerValue[] attrSeq = attributeValue.getSequence(2);
ObjectIdentifier type = attrSeq[0].getOID();
DerInputStream attrContent =
new DerInputStream(attrSeq[1].toByteArray());
DerValue[] attrValueSet = attrContent.getSet(1);
String[] values = new String[attrValueSet.length];
String printableString;
for (int i = 0; i < attrValueSet.length; i++) {
if (attrValueSet[i].tag == DerValue.tag_OctetString) {
values[i] = Debug.toString(attrValueSet[i].getOctetString());
} else if ((printableString = attrValueSet[i].getAsString())
!= null) {
values[i] = printableString;
} else if (attrValueSet[i].tag == DerValue.tag_ObjectId) {
values[i] = attrValueSet[i].getOID().toString();
} else if (attrValueSet[i].tag == DerValue.tag_GeneralizedTime) {
values[i] = attrValueSet[i].getGeneralizedTime().toString();
} else if (attrValueSet[i].tag == DerValue.tag_UtcTime) {
values[i] = attrValueSet[i].getUTCTime().toString();
} else if (attrValueSet[i].tag == DerValue.tag_Integer) {
values[i] = attrValueSet[i].getBigInteger().toString();
} else if (attrValueSet[i].tag == DerValue.tag_Boolean) {
values[i] = String.valueOf(attrValueSet[i].getBoolean());
} else {
values[i] = Debug.toString(attrValueSet[i].getDataBytes());
}
}
this.name = type.toString();
this.value = values.length == 1 ? values[0] : Arrays.toString(values);
}
}
......@@ -30,27 +30,30 @@ import java.security.AccessController;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PrivilegedAction;
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.Security;
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;
......@@ -107,11 +110,12 @@ import sun.security.pkcs.EncryptedPrivateKeyInfo;
* OpenSSL PKCS#12 code. All. All.
* ---------------------------------------------------------------------
*
* NOTE: Currently PKCS12 KeyStore does not support TrustedCertEntries.
* NOTE: PKCS12 KeyStore supports PrivateKeyEntry and TrustedCertficateEntry.
* PKCS#12 is mainly used to deliver private keys with their associated
* certificate chain and aliases. In a PKCS12 keystore, entries are
* identified by the alias, and a localKeyId is required to match the
* private key with the certificate.
* private key with the certificate. Trusted certificate entries are identified
* by the presence of an trustedKeyUsage attribute.
*
* @author Seema Malkani
* @author Jeff Nisewanger
......@@ -136,6 +140,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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};
......@@ -147,15 +152,26 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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 static final int iterationCount = 1024;
......@@ -166,6 +182,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
// in pkcs12 with one private key entry and associated cert-chain
private int privateKeyCount = 0;
// secret key count
private int secretKeyCount = 0;
// certificate count
private int certificateCount = 0;
// the source of randomness
private SecureRandom random;
......@@ -173,6 +195,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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);
......@@ -181,38 +204,67 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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
}
}
// Private keys and their supporting certificate chains
private static class KeyEntry {
// A keystore entry and associated attributes
private static class Entry {
Date date; // the creation date of this entry
String alias;
byte[] keyId;
Set<KeyStore.Entry.Attribute> attributes;
}
// A key entry
private static class KeyEntry extends Entry {
}
// A private key entry and its supporting certificate chain
private static class PrivateKeyEntry extends KeyEntry {
byte[] protectedPrivKey;
Certificate chain[];
byte[] keyId;
String alias;
};
// A certificate with its PKCS #9 attributes
private static class CertEntry {
// A secret key
private static class SecretKeyEntry extends KeyEntry {
byte[] protectedSecretKey;
};
// A certificate entry
private static class CertEntry extends Entry {
final X509Certificate cert;
final byte[] keyId;
final String alias;
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 keys and certificates are stored in a hashtable.
* Hash entries are keyed by alias names.
* Private keys and certificates are stored in a map.
* Map entries are keyed by alias names.
*/
private Hashtable<String, KeyEntry> entries =
new Hashtable<String, KeyEntry>();
private Map<String, Entry> entries =
Collections.synchronizedMap(new LinkedHashMap<String, Entry>());
private ArrayList<KeyEntry> keyList = new ArrayList<KeyEntry>();
private LinkedHashMap<X500Principal, X509Certificate> certsMap =
......@@ -237,15 +289,22 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
public Key engineGetKey(String alias, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException
{
KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
Key key = null;
if (entry == null) {
if (entry == null || (!(entry instanceof KeyEntry))) {
return null;
}
// get the encoded private key
byte[] encrBytes = entry.protectedPrivKey;
// get the encoded private key or secret key
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;
......@@ -271,14 +330,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
}
try {
byte[] privateKeyInfo;
byte[] keyInfo;
while (true) {
try {
// Use JCE
SecretKey skey = getPBEKey(password);
Cipher cipher = Cipher.getInstance(algOid.toString());
cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
privateKeyInfo = cipher.doFinal(encryptedKey);
keyInfo = cipher.doFinal(encryptedKey);
break;
} catch (Exception e) {
if (password.length == 0) {
......@@ -291,27 +350,52 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
}
}
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(privateKeyInfo);
/*
* Parse the key algorithm and then use a JCA key factory
* to create the private key.
* to re-create the key.
*/
DerValue val = new DerValue(privateKeyInfo);
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 algName = algId.getName();
String keyAlgo = algId.getName();
KeyFactory kfac = KeyFactory.getInstance(algName);
key = kfac.generatePrivate(kspec);
// decode private key
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 at alias '" +
alias + "'");
}
if (debug != null) {
debug.println("Retrieved a protected private key (" +
key.getClass().getName() + ") at alias '" + alias +
"'");
}
// decode secret key
} else {
SecretKeyFactory sKeyFactory =
SecretKeyFactory.getInstance(keyAlgo);
byte[] keyBytes = in.getOctetString();
SecretKeySpec secretKeySpec =
new SecretKeySpec(keyBytes, keyAlgo);
// Special handling required for PBE: needs a PBEKeySpec
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: " +
......@@ -334,19 +418,19 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* <i>key entry</i> without a certificate chain).
*/
public Certificate[] engineGetCertificateChain(String alias) {
KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
if (entry.chain == null) {
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 " +
entry.chain.length +
((PrivateKeyEntry) entry).chain.length +
"-certificate chain at alias '" + alias + "'");
}
return entry.chain.clone();
return ((PrivateKeyEntry) entry).chain.clone();
}
} else {
return null;
......@@ -369,9 +453,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* does not contain a certificate.
*/
public Certificate engineGetCertificate(String alias) {
KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
if (entry.chain == null) {
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 {
......@@ -380,8 +483,9 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
"'");
}
return entry.chain[0];
return ((PrivateKeyEntry) entry).chain[0];
}
} else {
return null;
}
......@@ -396,7 +500,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* not exist
*/
public Date engineGetCreationDate(String alias) {
KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
return new Date(entry.date.getTime());
} else {
......@@ -434,7 +538,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
new KeyStore.PasswordProtection(password);
try {
setKeyEntry(alias, key, passwordProtection, chain);
setKeyEntry(alias, key, passwordProtection, chain, null);
} finally {
try {
......@@ -446,57 +550,94 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
}
/*
* Sets a key entry
* Sets a key entry (with attributes, when present)
*/
private void setKeyEntry(String alias, Key key,
KeyStore.PasswordProtection passwordProtection, Certificate[] chain)
KeyStore.PasswordProtection passwordProtection, Certificate[] chain,
Set<KeyStore.Entry.Attribute> attributes)
throws KeyStoreException
{
try {
KeyEntry entry = new KeyEntry();
entry.date = new Date();
Entry entry;
if (key instanceof PrivateKey) {
PrivateKeyEntry keyEntry = new PrivateKeyEntry();
keyEntry.date = new Date();
if ((key.getFormat().equals("PKCS#8")) ||
(key.getFormat().equals("PKCS8"))) {
// Encrypt the private key
if (debug != null) {
debug.println("Setting a protected private key at " +
"alias '" + alias + "'");
}
debug.println("Setting a protected private key (" +
key.getClass().getName() + ") at alias '" + alias +
"'");
}
entry.protectedPrivKey =
// Encrypt the private key
keyEntry.protectedPrivKey =
encryptPrivateKey(key.getEncoded(), passwordProtection);
} else {
throw new KeyStoreException("Private key is not encoded" +
"as PKCS#8");
}
} else {
throw new KeyStoreException("Key is not a PrivateKey");
}
// clone the chain
if (chain != null) {
// validate cert-chain
if ((chain.length > 1) && (!validateChain(chain)))
throw new KeyStoreException("Certificate chain is " +
"not validate");
entry.chain = chain.clone();
// clone the chain
if (chain != null) {
// validate cert-chain
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();
// Encode secret key in a PKCS#8
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);
// Encrypt the secret key (using same PBE as for private keys)
keyEntry.protectedSecretKey =
encryptPrivateKey(pkcs8.toByteArray(), passwordProtection);
if (debug != null) {
debug.println("Setting a " + chain.length +
"-certificate chain at alias '" + alias + "'");
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);
}
// set the keyId to current date
entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8");
// set the alias
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);
......@@ -530,7 +671,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
Certificate[] chain)
throws KeyStoreException
{
// key must be encoded as EncryptedPrivateKeyInfo
// Private key must be encoded as EncryptedPrivateKeyInfo
// as defined in PKCS#8
try {
new EncryptedPrivateKeyInfo(key);
......@@ -539,11 +680,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
+ " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe);
}
KeyEntry entry = new KeyEntry();
PrivateKeyEntry entry = new PrivateKeyEntry();
entry.date = new Date();
if (debug != null) {
debug.println("Setting a protected key at alias '" + alias + "'");
debug.println("Setting a protected private key at alias '" +
alias + "'");
}
try {
......@@ -557,7 +699,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
entry.protectedPrivKey = key.clone();
if (chain != null) {
entry.chain = chain.clone();
entry.chain = chain.clone();
certificateCount += chain.length;
if (debug != null) {
debug.println("Setting a " + entry.chain.length +
......@@ -566,6 +709,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
}
// add the entry
privateKeyCount++;
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
}
......@@ -644,6 +788,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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);
......@@ -695,7 +840,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
new PrivilegedAction<String>() {
public String run() {
String prop =
Security.getProperty(
Security.getProperty
KEY_PROTECTION_ALGORITHM[0]);
if (prop == null) {
prop = Security.getProperty(
......@@ -762,17 +907,36 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* @param cert the certificate
*
* @exception KeyStoreException if the given alias already exists and does
* identify a <i>key entry</i>, or on an attempt to create a
* <i>trusted cert entry</i> which is currently not supported.
* not identify a <i>trusted certificate entry</i>, or this operation fails
* for some other reason.
*/
public synchronized void engineSetCertificateEntry(String alias,
Certificate cert) throws KeyStoreException
{
KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
setCertEntry(alias, cert, null);
}
/*
* Sets a trusted cert entry (with attributes, when present)
*/
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");
} else
throw new KeyStoreException("TrustedCertEntry not supported");
}
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 +
"'");
}
}
/**
......@@ -789,6 +953,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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));
}
......@@ -798,7 +974,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* @return enumeration of the alias names
*/
public Enumeration<String> engineAliases() {
return entries.keys();
return Collections.enumeration(entries.keySet());
}
/**
......@@ -829,8 +1005,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* <i>key entry</i>, false otherwise.
*/
public boolean engineIsKeyEntry(String alias) {
KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null && entry instanceof KeyEntry) {
return true;
} else {
return false;
......@@ -845,8 +1021,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* <i>trusted certificate entry</i>, false otherwise.
*/
public boolean engineIsCertificateEntry(String alias) {
// TrustedCertEntry is not supported
return false;
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null && entry instanceof CertEntry &&
((CertEntry) entry).trustedKeyUsage != null) {
return true;
} else {
return false;
}
}
/**
......@@ -868,11 +1049,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
public String engineGetCertificateAlias(Certificate cert) {
Certificate certElem = null;
for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
KeyEntry entry = entries.get(alias);
if (entry.chain != null) {
certElem = entry.chain[0];
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;
......@@ -918,26 +1106,32 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
DerOutputStream authSafeContentInfo = new DerOutputStream();
// -- create safeContent Data ContentInfo
if (debug != null) {
debug.println("Storing " + privateKeyCount +
" protected key(s) in a PKCS#7 data content-type");
}
if (privateKeyCount > 0 || secretKeyCount > 0) {
byte[] safeContentData = createSafeContent();
ContentInfo dataContentInfo = new ContentInfo(safeContentData);
dataContentInfo.encode(authSafeContentInfo);
if (debug != null) {
debug.println("Storing " + privateKeyCount +
" protected key(s) in a PKCS#7 data content-type");
}
// -- create EncryptedContentInfo
if (debug != null) {
debug.println("Storing certificate(s) in a PKCS#7 encryptedData " +
"content-type");
byte[] safeContentData = createSafeContent();
ContentInfo dataContentInfo = new ContentInfo(safeContentData);
dataContentInfo.encode(authSafeContentInfo);
}
byte[] encrData = createEncryptedData(password);
ContentInfo encrContentInfo =
// -- create EncryptedContentInfo
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);
encrContentInfo.encode(authSafeContentInfo);
}
// wrap as SequenceOf ContentInfos
DerOutputStream cInfo = new DerOutputStream();
......@@ -962,6 +1156,207 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
stream.flush();
}
/**
* Gets a <code>KeyStore.Entry</code> for the specified alias
* with the specified protection parameter.
*
* @param alias get the <code>KeyStore.Entry</code> for this alias
* @param protParam the <code>ProtectionParameter</code>
* used to protect the <code>Entry</code>,
* which may be <code>null</code>
*
* @return the <code>KeyStore.Entry</code> for the specified alias,
* or <code>null</code> if there is no such entry
*
* @exception KeyStoreException if the operation failed
* @exception NoSuchAlgorithmException if the algorithm for recovering the
* entry cannot be found
* @exception UnrecoverableEntryException if the specified
* <code>protParam</code> were insufficient or invalid
* @exception UnrecoverableKeyException if the entry is a
* <code>PrivateKeyEntry</code> or <code>SecretKeyEntry</code>
* and the specified <code>protParam</code> does not contain
* the information needed to recover the key (e.g. wrong password)
*
* @since 1.5
*/
@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();
}
/**
* Saves a <code>KeyStore.Entry</code> under the specified alias.
* The specified protection parameter is used to protect the
* <code>Entry</code>.
*
* <p> If an entry already exists for the specified alias,
* it is overridden.
*
* @param alias save the <code>KeyStore.Entry</code> under this alias
* @param entry the <code>Entry</code> to save
* @param protParam the <code>ProtectionParameter</code>
* used to protect the <code>Entry</code>,
* which may be <code>null</code>
*
* @exception KeyStoreException if this operation fails
*
* @since 1.5
*/
@Override
public synchronized void engineSetEntry(String alias, KeyStore.Entry entry,
KeyStore.ProtectionParameter protParam) throws KeyStoreException {
// get password
if (protParam != null &&
!(protParam instanceof KeyStore.PasswordProtection)) {
throw new KeyStoreException("unsupported protection parameter");
}
KeyStore.PasswordProtection pProtect = null;
if (protParam != null) {
pProtect = (KeyStore.PasswordProtection)protParam;
}
// set entry
if (entry instanceof KeyStore.TrustedCertificateEntry) {
if (protParam != null && pProtect.getPassword() != null) {
// pre-1.5 style setCertificateEntry did not allow password
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) {
// pre-1.5 style setKeyEntry required password
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) {
// pre-1.5 style setKeyEntry required password
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());
}
/*
* Assemble the entry attributes
*/
private Set<KeyStore.Entry.Attribute> getAttributes(Entry entry) {
if (entry.attributes == null) {
entry.attributes = new HashSet<>();
}
// friendlyName
entry.attributes.add(new PKCS12Attribute(
PKCS9FriendlyName_OID.toString(), entry.alias));
// localKeyID
byte[] keyIdValue = entry.keyId;
if (keyIdValue != null) {
entry.attributes.add(new PKCS12Attribute(
PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue)));
}
// trustedKeyUsage
if (entry instanceof CertEntry) {
ObjectIdentifier[] trustedKeyUsageValue =
((CertEntry) entry).trustedKeyUsage;
if (trustedKeyUsageValue != null) {
if (trustedKeyUsageValue.length == 1) { // omit brackets
entry.attributes.add(new PKCS12Attribute(
TrustedKeyUsage_OID.toString(),
trustedKeyUsageValue[0].toString()));
} else { // multi-valued
entry.attributes.add(new PKCS12Attribute(
TrustedKeyUsage_OID.toString(),
Arrays.toString(trustedKeyUsageValue)));
}
}
}
return entry.attributes;
}
/*
* Generate Hash.
*/
......@@ -1036,11 +1431,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
/*
* Create PKCS#12 Attributes, friendlyName and localKeyId.
* Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage.
*
* Although attributes are optional, they could be required.
* For e.g. localKeyId attribute is required to match the
* private key with the associated end-entity certificate.
* The trustedKeyUsage attribute is used to denote a trusted certificate.
*
* PKCS8ShroudedKeyBags include unique localKeyID and friendlyName.
* CertBags may or may not include attributes depending on the type
......@@ -1062,20 +1458,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* friendlyName unique same/ same/ unique
* unique unique/
* null
* trustedKeyUsage - - - true
*
* Note: OpenSSL adds friendlyName for end-entity cert only, and
* removes the localKeyID and friendlyName for CA certs.
* If the CertBag did not have a friendlyName, most vendors will
* add it, and assign it to the DN of the cert.
*/
private byte[] getBagAttributes(String alias, byte[] keyId)
throws IOException {
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;
// return null if both attributes are null
if ((alias == null) && (keyId == null)) {
// return null if all three attributes are null
if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) {
return null;
}
......@@ -1106,6 +1510,20 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
localKeyID = bagAttrValue2.toByteArray();
}
// Encode the trustedKeyUsage oid.
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);
......@@ -1113,11 +1531,20 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
if (localKeyID != null) {
attrs.write(localKeyID);
}
if (trustedKeyUsage != null) {
attrs.write(trustedKeyUsage);
}
if (attributes != null) {
for (KeyStore.Entry.Attribute attribute : attributes) {
attrs.write(((PKCS12Attribute) attribute).getEncoded());
}
}
bagAttrs.write(DerValue.tag_Set, attrs);
return bagAttrs.toByteArray();
}
/*
* Create EncryptedData content type, that contains EncryptedContentInfo.
* Includes certificates in individual SafeBags of type CertBag.
......@@ -1128,17 +1555,26 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
throws CertificateException, IOException
{
DerOutputStream out = new DerOutputStream();
for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
KeyEntry entry = entries.get(alias);
Entry entry = entries.get(alias);
// certificate chain
int chainLen;
if (entry.chain == null) {
chainLen = 0;
} else {
chainLen = entry.chain.length;
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++) {
......@@ -1152,7 +1588,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
// write encoded certs in a context-specific tag
DerOutputStream certValue = new DerOutputStream();
X509Certificate cert = (X509Certificate)entry.chain[i];
X509Certificate cert = (X509Certificate) certs[i];
certValue.putOctetString(cert.getEncoded());
certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), certValue);
......@@ -1175,7 +1611,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
byte[] bagAttrs = null;
if (i == 0) {
// Only End-Entity Cert should have a localKeyId.
bagAttrs = getBagAttributes(entry.alias, entry.keyId);
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 {
// Trusted root CA certs and Intermediate CA certs do not
// need to have a localKeyId, and hence localKeyId is null
......@@ -1184,7 +1631,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
// certificate chain to have unique or null localKeyID.
// However, IE/OpenSSL do not impose this restriction.
bagAttrs = getBagAttributes(
cert.getSubjectX500Principal().getName(), null);
cert.getSubjectX500Principal().getName(), null,
entry.attributes);
}
if (bagAttrs != null) {
safeBag.write(bagAttrs);
......@@ -1214,6 +1662,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
/*
* Create SafeContent Data content type.
* Includes encrypted secret key in a SafeBag of type SecretBag.
* Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag.
* Each PKCS8ShroudedKeyBag includes pkcs12 attributes
* (see comments in getBagAttributes)
......@@ -1222,33 +1671,74 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
throws CertificateException, IOException {
DerOutputStream out = new DerOutputStream();
for (Enumeration<String> e = entries.keys(); e.hasMoreElements(); ) {
for (Enumeration<String> e = engineAliases(); e.hasMoreElements(); ) {
String alias = e.nextElement();
KeyEntry entry = entries.get(alias);
// Create SafeBag of type pkcs8ShroudedKeyBag
Entry entry = entries.get(alias);
if (entry == null || (!(entry instanceof KeyEntry))) {
continue;
}
DerOutputStream safeBag = new DerOutputStream();
safeBag.putOID(PKCS8ShroudedKeyBag_OID);
KeyEntry keyEntry = (KeyEntry) entry;
// get the encrypted private key
byte[] encrBytes = entry.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());
}
// DER encode the private key
if (keyEntry instanceof PrivateKeyEntry) {
// Create SafeBag of type pkcs8ShroudedKeyBag
safeBag.putOID(PKCS8ShroudedKeyBag_OID);
// get the encrypted private key
byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey;
EncryptedPrivateKeyInfo encrInfo = null;
try {
encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
// Wrap the EncryptedPrivateKeyInfo in a context-specific tag.
DerOutputStream bagValue = new DerOutputStream();
bagValue.write(encrInfo.getEncoded());
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
} catch (IOException ioe) {
throw new IOException("Private key not stored as "
+ "PKCS#8 EncryptedPrivateKeyInfo"
+ ioe.getMessage());
}
// Wrap the EncryptedPrivateKeyInfo in a context-specific tag.
DerOutputStream bagValue = new DerOutputStream();
bagValue.write(encrInfo.getEncoded());
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), bagValue);
// DER encode the secret key
} else if (keyEntry instanceof SecretKeyEntry) {
// Create SafeBag of type SecretBag
safeBag.putOID(SecretBag_OID);
// Create a SecretBag
DerOutputStream secretBag = new DerOutputStream();
secretBag.putOID(PKCS8ShroudedKeyBag_OID);
// Write secret key in a context-specific tag
DerOutputStream secretKeyValue = new DerOutputStream();
secretKeyValue.putOctetString(
((SecretKeyEntry) keyEntry).protectedSecretKey);
secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), secretKeyValue);
// Wrap SecretBag in a Sequence
DerOutputStream secretBagSeq = new DerOutputStream();
secretBagSeq.write(DerValue.tag_Sequence, secretBag);
byte[] secretBagValue = secretBagSeq.toByteArray();
// Wrap the secret bag in a context-specific tag.
DerOutputStream bagValue = new DerOutputStream();
bagValue.write(secretBagValue);
// Write SafeBag value
safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0), bagValue);
} else {
continue; // skip this entry
}
// write SafeBag Attributes
byte[] bagAttrs = getBagAttributes(alias, entry.keyId);
byte[] bagAttrs =
getBagAttributes(alias, entry.keyId, entry.attributes);
safeBag.write(bagAttrs);
// wrap as Sequence
......@@ -1377,8 +1867,10 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
DerValue[] safeContentsArray = as.getSequence(2);
int count = safeContentsArray.length;
// reset the count at the start
// reset the counters at the start
privateKeyCount = 0;
secretKeyCount = 0;
certificateCount = 0;
/*
* Spin over the ContentInfos.
......@@ -1445,7 +1937,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
continue;
}
throw new IOException(
"failed to decrypt safe contents entry: " + e, e);
"failed to decrypt safe contents entry: " + e, e);
}
}
} else {
......@@ -1493,9 +1985,10 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
/*
* Match up private keys with certificate chains.
*/
KeyEntry[] list = keyList.toArray(new KeyEntry[keyList.size()]);
PrivateKeyEntry[] list =
keyList.toArray(new PrivateKeyEntry[keyList.size()]);
for (int m = 0; m < list.length; m++) {
KeyEntry entry = list[m];
PrivateKeyEntry entry = list[m];
if (entry.keyId != null) {
ArrayList<X509Certificate> chain =
new ArrayList<X509Certificate>();
......@@ -1513,6 +2006,22 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
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();
......@@ -1523,7 +2032,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
* @param entry the KeyEntry to match
* @return a certificate, null if not found
*/
private X509Certificate findMatchedCertificate(KeyEntry entry) {
private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) {
CertEntry keyIdMatch = null;
CertEntry aliasMatch = null;
for (CertEntry ce: certEntries) {
......@@ -1567,7 +2076,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
}
bagValue = bagValue.data.getDerValue();
if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) {
KeyEntry kEntry = new KeyEntry();
PrivateKeyEntry kEntry = new PrivateKeyEntry();
kEntry.protectedPrivKey = bagValue.toByteArray();
bagItem = kEntry;
privateKeyCount++;
......@@ -1585,6 +2094,20 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
cert = (X509Certificate)cf.generateCertificate
(new ByteArrayInputStream(certValue.getOctetString()));
bagItem = cert;
certificateCount++;
} else if (bagId.equals((Object)SecretBag_OID)) {
DerInputStream ss = new DerInputStream(bagValue.toByteArray());
DerValue[] secretValues = ss.getSequence(2);
ObjectIdentifier secretId = secretValues[0].getOID();
if (!secretValues[1].isContextSpecific((byte)0)) {
throw new IOException(
"unsupported PKCS12 secret value type "
+ secretValues[1].tag);
}
DerValue secretValue = secretValues[1].data.getDerValue();
SecretKeyEntry kEntry = new SecretKeyEntry();
kEntry.protectedSecretKey = secretValue.getOctetString();
bagItem = kEntry;
} else {
if (debug != null) {
......@@ -1594,7 +2117,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
DerValue[] attrSet;
try {
attrSet = sbi.getSet(2);
attrSet = sbi.getSet(3);
} catch (IOException e) {
// entry does not have attributes
// Note: CA certs can have no attributes
......@@ -1604,11 +2127,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
String alias = null;
byte[] keyId = null;
ObjectIdentifier[] trustedKeyUsage = null;
Set<PKCS12Attribute> attributes = new HashSet<>();
if (attrSet != null) {
for (int j = 0; j < attrSet.length; j++) {
DerInputStream as =
new DerInputStream(attrSet[j].toByteArray());
byte[] encoded = attrSet[j].toByteArray();
DerInputStream as = new DerInputStream(encoded);
DerValue[] attrSeq = as.getSequence(2);
ObjectIdentifier attrId = attrSeq[0].getOID();
DerInputStream vs =
......@@ -1624,12 +2149,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
alias = valSet[0].getBMPString();
} else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) {
keyId = valSet[0].getOctetString();
} else {
if (debug != null) {
debug.println("Unsupported PKCS12 bag attribute: " +
attrId);
} else if
(attrId.equals((Object)TrustedKeyUsage_OID)) {
trustedKeyUsage = new ObjectIdentifier[valSet.length];
for (int k = 0; k < valSet.length; k++) {
trustedKeyUsage[k] = valSet[k].getOID();
}
} else {
attributes.add(new PKCS12Attribute(encoded));
}
}
}
......@@ -1645,16 +2172,19 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
*/
if (bagItem instanceof KeyEntry) {
KeyEntry entry = (KeyEntry)bagItem;
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;
}
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
......@@ -1672,11 +2202,16 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
date = new Date();
}
entry.date = date;
keyList.add(entry);
if (alias == null)
if (bagItem instanceof PrivateKeyEntry) {
keyList.add((PrivateKeyEntry) entry);
}
if (alias == null) {
alias = getUnfriendlyName();
}
entry.alias = alias;
entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
} else if (bagItem instanceof X509Certificate) {
X509Certificate cert = (X509Certificate)bagItem;
// Insert a localKeyID for the corresponding cert
......@@ -1689,7 +2224,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi {
keyId = "01".getBytes("UTF8");
}
}
certEntries.add(new CertEntry(cert, keyId, alias));
if (alias == null) {
alias = getUnfriendlyName();
}
// Trusted certificate
if (trustedKeyUsage != null) {
CertEntry certEntry =
new CertEntry(cert, keyId, alias, trustedKeyUsage,
attributes);
entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry);
} else {
certEntries.add(new CertEntry(cert, keyId, alias));
}
X500Principal subjectDN = cert.getSubjectX500Principal();
if (subjectDN != null) {
if (!certsMap.containsKey(subjectDN)) {
......
/*
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2013, 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
......@@ -502,6 +502,11 @@ public class AlgorithmId implements Serializable, DerEncoder {
return AlgorithmId.ECDH_oid;
}
// Secret key algorithms
if (name.equalsIgnoreCase("AES")) {
return AlgorithmId.AES_oid;
}
// Common signature types
if (name.equalsIgnoreCase("MD5withRSA")
|| name.equalsIgnoreCase("MD5/RSA")) {
......@@ -660,6 +665,12 @@ public class AlgorithmId implements Serializable, DerEncoder {
public static final ObjectIdentifier RSA_oid;
public static final ObjectIdentifier RSAEncryption_oid;
/*
* COMMON SECRET KEY TYPES
*/
public static final ObjectIdentifier AES_oid =
oid(2, 16, 840, 1, 101, 3, 4, 1);
/*
* COMMON SIGNATURE ALGORITHMS
*/
......@@ -893,6 +904,8 @@ public class AlgorithmId implements Serializable, DerEncoder {
nameTable.put(EC_oid, "EC");
nameTable.put(ECDH_oid, "ECDH");
nameTable.put(AES_oid, "AES");
nameTable.put(sha1WithECDSA_oid, "SHA1withECDSA");
nameTable.put(sha224WithECDSA_oid, "SHA224withECDSA");
nameTable.put(sha256WithECDSA_oid, "SHA256withECDSA");
......
/*
* Copyright (c) 2013, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8005408
* @summary KeyStore API enhancements
*/
import java.io.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.spec.InvalidKeySpecException;
// Store a password in a keystore and retrieve it again.
public class StorePasswordTest {
private final static String DIR = System.getProperty("test.src", ".");
private static final char[] PASSWORD = "passphrase".toCharArray();
private static final String KEYSTORE = "pwdstore.p12";
private static final String ALIAS = "my password";
private static final String USER_PASSWORD = "hello1";
public static void main(String[] args) throws Exception {
new File(KEYSTORE).delete();
try {
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(null, null);
// Set entry
keystore.setEntry(ALIAS,
new KeyStore.SecretKeyEntry(convertPassword(USER_PASSWORD)),
new KeyStore.PasswordProtection(PASSWORD));
System.out.println("Storing keystore to: " + KEYSTORE);
keystore.store(new FileOutputStream(KEYSTORE), PASSWORD);
System.out.println("Loading keystore from: " + KEYSTORE);
keystore.load(new FileInputStream(KEYSTORE), PASSWORD);
System.out.println("Loaded keystore with " + keystore.size() +
" entries");
KeyStore.Entry entry = keystore.getEntry(ALIAS,
new KeyStore.PasswordProtection(PASSWORD));
System.out.println("Retrieved entry: " + entry);
SecretKey key = (SecretKey) keystore.getKey(ALIAS, PASSWORD);
SecretKeyFactory factory =
SecretKeyFactory.getInstance(key.getAlgorithm());
PBEKeySpec keySpec =
(PBEKeySpec) factory.getKeySpec(key, PBEKeySpec.class);
char[] pwd = keySpec.getPassword();
System.out.println("Recovered credential: " + new String(pwd));
if (!Arrays.equals(USER_PASSWORD.toCharArray(), pwd)) {
throw new Exception("Failed to recover the stored password");
}
} finally {
new File(KEYSTORE).delete();
}
}
private static SecretKey convertPassword(String password)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");
return factory.generateSecret(new PBEKeySpec(password.toCharArray()));
}
}
/*
* Copyright (c) 2013, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8005408
* @summary KeyStore API enhancements
*/
import java.io.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
// Store a secret key in a keystore and retrieve it again.
public class StoreSecretKeyTest {
private final static String DIR = System.getProperty("test.src", ".");
private static final char[] PASSWORD = "passphrase".toCharArray();
private static final String KEYSTORE = "keystore.p12";
private static final String ALIAS = "my secret key";
public static void main(String[] args) throws Exception {
new File(KEYSTORE).delete();
try {
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(null, null);
// Set entry
keystore.setEntry(ALIAS,
new KeyStore.SecretKeyEntry(generateSecretKey("AES", 128)),
new KeyStore.PasswordProtection(PASSWORD));
System.out.println("Storing keystore to: " + KEYSTORE);
keystore.store(new FileOutputStream(KEYSTORE), PASSWORD);
System.out.println("Loading keystore from: " + KEYSTORE);
keystore.load(new FileInputStream(KEYSTORE), PASSWORD);
System.out.println("Loaded keystore with " + keystore.size() +
" entries");
KeyStore.Entry entry = keystore.getEntry(ALIAS,
new KeyStore.PasswordProtection(PASSWORD));
System.out.println("Retrieved entry: " + entry);
if (entry instanceof KeyStore.SecretKeyEntry) {
System.out.println("Retrieved secret key entry: " +
entry);
} else {
throw new Exception("Not a secret key entry");
}
} finally {
new File(KEYSTORE).delete();
}
}
private static SecretKey generateSecretKey(String algorithm, int size)
throws NoSuchAlgorithmException {
KeyGenerator generator = KeyGenerator.getInstance(algorithm);
generator.init(size);
return generator.generateKey();
}
}
/*
* Copyright (c) 2013, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8005408
* @summary KeyStore API enhancements
*/
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import java.security.cert.Certificate;
import javax.crypto.*;
import javax.crypto.spec.*;
// Store a trusted certificate in a keystore and retrieve it again.
public class StoreTrustedCertTest {
private final static String DIR = System.getProperty("test.src", ".");
private static final char[] PASSWORD = "passphrase".toCharArray();
private static final String KEYSTORE = "truststore.p12";
private static final String CERT = DIR + "/trusted.pem";
private static final String ALIAS = "my trustedcert";
private static final String ALIAS2 = "my trustedcert with attributes";
public static void main(String[] args) throws Exception {
new File(KEYSTORE).delete();
try {
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(null, null);
Certificate cert = loadCertificate(CERT);
Set<KeyStore.Entry.Attribute> attributes = new HashSet<>();
attributes.add(new PKCS12Attribute("1.3.5.7.9", "that's odd"));
attributes.add(new PKCS12Attribute("2.4.6.8.10", "that's even"));
// Set trusted certificate entry
keystore.setEntry(ALIAS,
new KeyStore.TrustedCertificateEntry(cert), null);
// Set trusted certificate entry with attributes
keystore.setEntry(ALIAS2,
new KeyStore.TrustedCertificateEntry(cert, attributes), null);
System.out.println("Storing keystore to: " + KEYSTORE);
keystore.store(new FileOutputStream(KEYSTORE), PASSWORD);
System.out.println("Loading keystore from: " + KEYSTORE);
keystore.load(new FileInputStream(KEYSTORE), PASSWORD);
System.out.println("Loaded keystore with " + keystore.size() +
" entries");
KeyStore.Entry entry = keystore.getEntry(ALIAS, null);
if (entry instanceof KeyStore.TrustedCertificateEntry) {
System.out.println("Retrieved trusted certificate entry: " +
entry);
} else {
throw new Exception("Not a trusted certificate entry");
}
System.out.println();
entry = keystore.getEntry(ALIAS2, null);
if (entry instanceof KeyStore.TrustedCertificateEntry) {
KeyStore.TrustedCertificateEntry trustedEntry =
(KeyStore.TrustedCertificateEntry) entry;
Set<KeyStore.Entry.Attribute> entryAttributes =
trustedEntry.getAttributes();
if (entryAttributes.containsAll(attributes)) {
System.out.println("Retrieved trusted certificate entry " +
"with attributes: " + entry);
} else {
throw new Exception("Failed to retrieve entry attributes");
}
} else {
throw new Exception("Not a trusted certificate entry");
}
} finally {
new File(KEYSTORE).delete();
}
}
private static Certificate loadCertificate(String certFile)
throws Exception {
X509Certificate cert = null;
try (FileInputStream certStream = new FileInputStream(certFile)) {
CertificateFactory factory =
CertificateFactory.getInstance("X.509");
return factory.generateCertificate(certStream);
}
}
}
-----BEGIN CERTIFICATE-----
MIIF5DCCBMygAwIBAgIQGVCD3zqdD1ZMZZ/zLAPnQzANBgkqhkiG9w0BAQUFADCBvDELMAkGA1UE
BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
ZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29t
L3JwYSAoYykxMDE2MDQGA1UEAxMtVmVyaVNpZ24gQ2xhc3MgMyBJbnRlcm5hdGlvbmFsIFNlcnZl
ciBDQSAtIEczMB4XDTEyMDcxMDAwMDAwMFoXDTEzMDczMTIzNTk1OVowgbgxCzAJBgNVBAYTAlVT
MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQHFA5SZWR3b29kIFNob3JlczEbMBkGA1UEChQS
T3JhY2xlIENvcnBvcmF0aW9uMRIwEAYDVQQLFAlHbG9iYWwgSVQxMzAxBgNVBAsUKlRlcm1zIG9m
IHVzZSBhdCB3d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNTEVMBMGA1UEAxQMKi5vcmFjbGUuY29t
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/dOCGrWzPj62q0ZkF59Oj9Fli4wHAuX
U4/S0yBXF8j6K7TKWFTQkGZt3+08KUhmLm1CE1DbbyRJT292YNXYXunNaKdABob8kaBO/NESUOEJ
0SZh7fd0xCSJAAPiwOMrM5jLeb/dEpU6nP74Afrhu5ffvKdcvTRGguj9H2oVsisTK8Z1HsiiwcJG
JXcrjvdCZoPU4FHvK03XZPAqPHKNSaJOrux6kRIWYjQMlmL+qDOb0nNHa6gBdi+VqqJHJHeAM677
dcUd0jn2m2OWtUnrM3MJZQof7/z27RTdX5J8np0ChkUgm63biDgRZO7uZP0DARQ0I6lZMlrarT8/
sct3twIDAQABo4IB4jCCAd4wFwYDVR0RBBAwDoIMKi5vcmFjbGUuY29tMAkGA1UdEwQCMAAwCwYD
VR0PBAQDAgWgMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHFwMwKjAoBggrBgEFBQcCARYcaHR0cHM6
Ly93d3cudmVyaXNpZ24uY29tL3JwYTAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwbgYI
KwYBBQUHAQwEYjBgoV6gXDBaMFgwVhYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUS2u5KJYGDLvQ
UjibKaxLB4shBRgwJhYkaHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nbzEuZ2lmMHIGCCsG
AQUFBwEBBGYwZDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AudmVyaXNpZ24uY29tMDwGCCsGAQUF
BzAChjBodHRwOi8vc3ZyaW50bC1nMy1haWEudmVyaXNpZ24uY29tL1NWUkludGxHMy5jZXIwQQYD
VR0fBDowODA2oDSgMoYwaHR0cDovL3N2cmludGwtZzMtY3JsLnZlcmlzaWduLmNvbS9TVlJJbnRs
RzMuY3JsMB8GA1UdIwQYMBaAFNebfNgioBX33a1fzimbWMO8RgC1MA0GCSqGSIb3DQEBBQUAA4IB
AQAITRBlEo+qXLwCL53Db2BGnhDgnSomjne8aCmU7Yt4Kp91tzJdhNuaC/wwDuzD2dPJqzemae3s
wKiOXrmDQZDj9NNTdkrXHnCvDR4TpOynWe3zBa0bwKnV2cIRKcv482yV53u0kALyFZbagYPwOOz3
YJA/2SqdcDn9Ztc/ABQ1SkyXyA5j4LJdf2g7BtYrFxjy0RG6We2iM781WSB/9MCNKyHgiwd3KpLf
urdSKLzy1elNAyt1P3UHwBIIvZ6sJIr/eeELc54Lxt6PtQCXx8qwxYTYXWPXbLgKBHdebgrmAbPK
TfD69wysvjk6vwSHjmvaqB4R4WRcgkuT+1gxx+ve
-----END CERTIFICATE-----
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册