/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.company.security.csp;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStore.LoadStoreParameter;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.Provider;
import java.security.SecurityPermission;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of key store for Windows using the Microsoft Crypto API.
*
* @since 1.6
*/
public class CSPKeyStore extends KeyStoreSpi {
public static int DEFAULT_PROVIDER_ID = 75; // TODO 75 - CryptoPro providerId in MS Crypto API
private static transient Logger LOGGER = LoggerFactory.getLogger(CSPKeyStore.class);
private static MessageDigest sha1;
public static final class MY extends CSPKeyStore {
public MY() {
super("MY", true, DEFAULT_PROVIDER_ID);
}
}
public static final class ROOT extends CSPKeyStore {
public ROOT() {
super("ROOT");
}
}
public static final class CA extends CSPKeyStore {
public CA() {
super("CA");
}
}
/**
* Linux CSP 3.6R3 "AddressBook" – для сертификатов других пользователей.
*/
public static final class AddressBook extends CSPKeyStore {
public AddressBook() {
super("AddressBook");
}
}
public static final class FILE extends CSPKeyStore {
public FILE() {
super("FILE", false, DEFAULT_PROVIDER_ID);
}
@Override
public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {
// не разрещаем загружать хрнилище, пока не будет указано название файла
if(keyStoreLocation != null)
super.engineLoad(stream, password);
}
}
// public static final class HDImage extends KeyStore {
// public HDImage() {
// super("\\\\.\\HDIMAGE\\", true, DEFAULT_PROVIDER_ID);
//// super("HDIMAGE");
// }
// }
//
// private static byte[] calcHashPropId(X509Certificate certificate) {
// byte[] digest = null;
// try {
// digest = calcHashPropId(certificate.getEncoded());
// }
// catch(CertificateEncodingException e) {
// }
// return digest;
// }
private static byte[] calcHashPropId(byte[] encoded) {
byte[] digest = null;
if(encoded != null) {
try {
if(sha1 == null) {
sha1 = MessageDigest.getInstance("SHA1");
}
try {
digest = sha1.digest(encoded);
}
finally {
sha1.reset();
}
}
catch(NoSuchAlgorithmException e) {
}
}
return digest;
}
class KeyEntry {
private String alias;
private CSPKey privateKey;
private X509Certificate certChain[];
KeyEntry(String alias, byte[] encoded) {
this.alias = alias;
// this.certificate = certificate;
if(alias == null) {
byte[] digest = calcHashPropId(encoded);
this.alias = new BigInteger(1, digest).toString(16);
}
}
KeyEntry(CSPKey key, X509Certificate[] chain) {
this(null, key, chain);
}
KeyEntry(String alias, CSPKey key, X509Certificate[] chain) {
this.privateKey = key;
this.certChain = chain;
// использовать только SHA1
if(useAliasSHA1) {
try {
byte[] digest = calcHashPropId(chain[0].getEncoded());
this.alias = new BigInteger(1, digest).toString(16);
} catch (CertificateEncodingException ex) {
// ошибка получения сведений о сертификате
throw new IllegalArgumentException(ex.getMessage(), ex);
}
}
else {
/*
* The default alias for both entry types is derived from a hash
* value intrinsic to the first certificate in the chain.
*/
if(alias == null) {
try {
alias = CSPKey.getContainerName(key.getHCryptKey());
} catch (KeyStoreException e) {
// ошибка получения контейнера закрытого ключа
try {
byte[] digest = calcHashPropId(chain[0].getEncoded());
this.alias = new BigInteger(1, digest).toString(16);
} catch (CertificateEncodingException ex) {
// ошибка получения сведений о сертификате
throw new IllegalArgumentException(e.getMessage(), e);
}
}
} 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.
*/
CSPKey getPrivateKey() {
return privateKey;
}
/**
* Sets the private key for the keystore entry.
*/
void setPrivateKey(CSPPrivateKey key) throws InvalidKeyException, KeyStoreException {
privateKey = key;
}
/**
* 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 = "org.company.security.csp.keyStoreCompatibilityMode";
private final boolean keyStoreCompatibilityMode;
/**
* Для совместимости с MS CertStore
* использовать только SHA1
*/
private boolean useAliasSHA1 = true;
/*
* The keystore entries.
*/
private Collection<KeyEntry> entries = new ArrayList<KeyEntry>();
/*
* The keystore name. Case is not significant.
*/
private final String storeName;
private final boolean system;
private final int providerId;
protected String keyStoreLocation = null;
public CSPKeyStore(String storeName) {
this(storeName, true, 0);
}
public CSPKeyStore(String storeName, boolean system, int providerId) {
// Get the compatibility mode
String prop = AccessController.doPrivileged(new PrivilegedAction<String>() {
@Override
public String run() {
return System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP);
}
});
if ("false".equalsIgnoreCase(prop)) {
keyStoreCompatibilityMode = false;
} else {
keyStoreCompatibilityMode = true;
}
this.storeName = storeName;
this.system = system;
this.providerId = providerId;
}
public String getKeyStoreLocation() {
return keyStoreLocation;
}
public void setKeyStoreLocation(String keyStoreLocation) {
this.keyStoreLocation = keyStoreLocation;
}
/**
* 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;
for (KeyEntry entry : entries) {
if (alias.equals(entry.getAlias())) {
return entry.getPrivateKey();
}
}
return null;
}
/**
* 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;
}
for (KeyEntry entry : entries) {
if (alias.equals(entry.getAlias())) {
X509Certificate[] certChain = entry.getCertificateChain();
return certChain.clone();
}
}
return null;
}
/**
* 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;
}
for (KeyEntry entry : entries) {
if (alias.equals(entry.getAlias())) {
X509Certificate[] certChain = entry.getCertificateChain();
return certChain[0];
}
}
return null;
}
/**
* 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 CSPPrivateKey) {
KeyEntry entry = null;
boolean found = false;
for (KeyEntry e : entries) {
if (alias.equals(e.getAlias())) {
found = true;
entry = e;
break;
}
}
if (!found) {
entry =
// TODO new KeyEntry(alias, key, (X509Certificate[]) chain);
new KeyEntry(alias, null, (X509Certificate[]) chain);
entries.add(entry);
}
entry.setAlias(alias);
try {
entry.setPrivateKey((CSPPrivateKey) key);
entry.setCertificateChain((X509Certificate[]) chain);
} 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 = null;
boolean found = false;
for (KeyEntry e : entries) {
if (alias.equals(e.getAlias())) {
found = true;
entry = e;
break;
}
}
if (!found) {
entry = new KeyEntry(alias, null, chain);
entries.add(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");
}
for (KeyEntry entry : entries) {
if (alias.equals(entry.getAlias())) {
// Get end-entity certificate and remove from system cert store
X509Certificate[] certChain = entry.getCertificateChain();
if (certChain != null) {
try {
byte[] encoding = certChain[0].getEncoded();
removeCertificate(getName(), alias, encoding,
encoding.length);
} catch (CertificateException e) {
throw new KeyStoreException("Cannot remove entry: " + e);
}
}
CSPKey privateKey = entry.getPrivateKey();
if (privateKey != null) {
destroyKeyContainer(
privateKey.getProviderId(),
storeName,
CSPKey.getContainerName(privateKey.getHCryptProvider()));
}
entries.remove(entry);
break;
}
}
}
/**
* Lists all the alias names of this keystore.
*
* @return enumeration of the alias names
*/
public Enumeration<String> engineAliases() {
final Iterator<KeyEntry> iter = entries.iterator();
return new Enumeration<String>() {
public boolean hasMoreElements() {
return iter.hasNext();
}
public String nextElement() {
KeyEntry entry = iter.next();
return entry.getAlias();
}
};
}
/**
* 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) {
for (Enumeration<String> enumerator = engineAliases(); enumerator.hasMoreElements();) {
String a = enumerator.nextElement();
if (a.equals(alias))
return true;
}
return false;
}
/**
* 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;
}
for (KeyEntry entry : entries) {
if (alias.equals(entry.getAlias())) {
return entry.getPrivateKey() != null;
}
}
return false;
}
/**
* 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) {
for (KeyEntry entry : entries) {
if (alias.equals(entry.getAlias())) {
return entry.getPrivateKey() == null;
}
}
return false;
}
/**
* 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 (KeyEntry entry : entries) {
if (entry.certChain != null && 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.JavaCSPProvider"));
}
// Clear all key entries
entries.clear();
try {
// Load keys and/or certificate chains
loadKeysOrCertificateChains(getName(), entries, system, providerId);
} catch (KeyStoreException e) {
throw new IOException(e);
}
}
/**
* Через данный метод происходит загрузка внешнего файла хранилища
*/
@Override
public void engineLoad(LoadStoreParameter param) throws IOException,
NoSuchAlgorithmException, CertificateException {
if(param.getProtectionParameter() instanceof KeyStoreProtection) {
KeyStoreProtection protection = (KeyStoreProtection) param.getProtectionParameter();
keyStoreLocation = protection.keyStoreLocation;
engineLoad(null, protection.getPassword());
}
else {
super.engineLoad(param);
}
}
/**
* Generates a certificate chain from the collection of certificates and
* stores the result into a key entry.
*/
private void generateCertificateChain(String alias,
Collection<X509Certificate> certCollection, Collection<KeyEntry> entries) {
try {
X509Certificate[] certChain = new X509Certificate[certCollection
.size()];
int i = 0;
for (Iterator<X509Certificate> iter = certCollection.iterator(); iter.hasNext(); i++) {
certChain[i] = iter.next();
}
KeyEntry entry = new KeyEntry(alias, null, certChain);
// Add cert chain
entries.add(entry);
} catch (Throwable e) {
// Ignore the exception and skip this entry
// TODO - throw CertificateException?
LOGGER.error(e.getMessage(), e);
}
}
/**
* Generates CSP key and certificate chain from the private key handle,
* collection of certificates and stores the result into key entries.
*/
private void generateCSPKeyAndCertificateChain(String alias, String container,
int providerId,
long hCryptProv, long hCryptKey, int keyLength,
Collection<X509Certificate> certCollection, Collection<KeyEntry> entries) {
try {
X509Certificate[] certChain = new X509Certificate[certCollection.size()];
int i = 0;
for (Iterator<X509Certificate> iter = certCollection.iterator(); iter.hasNext(); i++) {
certChain[i] = iter.next();
}
// если псевдоним ключа не задан, берем имя контейнера, как делает JCP
// if(alias == null)
// alias = container;
CSPPrivateKey privateKey = new CSPPrivateKey(hCryptProv, hCryptKey, keyLength);
privateKey.setContainer(container);
privateKey.setProviderId(providerId);
KeyEntry entry = new KeyEntry(alias, privateKey, certChain);
// Add cert chain
entries.add(entry);
} catch (Throwable e) {
// Ignore the exception and skip this entry
// TODO - throw CertificateException?
LOGGER.error(e.getMessage(), e);
}
}
/**
* Generates certificates from byte data and stores into cert collection.
*
* @param data
* Byte data.
* @param certCollection
* Collection of certificates.
*/
private void generateCertificate(byte[] data, Collection<X509Certificate> certCollection) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
// Obtain certificate factory
if (certificateFactory == null) {
certificateFactory = CertificateFactory.getInstance("X.509", CSPProvider.CSP_PROVIDER);
}
// Generate certificate
Collection c = certificateFactory.generateCertificates(bis);
certCollection.addAll(c);
} catch (CertificateException e) {
// Ignore the exception and skip this certificate
// TODO - throw CertificateException?
LOGGER.error(e.getMessage(), e);
} catch (Throwable te) {
// Ignore the exception and skip this certificate
// TODO - throw CertificateException?
LOGGER.error(te.getMessage(), te);
}
}
/**
* Returns the name of the keystore.
*/
private String getName() {
String location = storeName;
if(keyStoreLocation != null && !keyStoreLocation.isEmpty())
location = keyStoreLocation;
return location;
}
/**
* Load keys and/or certificates from keystore into Collection.
*
* @param name
* Name of keystore.
* @param entries
* Collection of key/certificate.
* @param providerId2
* @param system2
*/
private void loadKeysOrCertificateChains(String name, Collection<KeyEntry> entries, boolean system, int providerId) throws KeyStoreException {
NativeCrypto.loadKeysOrCertificateChains(this, name, entries, system, providerId);
}
/**
* 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 void storeCertificate(String name, String alias,
byte[] encoding, int encodingLength, long hCryptProvider,
long hCryptKey) throws CertificateException, KeyStoreException {
NativeCrypto.storeCertificate(name, alias, encoding, encodingLength, hCryptProvider, hCryptKey);
}
/**
* 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 void removeCertificate(String name, String alias, byte[] encoding, int encodingLength)
throws CertificateException, KeyStoreException {
NativeCrypto.removeCertificate(name, alias, encoding, encodingLength);
}
/**
* Destroys the key container.
*
*
* @param keyContainerName
* The name of the key container.
*/
private void destroyKeyContainer(int providerId, String storeName, String keyContainerName) throws KeyStoreException {
NativeCrypto.destroyKeyContainer(providerId, storeName, keyContainerName);
}
// /**
// * Generates a private-key BLOB from a key's components.
// */
// private native byte[] generatePrivateKeyBlob(int keyBitLength,
// byte[] modulus, byte[] publicExponent, byte[] privateExponent,
// byte[] primeP, byte[] primeQ, byte[] exponentP, byte[] exponentQ,
// byte[] crtCoefficient) throws InvalidKeyException;
//
// private native CSPPrivateKey storePrivateKey(byte[] keyBlob,
// String keyContainerName, int keySize) throws KeyStoreException;
public abstract static class Builder extends KeyStore.Builder {
// maximum times to try the callbackhandler if the password is wrong
static final int MAX_CALLBACK_TRIES = 3;
public static Builder newInstance(String type, Provider provider,
String keyStoreLocation, char[] password) {
if ((type == null) || (provider == null)) {
throw new NullPointerException();
}
if (!"CSPProvider".equals(provider.getName())) {
throw new IllegalArgumentException("Реализуется только для CSPProvider");
}
if (!"FILE".equals(type)) {
throw new IllegalArgumentException("Реализуется только для хранилища FILE в CSPProvider");
}
File file = new File(keyStoreLocation);
if (file.isFile() == false) {
throw new IllegalArgumentException
("File does not exist or it does not refer " +
"to a normal file: " + file);
}
KeyStoreProtection protection = new KeyStoreProtection(type, provider,
file.getAbsolutePath(), password);
return new KeyStoreBuilder(protection, AccessController.getContext());
}
private static final class KeyStoreBuilder extends Builder {
private KeyStoreProtection protection;
private final AccessControlContext context;
private KeyStore keyStore;
private Throwable oldException;
KeyStoreBuilder(KeyStoreProtection protection, AccessControlContext context) {
this.protection = protection;
this.context = context;
}
public synchronized KeyStore getKeyStore() throws KeyStoreException
{
if (keyStore != null) {
return keyStore;
}
if (oldException != null) {
throw new KeyStoreException
("Previous KeyStore instantiation failed",
oldException);
}
PrivilegedExceptionAction<Object> action =
new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
// when using a CallbackHandler,
// reprompt if the password is wrong
int tries = 0;
while (true) {
tries++;
try {
return run0();
} catch (IOException e) {
if ((tries < MAX_CALLBACK_TRIES)
&& (e.getCause() instanceof UnrecoverableKeyException)) {
continue;
}
throw e;
}
}
}
public Object run0() throws Exception {
KeyStore ks;
if (protection.provider == null) {
ks = KeyStore.getInstance(protection.type);
} else {
ks = KeyStore.getInstance(protection.type, protection.provider);
}
InputStream in = null;
char[] password = null;
try {
ks.load(new SimpleLoadStoreParameter(protection));
return ks;
} finally {
if (in != null) {
in.close();
}
}
}
};
try {
keyStore = (KeyStore)AccessController.doPrivileged
(action, context);
return keyStore;
} catch (PrivilegedActionException e) {
oldException = e.getCause();
throw new KeyStoreException
("KeyStore instantiation failed", oldException);
}
}
public synchronized ProtectionParameter
getProtectionParameter(String alias) {
if (alias == null) {
throw new NullPointerException();
}
if (keyStore == null) {
throw new IllegalStateException
("getKeyStore() must be called first");
}
return protection;
}
}
}
public static class KeyStoreProtection extends PasswordProtection {
private String keyStoreLocation;
private String type;
private Provider provider;
public KeyStoreProtection(String type, Provider provider,
String keyStoreLocation, char[] password) {
super(password);
this.type = type;
this.provider = provider;
this.keyStoreLocation = keyStoreLocation;
}
public String getKeyStoreLocation() {
return keyStoreLocation;
}
}
public static class SimpleLoadStoreParameter implements LoadStoreParameter {
private final ProtectionParameter protection;
SimpleLoadStoreParameter(ProtectionParameter protection) {
this.protection = protection;
}
public ProtectionParameter getProtectionParameter() {
return protection;
}
}
}