
org.globus.gsi.stores.PEMKeyStore Maven / Gradle / Ivy
The newest version!
/*
* Copyright 1999-2010 University of Chicago
*
* Licensed 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.globus.gsi.stores;
import static org.globus.gsi.util.CertificateIOUtil.writeCertificate;
import org.globus.gsi.CredentialException;
import org.globus.gsi.X509Credential;
import org.globus.gsi.provider.KeyStoreParametersFactory;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.Properties;
import org.globus.util.GlobusResource;
import org.globus.util.GlobusPathMatchingResourcePatternResolver;
import org.globus.gsi.util.CertificateIOUtil;
/**
* This class provides a KeyStore implementation that supports trusted
* certificates stored in PEM format and proxy certificates stored in PEM
* format. It reads trusted certificates from multiple directories and a proxy
* certificate from a file.
*
* @version ${version}
* @since 1.0
*/
public class PEMKeyStore extends KeyStoreSpi {
// Default trusted certificates directory
public static final String DEFAULT_DIRECTORY_KEY = "default_directory";
// List of directory names to load certificates from
// JGLOBUS-90 : does it take certificate file names in this list?
public static final String DIRECTORY_LIST_KEY = "directory_list";
// X.509 Certificate file name, should be set along with KEY_FILENAME
public static final String CERTIFICATE_FILENAME = "certificateFilename";
// Key, typically private key, accompanying the certificate
public static final String KEY_FILENAME = "keyFilename";
// X.509 PRoxy Cerificate file name
public static final String PROXY_FILENAME = "proxyFilename";
private static Log logger = LogFactory.getLog(PEMKeyStore.class
.getCanonicalName());
// Map from alias to the object (either key or certificate)
private Map> aliasObjectMap = new Hashtable>();
// Map from trusted certificate to filename
private Map certFilenameMap = new HashMap();
// default directory for trusted certificates
private File defaultDirectory;
private ResourceSecurityWrapperStore caDelegate = new ResourceCACertStore();
private ResourceSecurityWrapperStore proxyDelegate = new ResourceProxyCredentialStore();
private boolean inMemoryOnly = false;
public void setCACertStore(
ResourceSecurityWrapperStore caCertStore) {
this.caDelegate = caCertStore;
}
public void setProxyDelegate(
ResourceSecurityWrapperStore proxyDelegate) {
this.proxyDelegate = proxyDelegate;
}
private CredentialWrapper getKeyEntry(String alias) {
SecurityObjectWrapper> object = this.aliasObjectMap.get(alias);
if ((object != null) && (object instanceof CredentialWrapper)) {
return (CredentialWrapper) object;
}
return null;
}
private ResourceTrustAnchor getCertificateEntry(String alias) {
SecurityObjectWrapper> object = this.aliasObjectMap.get(alias);
if ((object != null) && (object instanceof ResourceTrustAnchor)) {
return (ResourceTrustAnchor) object;
}
return null;
}
/**
* Get the key referenced by the specified alias.
*
* @param s
* The key's alias.
* @param chars
* The key's password.
* @return The key reference by the alias or null.
* @throws NoSuchAlgorithmException
* If the key is encoded with an invalid algorithm.
* @throws UnrecoverableKeyException
* If the key can not be retrieved.
*/
@Override
public Key engineGetKey(String s, char[] chars)
throws NoSuchAlgorithmException, UnrecoverableKeyException {
CredentialWrapper credential = getKeyEntry(s);
Key key = null;
if (credential != null) {
try {
String password = null;
if (chars != null) {
password = new String(chars);
}
key = credential.getCredential().getPrivateKey(password);
} catch (ResourceStoreException e) {
throw new UnrecoverableKeyException(e.getMessage());
} catch (CredentialException e) {
throw new UnrecoverableKeyException(e.getMessage());
}
}
return key;
}
/**
* Does the supplied alias refer to a key in this key store.
*
* @param s
* The alias.
* @return True if the alias refers to a key.
*/
@Override
public boolean engineIsKeyEntry(String s) {
return getKeyEntry(s) != null;
}
/**
* Persist the security material in this keystore. If the object has a path
* associated with it, the object will be persisted to that path. Otherwise
* it will be stored in the default certificate directory. As a result, the
* parameters of this method are ignored.
*
* @param outputStream
* This parameter is ignored.
* @param chars
* This parameter is ignored.
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws CertificateException
*/
@Override
public void engineStore(OutputStream outputStream, char[] chars)
throws IOException, NoSuchAlgorithmException, CertificateException {
for (SecurityObjectWrapper> object : this.aliasObjectMap.values()) {
if (object instanceof Storable) {
try {
((Storable) object).store();
} catch (ResourceStoreException e) {
throw new CertificateException(e);
}
}
}
}
/**
* Get the creation date for the object referenced by the alias.
*
* @param s
* The alias of the security object.
* @return The creation date of the security object.
*/
@Override
public Date engineGetCreationDate(String s) {
try {
ResourceTrustAnchor trustAnchor = getCertificateEntry(s);
if (trustAnchor != null) {
return trustAnchor.getTrustAnchor().getTrustedCert()
.getNotBefore();
} else {
CredentialWrapper credential = getKeyEntry(s);
if (credential != null) {
return credential.getCredential().getNotBefore();
}
}
} catch (ResourceStoreException e) {
return null;
}
return null;
}
/**
* Get the alias associated with the supplied certificate.
*
* @param certificate
* The certificate to query
* @return The certificate's alias or null if the certificate is not present
* in this keystore.
*/
@Override
public String engineGetCertificateAlias(Certificate certificate) {
return this.certFilenameMap.get(certificate);
}
/**
* Get the certificateChain for the key referenced by the alias.
*
* @param s
* The key alias.
* @return The key's certificate chain or a 0 length array if the key is not
* in the keystore.
*/
@Override
public Certificate[] engineGetCertificateChain(String s) {
CredentialWrapper credential = getKeyEntry(s);
X509Certificate[] chain = new X509Certificate[0];
if (credential != null) {
try {
chain = credential.getCredential().getCertificateChain();
} catch (ResourceStoreException e) {
logger.warn(e.getMessage(), e);
chain = null;
}
}
return chain;
}
/**
* Get the certificate referenced by the supplied alias.
*
* @param s
* The alias.
* @return The Certificate or null if the alias does not exist in the
* keyStore.
*/
@Override
public Certificate engineGetCertificate(String s) {
ResourceTrustAnchor trustAnchor = getCertificateEntry(s);
if (trustAnchor != null) {
try {
return trustAnchor.getTrustAnchor().getTrustedCert();
} catch (ResourceStoreException e) {
return null;
}
}
return null;
}
/**
* Load the keystore based on parameters in the LoadStoreParameter. The
* parameter object must be an instance of FileBasedKeyStoreParameters.
*
* @param loadStoreParameter
* The parameters to load.
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws CertificateException
*/
@Override
public void engineLoad(KeyStore.LoadStoreParameter loadStoreParameter)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (!(loadStoreParameter instanceof KeyStoreParametersFactory.FileStoreParameters)) {
throw new IllegalArgumentException("Unable to process parameters: "
+ loadStoreParameter);
}
KeyStoreParametersFactory.FileStoreParameters params = (KeyStoreParametersFactory.FileStoreParameters) loadStoreParameter;
String defaultDirectoryString = (String) params
.getProperty(DEFAULT_DIRECTORY_KEY);
String directoryListString = (String) params
.getProperty(DIRECTORY_LIST_KEY);
String certFilename = (String) params.getProperty(CERTIFICATE_FILENAME);
String keyFilename = (String) params.getProperty(KEY_FILENAME);
String proxyFilename = (String) params.getProperty(PROXY_FILENAME);
initialize(defaultDirectoryString, directoryListString, proxyFilename,
certFilename, keyFilename);
}
/**
* Load the keystore from the supplied input stream. Unlike many other
* implementations of keystore (most notably the default JKS
* implementation), the input stream does not hold the keystore objects.
* Instead, it must be a properties file defining the locations of the
* keystore objects. The password is not used.
*
* @param inputStream
* An input stream to the properties file.
* @param chars
* The password is not used.
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws CertificateException
*/
@Override
public void engineLoad(InputStream inputStream, char[] chars)
throws IOException, NoSuchAlgorithmException, CertificateException {
try {
Properties properties = new Properties();
if(inputStream != null){
properties.load(inputStream);
if (properties.size() == 0) {
throw new CertificateException(
"Properties file for configuration was empty?");
}
}else{
if(chars == null){
// keyStore.load(null,null) -> in memory only keystore
inMemoryOnly = true;
}
}
String defaultDirectoryString = properties
.getProperty(DEFAULT_DIRECTORY_KEY);
String directoryListString = properties
.getProperty(DIRECTORY_LIST_KEY);
String proxyFilename = properties.getProperty(PROXY_FILENAME);
String certFilename = properties.getProperty(CERTIFICATE_FILENAME);
String keyFilename = properties.getProperty(KEY_FILENAME);
initialize(defaultDirectoryString, directoryListString,
proxyFilename, certFilename, keyFilename);
} finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
logger.info("Error closing inputStream", e);
}
}
}
}
/**
* Initialize resources from filename, proxyfile name
*
* @param defaultDirectoryString
* Name of the default directory name as:
* "file: directory name"
* @param directoryListString
* @param proxyFilename
* @param certFilename
* @param keyFilename
*
* @throws IOException
* @throws CertificateException
*/
private void initialize(String defaultDirectoryString,
String directoryListString, String proxyFilename,
String certFilename, String keyFilename) throws IOException,
CertificateException {
if (defaultDirectoryString != null) {
defaultDirectory = new GlobusPathMatchingResourcePatternResolver().getResource(defaultDirectoryString).getFile();
if (!defaultDirectory.exists()) {
boolean directoryMade = defaultDirectory.mkdirs();
if (!directoryMade) {
throw new IOException(
"Unable to create default certificate directory");
}
}
loadDirectories(defaultDirectoryString);
}
if (directoryListString != null) {
loadDirectories(directoryListString);
}
try {
if (proxyFilename != null && proxyFilename.length() > 0) {
loadProxyCertificate(proxyFilename);
}
if ((certFilename != null && certFilename.length() > 0)
&& (keyFilename != null && keyFilename.length() > 0)) {
loadCertificateKey(certFilename, keyFilename);
}
} catch (ResourceStoreException e) {
throw new CertificateException(e);
} catch (CredentialException e) {
e.printStackTrace();
throw new CertificateException(e);
}
}
private void loadProxyCertificate(String proxyFilename)
throws ResourceStoreException {
if (proxyFilename == null) {
return;
}
proxyDelegate.loadWrappers(proxyFilename);
Map wrapperMap = proxyDelegate
.getWrapperMap();
for (ResourceProxyCredential credential : wrapperMap.values()) {
this.aliasObjectMap.put(proxyFilename, credential);
}
}
private void loadCertificateKey(String userCertFilename,
String userKeyFilename) throws CredentialException,
ResourceStoreException {
GlobusPathMatchingResourcePatternResolver resolver = new GlobusPathMatchingResourcePatternResolver();
if ((userCertFilename == null) || (userKeyFilename == null)) {
return;
}
// File certFile = new File(userCertFilename);
// File keyFile = new File(userKeyFilename);
GlobusResource certResource = resolver.getResource(userCertFilename);
GlobusResource keyResource = resolver.getResource(userKeyFilename);
CertKeyCredential credential = new CertKeyCredential(certResource,
keyResource);
// What do we name this alias?
String alias = userCertFilename + ":" + userKeyFilename;
this.aliasObjectMap.put(alias, credential);
}
private void loadDirectories(String directoryList)
throws CertificateException {
try {
caDelegate.loadWrappers(directoryList);
Map wrapperMap = caDelegate
.getWrapperMap();
Set knownCerts = new HashSet();
// The alias hashing merits explanation. Loading all the files in a directory triggers a
// deadlock bug for old jglobus clients if the directory contains repeated CAs (like the
// modern IGTF bundle does). So, we ignore the cert if the alias is incorrect or already seen.
// However, we track all the certs we ignore and load any that were completely ignored due to
// aliases. So, non-hashed directories will still work.
Map ignoredAlias = new HashMap();
Map ignoredAnchor = new HashMap();
Map ignoredCert = new HashMap();
for (ResourceTrustAnchor trustAnchor : wrapperMap.values()) {
String alias = trustAnchor.getResourceURL().toExternalForm();
TrustAnchor tmpTrustAnchor = trustAnchor.getTrustAnchor();
X509Certificate trustCert = tmpTrustAnchor.getTrustedCert();
String hash = CertificateIOUtil.nameHash(trustCert.getSubjectX500Principal());
if (this.aliasObjectMap == null) {
System.out.println("Alias Map Null");
}
boolean hash_in_alias = !alias.contains(hash);
if (knownCerts.contains(hash) || !hash_in_alias) {
if (!hash_in_alias) {
ignoredAlias.put(hash, alias);
ignoredAnchor.put(hash, trustAnchor);
ignoredCert.put(hash, trustCert);
}
continue;
}
knownCerts.add(hash);
this.aliasObjectMap.put(alias, trustAnchor);
certFilenameMap.put(trustCert, alias);
}
// Add any CA we skipped above.
for (String hash : ignoredAlias.keySet()) {
if (knownCerts.contains(hash)) {
continue;
}
String alias = ignoredAlias.get(hash);
this.aliasObjectMap.put(alias, ignoredAnchor.get(hash));
certFilenameMap.put(ignoredCert.get(hash), alias);
}
} catch (ResourceStoreException e) {
throw new CertificateException("",e);
}
}
/**
* Delete a security object from this keystore.
*
* @param s
* The alias of the object to delete.
* @throws KeyStoreException
*/
@Override
public void engineDeleteEntry(String s) throws KeyStoreException {
SecurityObjectWrapper> object = this.aliasObjectMap.remove(s);
if (object != null) {
if (object instanceof ResourceTrustAnchor) {
ResourceTrustAnchor descriptor = (ResourceTrustAnchor) object;
Certificate cert;
try {
cert = descriptor.getTrustAnchor().getTrustedCert();
} catch (ResourceStoreException e) {
throw new KeyStoreException(e);
}
this.certFilenameMap.remove(cert);
boolean success = descriptor.getFile().delete();
if (!success) {
// JGLOBUS-91 : warn? throw error?
logger.info("Unable to delete certificate");
}
} else if (object instanceof ResourceProxyCredential) {
ResourceProxyCredential proxy = (ResourceProxyCredential) object;
try {
proxy.getCredential();
} catch (ResourceStoreException e) {
throw new KeyStoreException(e);
}
boolean success = proxy.getFile().delete();
if (!success) {
// JGLOBUS-91 : warn? throw error?
logger.info("Unable to delete credential");
}
}
}
}
/**
* Get an enumertion of all of the aliases in this keystore.
*
* @return An enumeration of the aliases in this keystore.
*/
@Override
public Enumeration engineAliases() {
return Collections.enumeration(this.aliasObjectMap.keySet());
}
/**
* Add a new private key to the keystore.
*
* @param s
* The alias for the object.
* @param key
* The private key.
* @param chars
* The password.
* @param certificates
* The key's certificate chain.
* @throws KeyStoreException
*/
@Override
public void engineSetKeyEntry(String s, Key key, char[] chars,
Certificate[] certificates) throws KeyStoreException {
if (!(key instanceof PrivateKey)) {
throw new KeyStoreException("PrivateKey expected");
}
if (!(certificates instanceof X509Certificate[])) {
throw new KeyStoreException(
"Certificate chain of X509Certificate expected");
}
CredentialWrapper wrapper;
X509Credential credential = new X509Credential((PrivateKey) key,
(X509Certificate[]) certificates);
if (credential.isEncryptedKey()) {
wrapper = createCertKeyCredential(s, credential);
} else {
wrapper = createProxyCredential(s, credential);
}
storeWrapper(wrapper);
this.aliasObjectMap.put(wrapper.getAlias(), wrapper);
}
@SuppressWarnings("rawtypes")
private CredentialWrapper createProxyCredential(String s,
X509Credential credential) throws KeyStoreException {
CredentialWrapper wrapper;
CredentialWrapper proxyCredential = getKeyEntry(s);
File file;
if (proxyCredential != null
&& proxyCredential instanceof AbstractResourceSecurityWrapper) {
AbstractResourceSecurityWrapper proxyWrapper = (AbstractResourceSecurityWrapper) proxyCredential;
file = proxyWrapper.getFile();
} else {
// JGLOBUS-91 : should alias be file name? or generate?
file = new File(defaultDirectory, s + "-key.pem");
}
try {
wrapper = new ResourceProxyCredential(inMemoryOnly, new GlobusResource(file.getAbsolutePath()),
credential);
} catch (ResourceStoreException e) {
throw new KeyStoreException(e);
}
return wrapper;
}
private CredentialWrapper createCertKeyCredential(String s,
X509Credential credential) throws KeyStoreException {
GlobusResource certResource;
GlobusResource keyResource;
CredentialWrapper wrapper;
CredentialWrapper credentialWrapper = getKeyEntry(s);
if (credentialWrapper != null
&& credentialWrapper instanceof CertKeyCredential) {
CertKeyCredential certKeyCred = (CertKeyCredential) credentialWrapper;
certResource = certKeyCred.getCertificateFile();
keyResource = certKeyCred.getKeyFile();
} else {
certResource = new GlobusResource(new File(defaultDirectory, s
+ ".0").getAbsolutePath());
keyResource = new GlobusResource(new File(defaultDirectory, s
+ "-key.pem").getAbsolutePath());
}
try {
wrapper = new CertKeyCredential(certResource, keyResource,
credential);
} catch (ResourceStoreException e) {
throw new KeyStoreException(e);
}
return wrapper;
}
private void storeWrapper(CredentialWrapper wrapper)
throws KeyStoreException {
if(!inMemoryOnly){
try {
wrapper.store();
} catch (ResourceStoreException e) {
throw new KeyStoreException("Error storing credential", e);
}
}
}
/**
* currently unsupported.
*
* @param s
* The key's alias
* @param bytes
* The encoded private key.
* @param certificates
* The key's certificate chain.
* @throws KeyStoreException
*/
@Override
public void engineSetKeyEntry(String s, byte[] bytes,
Certificate[] certificates) throws KeyStoreException {
throw new UnsupportedOperationException();
// JGLOBUS-91
}
/**
* Does the specified alias exist in this keystore?
*
* @param s
* The alias.
* @return True if the alias refers to a security object in the keystore.
*/
@Override
public boolean engineContainsAlias(String s) {
return this.aliasObjectMap.containsKey(s);
}
/**
* Get the number of security objects stored in this keystore.
*
* @return The number of security objects.
*/
@Override
public int engineSize() {
return this.aliasObjectMap.size();
}
/**
* Does the supplied alias refer to a certificate in this keystore?
*
* @param s
* The alias.
* @return True if this store contains a certificate with the specified
* alias.
*/
@Override
public boolean engineIsCertificateEntry(String s) {
return getCertificateEntry(s) != null;
}
/**
* Add a certificate to the keystore.
*
* @param alias
* The certificate alias.
* @param certificate
* The certificate to store.
* @throws KeyStoreException
*/
@Override
public void engineSetCertificateEntry(String alias, Certificate certificate)
throws KeyStoreException {
if (!(certificate instanceof X509Certificate)) {
throw new KeyStoreException(
"Certificate must be instance of X509Certificate");
}
File file;
ResourceTrustAnchor trustAnchor = getCertificateEntry(alias);
if (trustAnchor != null) {
file = trustAnchor.getFile();
} else {
file = new File(defaultDirectory, alias);
}
X509Certificate x509Cert = (X509Certificate) certificate;
try {
if(!inMemoryOnly){
writeCertificate(x509Cert, file);
}
ResourceTrustAnchor anchor = new ResourceTrustAnchor(inMemoryOnly,
new GlobusResource(file.getAbsolutePath()), new TrustAnchor(x509Cert,
null));
this.aliasObjectMap.put(alias, anchor);
this.certFilenameMap.put(x509Cert, alias);
} catch (ResourceStoreException e) {
throw new KeyStoreException(e);
} catch (IOException e) {
throw new KeyStoreException(e);
} catch (CertificateEncodingException e) {
throw new KeyStoreException(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy