mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
886 lines
30 KiB
Java
886 lines
30 KiB
Java
/*
|
|
* Copyright (c) 2005, 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
|
|
* 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 sun.security.mscapi;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.security.AccessController;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.Key;
|
|
import java.security.KeyStoreSpi;
|
|
import java.security.KeyStoreException;
|
|
import java.security.PrivilegedAction;
|
|
import java.security.UnrecoverableKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.SecurityPermission;
|
|
import java.security.cert.X509Certificate;
|
|
import java.security.cert.Certificate;
|
|
import java.security.cert.CertificateException;
|
|
import java.security.cert.CertificateFactory;
|
|
import java.security.interfaces.RSAPrivateCrtKey;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Implementation of key store for Windows using the Microsoft Crypto API.
|
|
*
|
|
* @since 1.6
|
|
*/
|
|
abstract class CKeyStore extends KeyStoreSpi {
|
|
|
|
public static final class MY extends CKeyStore {
|
|
public MY() {
|
|
super("MY");
|
|
}
|
|
}
|
|
|
|
public static final class ROOT extends CKeyStore {
|
|
public ROOT() {
|
|
super("ROOT");
|
|
}
|
|
}
|
|
|
|
class KeyEntry {
|
|
private CKey privateKey;
|
|
private X509Certificate[] certChain;
|
|
private String alias;
|
|
|
|
KeyEntry(CKey key, X509Certificate[] chain) {
|
|
this(null, key, chain);
|
|
}
|
|
|
|
KeyEntry(String alias, CKey key, X509Certificate[] chain) {
|
|
this.privateKey = key;
|
|
this.certChain = chain;
|
|
/*
|
|
* The default alias for both entry types is derived from a
|
|
* hash value intrinsic to the first certificate in the chain.
|
|
*/
|
|
if (alias == null) {
|
|
this.alias = Integer.toString(chain[0].hashCode());
|
|
} else {
|
|
this.alias = alias;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the alias for the keystore entry.
|
|
*/
|
|
String getAlias() {
|
|
return alias;
|
|
}
|
|
|
|
/**
|
|
* Sets the alias for the keystore entry.
|
|
*/
|
|
void setAlias(String alias) {
|
|
// TODO - set friendly name prop in cert store
|
|
this.alias = alias;
|
|
}
|
|
|
|
/**
|
|
* Gets the private key for the keystore entry.
|
|
*/
|
|
CKey getPrivateKey() {
|
|
return privateKey;
|
|
}
|
|
|
|
/**
|
|
* Sets the private key for the keystore entry.
|
|
*/
|
|
void setRSAPrivateKey(Key k)
|
|
throws InvalidKeyException, KeyStoreException {
|
|
RSAPrivateCrtKey key = (RSAPrivateCrtKey) k;
|
|
byte[] modulusBytes = key.getModulus().toByteArray();
|
|
|
|
// Adjust key length due to sign bit
|
|
int keyBitLength = (modulusBytes[0] == 0)
|
|
? (modulusBytes.length - 1) * 8
|
|
: modulusBytes.length * 8;
|
|
|
|
byte[] keyBlob = generateRSAPrivateKeyBlob(
|
|
keyBitLength,
|
|
modulusBytes,
|
|
key.getPublicExponent().toByteArray(),
|
|
key.getPrivateExponent().toByteArray(),
|
|
key.getPrimeP().toByteArray(),
|
|
key.getPrimeQ().toByteArray(),
|
|
key.getPrimeExponentP().toByteArray(),
|
|
key.getPrimeExponentQ().toByteArray(),
|
|
key.getCrtCoefficient().toByteArray());
|
|
|
|
privateKey = storePrivateKey("RSA", Objects.requireNonNull(keyBlob),
|
|
"{" + UUID.randomUUID().toString() + "}", keyBitLength);
|
|
}
|
|
|
|
/**
|
|
* Gets the certificate chain for the keystore entry.
|
|
*/
|
|
X509Certificate[] getCertificateChain() {
|
|
return certChain;
|
|
}
|
|
|
|
/**
|
|
* Sets the certificate chain for the keystore entry.
|
|
*/
|
|
void setCertificateChain(X509Certificate[] chain)
|
|
throws CertificateException, KeyStoreException {
|
|
for (int i = 0; i < chain.length; i++) {
|
|
byte[] encoding = chain[i].getEncoded();
|
|
if (i == 0 && privateKey != null) {
|
|
storeCertificate(getName(), alias, encoding,
|
|
encoding.length, privateKey.getHCryptProvider(),
|
|
privateKey.getHCryptKey());
|
|
|
|
} else {
|
|
storeCertificate(getName(), alias, encoding,
|
|
encoding.length, 0L, 0L); // no private key to attach
|
|
}
|
|
}
|
|
certChain = chain;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* An X.509 certificate factory.
|
|
* Used to create an X.509 certificate from its DER-encoding.
|
|
*/
|
|
private CertificateFactory certificateFactory = null;
|
|
|
|
/*
|
|
* Compatibility mode: for applications that assume keystores are
|
|
* stream-based this mode tolerates (but ignores) a non-null stream
|
|
* or password parameter when passed to the load or store methods.
|
|
* The mode is enabled by default.
|
|
*/
|
|
private static final String KEYSTORE_COMPATIBILITY_MODE_PROP =
|
|
"sun.security.mscapi.keyStoreCompatibilityMode";
|
|
private final boolean keyStoreCompatibilityMode;
|
|
|
|
/*
|
|
* The keystore entries.
|
|
* Keys in the map are unique aliases (thus can differ from
|
|
* KeyEntry.getAlias())
|
|
*/
|
|
private Map<String,KeyEntry> entries = new HashMap<>();
|
|
|
|
/*
|
|
* The keystore name.
|
|
* Case is not significant.
|
|
*/
|
|
private final String storeName;
|
|
|
|
CKeyStore(String storeName) {
|
|
// Get the compatibility mode
|
|
String prop = AccessController.doPrivileged(
|
|
(PrivilegedAction<String>) () -> System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP));
|
|
|
|
if ("false".equalsIgnoreCase(prop)) {
|
|
keyStoreCompatibilityMode = false;
|
|
} else {
|
|
keyStoreCompatibilityMode = true;
|
|
}
|
|
|
|
this.storeName = storeName;
|
|
}
|
|
|
|
/**
|
|
* Returns the key associated with the given alias.
|
|
* <p>
|
|
* A compatibility mode is supported for applications that assume
|
|
* a password must be supplied. It permits (but ignores) a non-null
|
|
* <code>password</code>. The mode is enabled by default.
|
|
* Set the
|
|
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
|
|
* system property to <code>false</code> to disable compatibility mode
|
|
* and reject a non-null <code>password</code>.
|
|
*
|
|
* @param alias the alias name
|
|
* @param password the password, which should be <code>null</code>
|
|
*
|
|
* @return the requested key, or null if the given alias does not exist
|
|
* or does not identify a <i>key entry</i>.
|
|
*
|
|
* @exception NoSuchAlgorithmException if the algorithm for recovering the
|
|
* key cannot be found,
|
|
* or if compatibility mode is disabled and <code>password</code> is
|
|
* non-null.
|
|
* @exception UnrecoverableKeyException if the key cannot be recovered.
|
|
*/
|
|
public java.security.Key engineGetKey(String alias, char[] password)
|
|
throws NoSuchAlgorithmException, UnrecoverableKeyException {
|
|
if (alias == null) {
|
|
return null;
|
|
}
|
|
|
|
if (password != null && !keyStoreCompatibilityMode) {
|
|
throw new UnrecoverableKeyException("Password must be null");
|
|
}
|
|
|
|
if (engineIsKeyEntry(alias) == false)
|
|
return null;
|
|
|
|
KeyEntry entry = entries.get(alias);
|
|
return (entry == null)
|
|
? null
|
|
: entry.getPrivateKey();
|
|
}
|
|
|
|
/**
|
|
* Returns the certificate chain associated with the given alias.
|
|
*
|
|
* @param alias the alias name
|
|
*
|
|
* @return the certificate chain (ordered with the user's certificate first
|
|
* and the root certificate authority last), or null if the given alias
|
|
* does not exist or does not contain a certificate chain (i.e., the given
|
|
* alias identifies either a <i>trusted certificate entry</i> or a
|
|
* <i>key entry</i> without a certificate chain).
|
|
*/
|
|
public Certificate[] engineGetCertificateChain(String alias) {
|
|
if (alias == null) {
|
|
return null;
|
|
}
|
|
|
|
KeyEntry entry = entries.get(alias);
|
|
X509Certificate[] certChain = (entry == null)
|
|
? null
|
|
: entry.getCertificateChain();
|
|
return (certChain == null)
|
|
? null
|
|
: certChain.clone();
|
|
}
|
|
|
|
/**
|
|
* Returns the certificate associated with the given alias.
|
|
*
|
|
* <p>If the given alias name identifies a
|
|
* <i>trusted certificate entry</i>, the certificate associated with that
|
|
* entry is returned. If the given alias name identifies a
|
|
* <i>key entry</i>, the first element of the certificate chain of that
|
|
* entry is returned, or null if that entry does not have a certificate
|
|
* chain.
|
|
*
|
|
* @param alias the alias name
|
|
*
|
|
* @return the certificate, or null if the given alias does not exist or
|
|
* does not contain a certificate.
|
|
*/
|
|
public Certificate engineGetCertificate(String alias) {
|
|
if (alias == null) {
|
|
return null;
|
|
}
|
|
|
|
KeyEntry entry = entries.get(alias);
|
|
X509Certificate[] certChain = (entry == null)
|
|
? null
|
|
: entry.getCertificateChain();
|
|
return (certChain == null || certChain.length == 0)
|
|
? null
|
|
: certChain[0];
|
|
}
|
|
|
|
/**
|
|
* Returns the creation date of the entry identified by the given alias.
|
|
*
|
|
* @param alias the alias name
|
|
*
|
|
* @return the creation date of this entry, or null if the given alias does
|
|
* not exist
|
|
*/
|
|
public Date engineGetCreationDate(String alias) {
|
|
if (alias == null) {
|
|
return null;
|
|
}
|
|
return new Date();
|
|
}
|
|
|
|
/**
|
|
* Stores the given private key and associated certificate chain in the
|
|
* keystore.
|
|
*
|
|
* <p>The given java.security.PrivateKey <code>key</code> must
|
|
* be accompanied by a certificate chain certifying the
|
|
* corresponding public key.
|
|
*
|
|
* <p>If the given alias already exists, the keystore information
|
|
* associated with it is overridden by the given key and certificate
|
|
* chain. Otherwise, a new entry is created.
|
|
*
|
|
* <p>
|
|
* A compatibility mode is supported for applications that assume
|
|
* a password must be supplied. It permits (but ignores) a non-null
|
|
* <code>password</code>. The mode is enabled by default.
|
|
* Set the
|
|
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
|
|
* system property to <code>false</code> to disable compatibility mode
|
|
* and reject a non-null <code>password</code>.
|
|
*
|
|
* @param alias the alias name
|
|
* @param key the private key to be associated with the alias
|
|
* @param password the password, which should be <code>null</code>
|
|
* @param chain the certificate chain for the corresponding public
|
|
* key (only required if the given key is of type
|
|
* <code>java.security.PrivateKey</code>).
|
|
*
|
|
* @exception KeyStoreException if the given key is not a private key,
|
|
* cannot be protected, or if compatibility mode is disabled and
|
|
* <code>password</code> is non-null, or if this operation fails for
|
|
* some other reason.
|
|
*/
|
|
public void engineSetKeyEntry(String alias, java.security.Key key,
|
|
char[] password, Certificate[] chain) throws KeyStoreException {
|
|
if (alias == null) {
|
|
throw new KeyStoreException("alias must not be null");
|
|
}
|
|
|
|
if (password != null && !keyStoreCompatibilityMode) {
|
|
throw new KeyStoreException("Password must be null");
|
|
}
|
|
|
|
if (key instanceof RSAPrivateCrtKey) {
|
|
|
|
KeyEntry entry = entries.get(alias);
|
|
|
|
X509Certificate[] xchain;
|
|
if (chain != null) {
|
|
if (chain instanceof X509Certificate[]) {
|
|
xchain = (X509Certificate[]) chain;
|
|
} else {
|
|
xchain = new X509Certificate[chain.length];
|
|
System.arraycopy(chain, 0, xchain, 0, chain.length);
|
|
}
|
|
} else {
|
|
xchain = null;
|
|
}
|
|
|
|
if (entry == null) {
|
|
entry =
|
|
//TODO new KeyEntry(alias, key, (X509Certificate[]) chain);
|
|
new KeyEntry(alias, null, xchain);
|
|
storeWithUniqueAlias(alias, entry);
|
|
}
|
|
|
|
entry.setAlias(alias);
|
|
|
|
try {
|
|
entry.setRSAPrivateKey(key);
|
|
entry.setCertificateChain(xchain);
|
|
|
|
} catch (CertificateException ce) {
|
|
throw new KeyStoreException(ce);
|
|
|
|
} catch (InvalidKeyException ike) {
|
|
throw new KeyStoreException(ike);
|
|
}
|
|
|
|
} else {
|
|
throw new UnsupportedOperationException(
|
|
"Cannot assign the key to the given alias.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Assigns the given key (that has already been protected) to the given
|
|
* alias.
|
|
*
|
|
* <p>If the protected key is of type
|
|
* <code>java.security.PrivateKey</code>, it must be accompanied by a
|
|
* certificate chain certifying the corresponding public key. If the
|
|
* underlying keystore implementation is of type <code>jks</code>,
|
|
* <code>key</code> must be encoded as an
|
|
* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
|
|
*
|
|
* <p>If the given alias already exists, the keystore information
|
|
* associated with it is overridden by the given key (and possibly
|
|
* certificate chain).
|
|
*
|
|
* @param alias the alias name
|
|
* @param key the key (in protected format) to be associated with the alias
|
|
* @param chain the certificate chain for the corresponding public
|
|
* key (only useful if the protected key is of type
|
|
* <code>java.security.PrivateKey</code>).
|
|
*
|
|
* @exception KeyStoreException if this operation fails.
|
|
*/
|
|
public void engineSetKeyEntry(String alias, byte[] key,
|
|
Certificate[] chain)
|
|
throws KeyStoreException {
|
|
throw new UnsupportedOperationException(
|
|
"Cannot assign the encoded key to the given alias.");
|
|
}
|
|
|
|
/**
|
|
* Assigns the given certificate to the given alias.
|
|
*
|
|
* <p>If the given alias already exists in this keystore and identifies a
|
|
* <i>trusted certificate entry</i>, the certificate associated with it is
|
|
* overridden by the given certificate.
|
|
*
|
|
* @param alias the alias name
|
|
* @param cert the certificate
|
|
*
|
|
* @exception KeyStoreException if the given alias already exists and does
|
|
* not identify a <i>trusted certificate entry</i>, or this operation
|
|
* fails for some other reason.
|
|
*/
|
|
public void engineSetCertificateEntry(String alias, Certificate cert)
|
|
throws KeyStoreException {
|
|
if (alias == null) {
|
|
throw new KeyStoreException("alias must not be null");
|
|
}
|
|
|
|
if (cert instanceof X509Certificate) {
|
|
|
|
// TODO - build CryptoAPI chain?
|
|
X509Certificate[] chain =
|
|
new X509Certificate[]{ (X509Certificate) cert };
|
|
KeyEntry entry = entries.get(alias);
|
|
|
|
if (entry == null) {
|
|
entry =
|
|
new KeyEntry(alias, null, chain);
|
|
storeWithUniqueAlias(alias, entry);
|
|
}
|
|
|
|
if (entry.getPrivateKey() == null) { // trusted-cert entry
|
|
entry.setAlias(alias);
|
|
|
|
try {
|
|
entry.setCertificateChain(chain);
|
|
|
|
} catch (CertificateException ce) {
|
|
throw new KeyStoreException(ce);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
throw new UnsupportedOperationException(
|
|
"Cannot assign the certificate to the given alias.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes the entry identified by the given alias from this keystore.
|
|
*
|
|
* @param alias the alias name
|
|
*
|
|
* @exception KeyStoreException if the entry cannot be removed.
|
|
*/
|
|
public void engineDeleteEntry(String alias) throws KeyStoreException {
|
|
if (alias == null) {
|
|
throw new KeyStoreException("alias must not be null");
|
|
}
|
|
|
|
KeyEntry entry = entries.remove(alias);
|
|
if (entry != null) {
|
|
// Get end-entity certificate and remove from system cert store
|
|
X509Certificate[] certChain = entry.getCertificateChain();
|
|
if (certChain != null && certChain.length > 0) {
|
|
|
|
try {
|
|
|
|
byte[] encoding = certChain[0].getEncoded();
|
|
removeCertificate(getName(), entry.getAlias(), encoding,
|
|
encoding.length);
|
|
|
|
} catch (CertificateException e) {
|
|
throw new KeyStoreException("Cannot remove entry: ", e);
|
|
}
|
|
}
|
|
CKey privateKey = entry.getPrivateKey();
|
|
if (privateKey != null) {
|
|
destroyKeyContainer(
|
|
CKey.getContainerName(privateKey.getHCryptProvider()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lists all the alias names of this keystore.
|
|
*
|
|
* @return enumeration of the alias names
|
|
*/
|
|
public Enumeration<String> engineAliases() {
|
|
final Iterator<String> iter = entries.keySet().iterator();
|
|
|
|
return new Enumeration<String>() {
|
|
public boolean hasMoreElements() {
|
|
return iter.hasNext();
|
|
}
|
|
|
|
public String nextElement() {
|
|
return iter.next();
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Checks if the given alias exists in this keystore.
|
|
*
|
|
* @param alias the alias name
|
|
*
|
|
* @return true if the alias exists, false otherwise
|
|
*/
|
|
public boolean engineContainsAlias(String alias) {
|
|
return entries.containsKey(alias);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the number of entries in this keystore.
|
|
*
|
|
* @return the number of entries in this keystore
|
|
*/
|
|
public int engineSize() {
|
|
return entries.size();
|
|
}
|
|
|
|
/**
|
|
* Returns true if the entry identified by the given alias is a
|
|
* <i>key entry</i>, and false otherwise.
|
|
*
|
|
* @return true if the entry identified by the given alias is a
|
|
* <i>key entry</i>, false otherwise.
|
|
*/
|
|
public boolean engineIsKeyEntry(String alias) {
|
|
|
|
if (alias == null) {
|
|
return false;
|
|
}
|
|
|
|
KeyEntry entry = entries.get(alias);
|
|
return entry != null && entry.getPrivateKey() != null;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the entry identified by the given alias is a
|
|
* <i>trusted certificate entry</i>, and false otherwise.
|
|
*
|
|
* @return true if the entry identified by the given alias is a
|
|
* <i>trusted certificate entry</i>, false otherwise.
|
|
*/
|
|
public boolean engineIsCertificateEntry(String alias) {
|
|
|
|
if (alias == null) {
|
|
return false;
|
|
}
|
|
|
|
KeyEntry entry = entries.get(alias);
|
|
return entry != null && entry.getPrivateKey() == null;
|
|
}
|
|
|
|
/**
|
|
* Returns the (alias) name of the first keystore entry whose certificate
|
|
* matches the given certificate.
|
|
*
|
|
* <p>This method attempts to match the given certificate with each
|
|
* keystore entry. If the entry being considered
|
|
* is a <i>trusted certificate entry</i>, the given certificate is
|
|
* compared to that entry's certificate. If the entry being considered is
|
|
* a <i>key entry</i>, the given certificate is compared to the first
|
|
* element of that entry's certificate chain (if a chain exists).
|
|
*
|
|
* @param cert the certificate to match with.
|
|
*
|
|
* @return the (alias) name of the first entry with matching certificate,
|
|
* or null if no such entry exists in this keystore.
|
|
*/
|
|
public String engineGetCertificateAlias(Certificate cert) {
|
|
|
|
for (Map.Entry<String,KeyEntry> mapEntry : entries.entrySet()) {
|
|
KeyEntry entry = mapEntry.getValue();
|
|
if (entry.certChain != null &&
|
|
entry.certChain.length > 0 &&
|
|
entry.certChain[0].equals(cert)) {
|
|
return entry.getAlias();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* engineStore is currently a no-op.
|
|
* Entries are stored during engineSetEntry.
|
|
*
|
|
* A compatibility mode is supported for applications that assume
|
|
* keystores are stream-based. It permits (but ignores) a non-null
|
|
* <code>stream</code> or <code>password</code>.
|
|
* The mode is enabled by default.
|
|
* Set the
|
|
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
|
|
* system property to <code>false</code> to disable compatibility mode
|
|
* and reject a non-null <code>stream</code> or <code>password</code>.
|
|
*
|
|
* @param stream the output stream, which should be <code>null</code>
|
|
* @param password the password, which should be <code>null</code>
|
|
*
|
|
* @exception IOException if compatibility mode is disabled and either
|
|
* parameter is non-null.
|
|
*/
|
|
public void engineStore(OutputStream stream, char[] password)
|
|
throws IOException, NoSuchAlgorithmException, CertificateException {
|
|
if (stream != null && !keyStoreCompatibilityMode) {
|
|
throw new IOException("Keystore output stream must be null");
|
|
}
|
|
|
|
if (password != null && !keyStoreCompatibilityMode) {
|
|
throw new IOException("Keystore password must be null");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads the keystore.
|
|
*
|
|
* A compatibility mode is supported for applications that assume
|
|
* keystores are stream-based. It permits (but ignores) a non-null
|
|
* <code>stream</code> or <code>password</code>.
|
|
* The mode is enabled by default.
|
|
* Set the
|
|
* <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
|
|
* system property to <code>false</code> to disable compatibility mode
|
|
* and reject a non-null <code>stream</code> or <code>password</code>.
|
|
*
|
|
* @param stream the input stream, which should be <code>null</code>.
|
|
* @param password the password, which should be <code>null</code>.
|
|
*
|
|
* @exception IOException if there is an I/O or format problem with the
|
|
* keystore data. Or if compatibility mode is disabled and either
|
|
* parameter is non-null.
|
|
* @exception NoSuchAlgorithmException if the algorithm used to check
|
|
* the integrity of the keystore cannot be found
|
|
* @exception CertificateException if any of the certificates in the
|
|
* keystore could not be loaded
|
|
* @exception SecurityException if the security check for
|
|
* <code>SecurityPermission("authProvider.<i>name</i>")</code> does not
|
|
* pass, where <i>name</i> is the value returned by
|
|
* this provider's <code>getName</code> method.
|
|
*/
|
|
public void engineLoad(InputStream stream, char[] password)
|
|
throws IOException, NoSuchAlgorithmException, CertificateException {
|
|
if (stream != null && !keyStoreCompatibilityMode) {
|
|
throw new IOException("Keystore input stream must be null");
|
|
}
|
|
|
|
if (password != null && !keyStoreCompatibilityMode) {
|
|
throw new IOException("Keystore password must be null");
|
|
}
|
|
|
|
/*
|
|
* Use the same security check as AuthProvider.login
|
|
*/
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
sm.checkPermission(new SecurityPermission(
|
|
"authProvider.SunMSCAPI"));
|
|
}
|
|
|
|
// Clear all key entries
|
|
entries.clear();
|
|
|
|
try {
|
|
|
|
// Load keys and/or certificate chains
|
|
loadKeysOrCertificateChains(getName());
|
|
|
|
} catch (KeyStoreException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stores the given entry into the map, making sure
|
|
* the alias, used as the key is unique.
|
|
* If the same alias already exists, it tries to append
|
|
* a suffix (1), (2), etc to it until it finds a unique
|
|
* value.
|
|
*/
|
|
private void storeWithUniqueAlias(String alias, KeyEntry entry) {
|
|
String uniqAlias = alias;
|
|
int uniqNum = 1;
|
|
|
|
while (true) {
|
|
if (entries.putIfAbsent(uniqAlias, entry) == null) {
|
|
break;
|
|
}
|
|
uniqAlias = alias + " (" + (uniqNum++) + ")";
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Generates a certificate chain from the collection of
|
|
* certificates and stores the result into a key entry.
|
|
* <p>
|
|
* This method is called by native codes in security.cpp.
|
|
*/
|
|
private void generateCertificateChain(String alias,
|
|
Collection<? extends Certificate> certCollection) {
|
|
try {
|
|
X509Certificate[] certChain =
|
|
new X509Certificate[certCollection.size()];
|
|
|
|
int i = 0;
|
|
for (Iterator<? extends Certificate> iter =
|
|
certCollection.iterator(); iter.hasNext(); i++) {
|
|
certChain[i] = (X509Certificate) iter.next();
|
|
}
|
|
|
|
storeWithUniqueAlias(alias,
|
|
new KeyEntry(alias, null, certChain));
|
|
} catch (Throwable e) {
|
|
// Ignore the exception and skip this entry
|
|
// If e is thrown, remember to deal with it in
|
|
// native code.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates key and certificate chain from the private key handle,
|
|
* collection of certificates and stores the result into key entries.
|
|
* <p>
|
|
* This method is called by native codes in security.cpp.
|
|
*/
|
|
private void generateKeyAndCertificateChain(boolean isRSA, String alias,
|
|
long hCryptProv, long hCryptKey, int keyLength,
|
|
Collection<? extends Certificate> certCollection) {
|
|
try {
|
|
X509Certificate[] certChain =
|
|
new X509Certificate[certCollection.size()];
|
|
|
|
int i = 0;
|
|
for (Iterator<? extends Certificate> iter =
|
|
certCollection.iterator(); iter.hasNext(); i++) {
|
|
certChain[i] = (X509Certificate) iter.next();
|
|
}
|
|
storeWithUniqueAlias(alias, new KeyEntry(alias,
|
|
CPrivateKey.of(isRSA ? "RSA" : "EC", hCryptProv, hCryptKey, keyLength),
|
|
certChain));
|
|
} catch (Throwable e) {
|
|
// Ignore the exception and skip this entry
|
|
// If e is thrown, remember to deal with it in
|
|
// native code.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates certificates from byte data and stores into cert collection.
|
|
* <p>
|
|
* This method is called by native codes in security.cpp.
|
|
*
|
|
* @param data Byte data.
|
|
* @param certCollection Collection of certificates.
|
|
*/
|
|
private void generateCertificate(byte[] data,
|
|
Collection<Certificate> certCollection) {
|
|
try {
|
|
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
|
|
|
// Obtain certificate factory
|
|
if (certificateFactory == null) {
|
|
certificateFactory = CertificateFactory.getInstance("X.509", "SUN");
|
|
}
|
|
|
|
// Generate certificate
|
|
Collection<? extends Certificate> c =
|
|
certificateFactory.generateCertificates(bis);
|
|
certCollection.addAll(c);
|
|
} catch (CertificateException e) {
|
|
// Ignore the exception and skip this certificate
|
|
// If e is thrown, remember to deal with it in
|
|
// native code.
|
|
}
|
|
catch (Throwable te)
|
|
{
|
|
// Ignore the exception and skip this certificate
|
|
// If e is thrown, remember to deal with it in
|
|
// native code.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the keystore.
|
|
*/
|
|
private String getName() {
|
|
return storeName;
|
|
}
|
|
|
|
/**
|
|
* Load keys and/or certificates from keystore into Collection.
|
|
*
|
|
* @param name Name of keystore.
|
|
*/
|
|
private native void loadKeysOrCertificateChains(String name)
|
|
throws KeyStoreException;
|
|
|
|
/**
|
|
* Stores a DER-encoded certificate into the certificate store
|
|
*
|
|
* @param name Name of the keystore.
|
|
* @param alias Name of the certificate.
|
|
* @param encoding DER-encoded certificate.
|
|
*/
|
|
private native void storeCertificate(String name, String alias,
|
|
byte[] encoding, int encodingLength, long hCryptProvider,
|
|
long hCryptKey) throws CertificateException, KeyStoreException;
|
|
|
|
/**
|
|
* Removes the certificate from the certificate store
|
|
*
|
|
* @param name Name of the keystore.
|
|
* @param alias Name of the certificate.
|
|
* @param encoding DER-encoded certificate.
|
|
*/
|
|
private native void removeCertificate(String name, String alias,
|
|
byte[] encoding, int encodingLength)
|
|
throws CertificateException, KeyStoreException;
|
|
|
|
/**
|
|
* Destroys the key container.
|
|
*
|
|
* @param keyContainerName The name of the key container.
|
|
*/
|
|
private native void destroyKeyContainer(String keyContainerName)
|
|
throws KeyStoreException;
|
|
|
|
/**
|
|
* Generates a private-key BLOB from a key's components.
|
|
*/
|
|
private native byte[] generateRSAPrivateKeyBlob(
|
|
int keyBitLength,
|
|
byte[] modulus,
|
|
byte[] publicExponent,
|
|
byte[] privateExponent,
|
|
byte[] primeP,
|
|
byte[] primeQ,
|
|
byte[] exponentP,
|
|
byte[] exponentQ,
|
|
byte[] crtCoefficient) throws InvalidKeyException;
|
|
|
|
private native CPrivateKey storePrivateKey(String alg, byte[] keyBlob,
|
|
String keyContainerName, int keySize) throws KeyStoreException;
|
|
}
|