com.redhat.ceylon.common.config.Keystores Maven / Gradle / Ivy
package com.redhat.ceylon.common.config;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import com.redhat.ceylon.common.FileUtil;
/**
* Creates {@link Store}s based on {@code [keystore]} sections in the
* {@link CeylonConfig}
*/
public class Keystores {
private static final String SECTION_KEYSTORE = "keystore";
/**
* Configuration item the keystore file, if the particular implementation
* supports storage in a file.
*/
private static final String ITEM_FILE = "file";
private static final String DEFAULT_FILE = "keystore";
/**
* Configuration item for whether the itself Keystore is protected with a
* password, and/or whether the entry is protected with a password.
*/
private static final String ITEM_PROTECTION = "protection";
private static final String DEFAULT_PROTECTION = "both";// store|items
/**
* Note that the Java default Keystore "jks" is unable to proteced
* SecretKeys, so that is not a suiatable default.
*/
private static final String ITEM_STORE_TYPE = "store-type";
private static final String DEFAULT_STORE_TYPE = "jceks";
/**
* The provider of the Keystore
*/
private static final String ITEM_STORE_PROVIDER = "store-provider";
private static final String DEFAULT_STORE_PROVIDER = "SunJCE";
/** Configuration item for SecretKeyFatory algorithm used used for converting
* passwords into SecretKeys. This setting does not change how the
* Keystore protects that SecretKey.
*/
private static final String ITEM_KEY_FACTORY_ALGO = "key-factory-algo";
private static final String DEFAULT_KEY_FACTORY_ALGO = "PBEWithMD5AndDES";
/** Configuration item for SecretKeyFatory provider used for converting
* passwords into SecretKeys. This setting does not change how the
* Keystore protects that SecretKey.
*/
private static final String ITEM_KEY_FACTORY_PROVIDER = "key-factory-provider";
private static final String DEFAULT_KEY_FACTORY_PROVIDER = "SunJCE";
/* We need to ensure that multiple threads can't to update the keystore
* at the same time. We don't want to corrupt it. Using a class which
* should be loaded by the system classloader should be safe against
* multiple class loaders too.
*/
private static final Object MUTEX = KeyStore.class;
private final CeylonConfig config;
private String keystoreKey(String keystoreName, String itemName) {
return SECTION_KEYSTORE + (keystoreName != null ? "." + keystoreName : "") + "." + itemName;
}
private Keystores() {
this(CeylonConfig.get());
}
private Keystores(CeylonConfig config) {
this.config = config;
}
private static Keystores instance;
public static Keystores get() {
if (instance == null) {
instance = new Keystores();
}
return instance;
}
public static void set(Keystores keystores) {
instance = keystores;
}
public static Keystores withConfig(CeylonConfig config) {
return new Keystores(config);
}
/**
* Bean representing a {@code [keystore]} section.
*/
public class Store {
private String filename;
private String keyStoreType;
private String keyStoreProvider;
private String keyFactoryAlgorithm;
private String keyFactoryProvider;
private String protection;
public Store(String storeFile, String keyStoreType,
String keyStoreProvider, String keyFactoryAlgorithm,
String keyFactoryProvider, String protection) {
super();
this.filename = storeFile;
this.keyStoreType = keyStoreType;
this.keyStoreProvider = keyStoreProvider;
this.keyFactoryAlgorithm = keyFactoryAlgorithm;
this.keyFactoryProvider = keyFactoryProvider;
this.protection = protection;
}
/**
* Path to the keystore file, relative to {@code ~/.ceylon}.
* @return
*/
public String getFilename() {
return filename;
}
private File getStoreFile() {
return getFilename() != null ? new File(FileUtil.getUserDir(), getFilename()) : null;
}
/**
* If the keystore is configured with a file item, does this file exist?
*/
public boolean fileExists() {
File file = getStoreFile();
return file == null || file.exists();
}
/**
* The {@link KeyStore} type
*/
public String getKeyStoreType() {
return keyStoreType;
}
/**
* The {@link KeyStore} provider name
*/
public String getKeyStoreProvider() {
return keyStoreProvider;
}
/**
* The {@link SecretKeyFactory} algorithm name
*/
public String getKeyFactoryAlgorithm() {
return keyFactoryAlgorithm;
}
/**
* The {@link SecretKeyFactory} provider name
*/
public String getKeyFactoryProvider() {
return keyFactoryProvider;
}
public String getProtection() {
return protection;
}
private KeyStore loadKeyStore(char[] storePassword)
throws GeneralSecurityException, IOException {
KeyStore ks = KeyStore.getInstance(getKeyStoreType(), getKeyStoreProvider());
FileInputStream stream = fileExists() ? new FileInputStream(getStoreFile()) : null;
try {
ks.load(stream , storePassword);
} finally {
if (stream != null) {
stream.close();
}
}
return ks;
}
private void saveKeystore(KeyStore ks, char[] storePassword)
throws FileNotFoundException, KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException {
File file = getStoreFile();
if (file != null) {
FileOutputStream out = new FileOutputStream(file);
try {
ks.store(out, storePassword);
} finally {
out.close();
}
}
}
private String canonicalizeAlias(String keystoreAlias) {
return keystoreAlias.toLowerCase();
}
/**
* Gets the password with the given alias from this store
* @param keystoreAlias The alias in the keystore of this password to get
* @param storePassword The password for accessing the keystore
* @param entryPassword The password for accessing the entry
* @return The password, or null if the store doesn't contain a key
* for the given alias.
* @throws GeneralSecurityException
* @throws IOException
*/
public char[] getPassword(String keystoreAlias, char[] storePassword, char[] entryPassword)
throws GeneralSecurityException, IOException {
keystoreAlias = canonicalizeAlias(keystoreAlias);
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(getKeyFactoryAlgorithm(), getKeyFactoryProvider());
PBEKeySpec keySpec = null;
synchronized (MUTEX) {
KeyStore ks = loadKeyStore(storePassword);
KeyStore.SecretKeyEntry entry = (KeyStore.SecretKeyEntry)ks.getEntry(keystoreAlias, new KeyStore.PasswordProtection(entryPassword));
if (entry != null) {
keySpec = (PBEKeySpec)keyFac.getKeySpec(entry.getSecretKey(), PBEKeySpec.class);
}
}
return keySpec != null ? keySpec.getPassword() : null;
}
public char[] getPassword(String keystoreAlias, char[] accessPassword) throws GeneralSecurityException, IOException {
return getPassword(keystoreAlias, accessPassword, accessPassword);
}
/**
* Sets the password with the given alias in this store
* @param keystoreAlias The alias in the keystore of this password to get
* @param storePassword The password for accessing the keystore
* @param entryPassword The password for accessing the entry
* @param password The password
* @throws GeneralSecurityException
* @throws IOException
*/
public void setPassword(String keystoreAlias, char[] storePassword, char[] entryPassword, char[] password)
throws Exception {
keystoreAlias = canonicalizeAlias(keystoreAlias);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(
keyFactoryAlgorithm, keyFactoryProvider);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
KeyStore.SecretKeyEntry credentials = new KeyStore.SecretKeyEntry(pbeKey);
synchronized (MUTEX) {
KeyStore ks = loadKeyStore(storePassword);
ks.setEntry(keystoreAlias, credentials, entryPassword != null ? new KeyStore.PasswordProtection(entryPassword) : null);
saveKeystore(ks, storePassword);
}
}
public void setPassword(String keystoreAlias, char[] accessPassword, char[] password) throws Exception {
setPassword(keystoreAlias, accessPassword, accessPassword, password);
}
public void deletePassword(String keystoreAlias, char[] storePassword) throws Exception {
keystoreAlias = canonicalizeAlias(keystoreAlias);
synchronized (MUTEX) {
KeyStore ks = loadKeyStore(storePassword);
if (ks.containsAlias(keystoreAlias)) {
ks.deleteEntry(keystoreAlias);
}
saveKeystore(ks, storePassword);
}
}
}
/**
* Gets the given names {@code [keystore]} sections from the config
* @param keystore The name of the {@code [keystore]} section, or null for
* the default keystore
* @return The Store
*/
public Store getStore(String keystore) {
String file = config.getOption(keystoreKey(keystore, ITEM_FILE), DEFAULT_FILE);
String keyStoreType = config.getOption(keystoreKey(keystore, ITEM_STORE_TYPE), DEFAULT_STORE_TYPE);
String keyStoreProvider = config.getOption(keystoreKey(keystore, ITEM_STORE_PROVIDER), DEFAULT_STORE_PROVIDER);
String keyFactoryAlgorithm = config.getOption(keystoreKey(keystore, ITEM_KEY_FACTORY_ALGO), DEFAULT_KEY_FACTORY_ALGO);
String keyFactoryProvider = config.getOption(keystoreKey(keystore, ITEM_KEY_FACTORY_PROVIDER), DEFAULT_KEY_FACTORY_PROVIDER);
String protection = config.getOption(keystoreKey(keystore, ITEM_PROTECTION), DEFAULT_PROTECTION);
return new Store(file, keyStoreType, keyStoreProvider, keyFactoryAlgorithm, keyFactoryProvider, protection);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy