Java tutorial
/* * Adito * * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * This program 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 for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.adito.boot; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.Key; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.Principal; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.maverick.crypto.asn1.ASN1Sequence; import com.maverick.crypto.asn1.DERInputStream; import com.maverick.crypto.asn1.x509.X509CertificateStructure; import com.maverick.ssl.TrustedCACertStore; /** * <p> * Manages one or more keystores. Adito currently uses two keystores, one * for the SSL server certificate and one for storing all of the client * certificates. * </p> * * <p> * Before a keystore manager may be used, it must be registered using * {@link #registerKeyStore(String, String, boolean, String, KeyStoreType)} * * <p> * To obtain a keystore, use {@link #getInstance(String)}, passing the keystore * name. If the named keystore has not yet been initialised then it is created * and initialised, otherwise the last used instance is returned. * </p> * * <p> * The key store files are held and manipulated in the <i>conf</i> directory. * All key stores are also updated to the * {@link com.adito.boot.Repository} which is loaded at start up, * replacing all the files. * </p> * * @see com.adito.boot.Repository */ public class KeyStoreManager { // Public statics /** * The default key store name. Used to store Aditos own server * certificate */ public static final String DEFAULT_KEY_STORE = "default"; /** * Key store name for Server authentication certificates used to store * certificates to use to connect to other servers */ public static final String SERVER_AUTHENTICATION_CERTIFICATES_KEY_STORE = "serverAuthentication"; /** * Key Store name for Server certificates that are trusted by Adito * making outgoing connections to it. */ public static final String TRUSTED_SERVER_CERTIFICATES_KEY_STORE = "trustedServer"; /** * Default password used for storing the untrusted key */ public static final String DEFAULT_KEY_PASSWORD = "adito"; /** * Repository name */ public static final String KEYSTORE_REPOSITORY = "keystore"; // Private instance variables private boolean keyStoreExists; private Date keystoreLastModified; private KeyStore keyStore; private boolean keyStoreEmpty; private File keyStoreFile; private Throwable keyStoreException; private String keyStoreName; private KeyStoreType keyStoreType; private String bundle; private boolean removeable; private String storePassword; // Private statics final static Log log = LogFactory.getLog(KeyStoreManager.class); static String KEY_TOOL = SystemProperties.get("java.home") + File.separator + "bin" + File.separator + "keytool"; private static HashMap<String, KeyStoreManager> instances = new HashMap<String, KeyStoreManager>(); /** * Constant for jks keystore */ public static final KeyStoreType TYPE_JKS = new KeyStoreType("JKS", "jks"); /** * Constant for pkcs12 keystore */ public static final KeyStoreType TYPE_PKCS12 = new KeyStoreType("PKCS12", "p12"); private static final List keyStoreTypes = Arrays.asList(new KeyStoreType[] { TYPE_JKS, TYPE_PKCS12 }); /** * Constructor. Private to prevent direct instantiation * * @param keyStoreName name of key store * @param bundle bundle key from which to get key store messages (title, * description etc) * @param removeable admin may remove certificates manually in the key store * management page * @param storePassword the keystore password * @param type */ private KeyStoreManager(String keyStoreName, String bundle, boolean removeable, String storePassword, KeyStoreType type) { super(); this.keyStoreName = keyStoreName; this.bundle = bundle; this.removeable = removeable; this.storePassword = storePassword; this.keyStoreType = type; initKeyStoreFile(); // Make sure that this keystore is synchronized with the repository try { synchronizeWithRepository(); } catch (IOException ex) { log.error("The keystore could not be synchornized with the repository", ex); } } /** * Get an instance of a keystore manager given the keystore name * * @param keyStoreName * @return keyStore instance */ public static KeyStoreManager getInstance(String keyStoreName) { KeyStoreManager mgr = (KeyStoreManager) instances.get(keyStoreName); if (mgr == null) { throw new IllegalArgumentException("No keystore named " + keyStoreName); } return mgr; } /** * @return InputStream * @throws IOException */ public InputStream getInputStream() throws IOException { return RepositoryFactory.getRepository().getStore(KEYSTORE_REPOSITORY) .getEntryInputStream(keyStoreFile.getName()); } /** * Get a list of registered {@link KeyStoreManager}s. * * @return key stores */ public static List<KeyStoreManager> getKeyStores() { List<KeyStoreManager> keyStoreList = new ArrayList<KeyStoreManager>(); for (KeyStoreManager manager : instances.values()) { keyStoreList.add(manager); } return keyStoreList; } /** * Register a new keystore * * @param name name of keystore * @param bundle bundle for messages * @param removeable <code>true</code> if certificates may manually be * removed * @param storePassword key store password * @param type */ public static void registerKeyStore(String name, String bundle, boolean removeable, String storePassword, KeyStoreType type) { if (log.isInfoEnabled()) log.info("Registering keystore " + name); KeyStoreManager mgr = new KeyStoreManager(name, bundle, removeable, storePassword, type); instances.put(name, mgr); } /** * Set the new store password. Note, this only sets the password to use for * reading the key store. It does not change the password of the key store * itself. * * @param storePassword store password */ public void setStorePassword(String storePassword) { this.storePassword = storePassword; reloadKeystore(); } /** * Get if certificates are removeable by the administrator * * @return removeable */ public boolean getRemoveable() { return removeable; } /** * Get the bundle that contains messages for this key store * * @return bundle */ public String getBundle() { return bundle; } /** * Get the name that this key store was registered with * * @return name */ public String getName() { return keyStoreName; } /** * Get if the keystore currently exists * * @return keystore exists */ public boolean isKeyStoreExists() { try { checkKeyStore(); } catch (Exception e) { log.error("Could not determine if key store exists."); } return keyStoreExists; } /** * Get if the keystore is empty. * * @return keystore is empty */ public boolean isKeyStoreEmpty() { try { checkKeyStore(); } catch (Exception e) { log.error("Could not determine if key store exists."); } return keyStoreEmpty; } /** * Determine whether the certificate with the supplied alias is trusted or * not. * * @param alias certificiate name * @return trust certificate */ public boolean isCertificateTrusted(String alias) { try { checkKeyStore(); if (isKeyStoreExists() && !isKeyStoreEmpty()) { return doIsCertificateTrused(alias, keyStore); } } catch (Exception e) { log.error("Could not determine if certificate " + alias + " is trusted.", e); } return false; } /** * If there were any errors loading or iniatilising the keystore, this * method will a non-null exception object detailing the error. If the * keystore was loaded successfully then <code>null</code> will be * returned. * * @return exception caught whilst initialising the keystore */ public Throwable getKeyStoreException() { checkKeyStore(); return keyStoreException; } /** * Get the {@link KeyStore} this keystore manager is managing. * * @return keystore */ public KeyStore getKeyStore() { if (keyStore == null) this.reloadKeystore(); return keyStore; } /** * Get a certificate given its alias. * * @param alias certificate alias. * @return certificate */ public Certificate getCertificate(String alias) { try { checkKeyStore(); if (isKeyStoreExists() && !isKeyStoreEmpty()) { return keyStore.getCertificate(alias); } } catch (Exception e) { log.error("Could not get certificate with alias " + alias + ".", e); } return null; } /** * Return an enumeration of {@link String} objects aliases or * <code>null</code> if the key store is not loaded. * * @return enumeration of {@link Certificate} objects. */ public Enumeration getCertificateAliases() { checkKeyStore(); try { if (keyStore != null) { return keyStore.aliases(); } } catch (Exception e) { log.error("Could not get certificates.", e); } return null; } /** * Get the number of keys / certificates in this key store * * @return number of keys / certificates in this key store */ public int getSize() { checkKeyStore(); try { return keyStore != null ? keyStore.size() : 0; } catch (KeyStoreException e) { log.error("Failed to determine size of key store.", e); } return 0; } /** * Change the password used to encrypt this key store. * * @param oldPassword old password * @param password new password * @throws Exception on any error */ public void changeKeystorePassword(String oldPassword, String password) throws Exception { checkKeyStore(); if (!isKeyStoreExists()) { throw new Exception("Key store doesn't exists. Password cannot be changed."); } CommandRunner runner = null; try { Vector<String> v = new Vector<String>(); v.add(KEY_TOOL); v.add("-storepasswd"); v.add("-new"); v.add(password); v.add("-keystore"); v.add(getKeyStoreFile().getAbsolutePath()); v.add("-storepass"); v.add(oldPassword); runner = new CommandRunner(v); runner.runCommand(); this.storePassword = password; } catch (Exception e) { log.error("Failed to change keystore password.", e); throw new Exception(runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); } } /** * Get a key pair from this key store * * @param alias alias under which the pair is stored * @param password password protecting the keys if any * @return key pair */ public KeyPair getKeyPair(String alias, char[] password) { try { checkKeyStore(); if (isKeyStoreExists() && !isKeyStoreEmpty()) { Key key = keyStore.getKey(alias, password); if (key instanceof PrivateKey) { Certificate cert = keyStore.getCertificate(alias); PublicKey publicKey = cert.getPublicKey(); return new KeyPair(publicKey, (PrivateKey) key); } } } catch (Exception e) { log.error("Could not get key pair with alias " + alias + ".", e); } return null; } /** * Get a private key from this key store * * @param alias alias under which the key is stored * @param password password protecting the key if any * @return key */ public PrivateKey getPrivateKey(String alias, char[] password) { try { checkKeyStore(); if (isKeyStoreExists() && !isKeyStoreEmpty()) { return (PrivateKey) keyStore.getKey(alias, password); } } catch (Exception e) { log.error("Could not get private key with alias " + alias + ".", e); } return null; } /** * Get the chain of certificates from the specified alias up to the root CA * certificate * * @param alias alias * @return certificate chain */ public Certificate[] getCertificateChain(String alias) { Certificate[] chain = null; try { checkKeyStore(); if (isKeyStoreExists() && !isKeyStoreEmpty()) { chain = keyStore.getCertificateChain(alias); } } catch (Exception e) { log.error(e); } if (chain == null) { log.error("Could not get private key with alias " + alias + "."); } return chain; } /** * Utility method to extract an entity from a certificates subject DN * * @param c certificate * @param entity entity to extract * @return entity value */ public static String getX509CertificateEntity(X509Certificate c, String entity) { // This assumes the keystore returns the last certificate in the chain // the actual certifcate that is signed by a CA or untrusted cert Principal subjectPrincipal = c.getSubjectDN(); StringTokenizer t = new StringTokenizer(subjectPrincipal.getName(), ","); while (t.hasMoreTokens()) { String e = t.nextToken().trim(); String f = entity.trim() + "="; if (e.toLowerCase().startsWith(f.toLowerCase())) { return e.substring(f.length()).trim(); } } return ""; } /** * Reload the key store this manager is managing */ public void reloadKeystore() { keyStoreExists = false; keyStoreException = null; keyStoreEmpty = true; keyStore = null; try { File keystoreFile = getKeyStoreFile(); InputStream in = null; if (keystoreFile.exists() && keystoreFile.canRead()) { keyStoreExists = true; keyStoreException = null; keyStoreEmpty = true; keyStore = null; try { keyStore = KeyStore.getInstance(keyStoreType.getName()); String keystorePassword = getKeyStorePassword(); if (keystoreFile.length() != 0) { in = new FileInputStream(keystoreFile); keyStore.load(in, keystorePassword.toCharArray()); keyStoreEmpty = keyStore.size() == 0; } } finally { Util.closeStream(in); } } else { // No change } } catch (Exception e) { log.error("Failed to check key store.", e); keyStoreException = e; } } /** * Check the check store to see if it has been modified since it was last * loaded, loading it if it has changed */ public void checkKeyStore() { initKeyStoreFile(); try { File keystoreFile = getKeyStoreFile(); if (keystoreFile.exists() && keystoreFile.canRead()) { Date fileLastModified = new Date(keystoreFile.lastModified()); if (keystoreLastModified == null || !keystoreLastModified.equals(fileLastModified)) { keystoreLastModified = fileLastModified; reloadKeystore(); } else { // No change } } else { keyStore = null; keyStoreExists = false; keyStoreEmpty = true; keyStoreException = null; } } catch (Exception e) { log.error("Failed to check key store.", e); keyStoreException = e; } } /** * Import a key in PKCS12 key format * * @param keyFile file to import * @param password password for key * @param alias alias for key * @param newAlias * @throws Exception on any error * @return the alias of the key imported */ public String importPKCS12Key(File keyFile, String password, String alias, String newAlias) throws Exception { KeyStore kspkcs12 = KeyStore.getInstance("PKCS12"); kspkcs12.load(new FileInputStream(keyFile), password == null ? null : password.toCharArray()); boolean hasTemp = false; if (isKeyStoreEmpty()) { if (isKeyStoreExists()) { deleteKeyStore(); } createKeyStore(); String dname = "cn=tmp, ou=tmp, o=tmp, l=tmp, st=tmp, c=GB"; createKey("temporary-key", dname); hasTemp = true; reloadKeystore(); } try { String firstAlias = (String) kspkcs12.aliases().nextElement(); if (Util.isNullOrTrimmedBlank(alias)) { log.info("Alias not specified, importing first alias " + firstAlias); alias = firstAlias; } if (Util.isNullOrTrimmedBlank(newAlias)) { log.info("New alias not specified, using imported alias " + alias); newAlias = alias; } Certificate c[] = kspkcs12.getCertificateChain(alias); // Make sure we don't have a null chain if (c == null) c = new Certificate[] {}; Key key = kspkcs12.getKey(alias, password == null ? null : password.toCharArray()); if (key == null) { throw new Exception("No alias of '" + alias + "' in imported PKCS12 key file."); } this.keyStore.setKeyEntry(newAlias, key, getKeyStorePassword().toCharArray(), c); } finally { if (hasTemp || keyStore.containsAlias("temporary-key")) this.keyStore.deleteEntry("temporary-key"); OutputStream out = null; try { out = new FileOutputStream(keyStoreFile.getAbsolutePath()); getKeyStore().store(out, getKeyStorePassword().toCharArray()); } finally { Util.closeStream(out); } updateRepository(false); } return newAlias; } /** * Get the key store file this manager is managing * * @return file */ public File getKeyStoreFile() { return keyStoreFile; } /** * Create a new private key given an alias an DN. Note that the * DN will be escaped as required by RFC2253 * * @param alias alias * @param dname DN * @throws Exception on any error */ public void createKey(String alias, String dname) throws Exception { checkKeyStore(); if (!isKeyStoreExists()) { throw new Exception("Key store doesn't exists. Key cannot be created."); } /* * Because an empty keystore file is not valid, delete the key first * then let genkey create a new keystore */ if (isKeyStoreEmpty()) { if (!getKeyStoreFile().delete()) { throw new Exception("Could not delete key store."); } } CommandRunner runner = null; try { String keyStorePassword = getKeyStorePassword(); Vector<String> v = new Vector<String>(); v.add(KEY_TOOL); v.add("-genkey"); v.add("-alias"); v.add(alias); v.add("-keyalg"); v.add("RSA"); v.add("-keystore"); v.add(keyStoreFile.getAbsolutePath()); v.add("-dname"); v.add(dname); v.add("-storetype"); v.add(keyStoreType.getName()); v.add("-storepass"); v.add(keyStorePassword); v.add("-keypass"); v.add(keyStorePassword); v.add("-validity"); v.add("365"); runner = new CommandRunner(v); runner.runCommand(); updateRepository(false); } catch (Exception e) { log.error("Failed to create key.", e); throw new Exception(runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); } } /** * Import a certificate from a file and store with the specified a alias. * File must be X509 and Base 64 or DER encoded. * * @param alias alias to store cert. under * @param certFile file contain certificate * @param keyPass key password or <code>null</code> for default * @throws Exception on any error */ public void importCert(String alias, File certFile, String keyPass) throws Exception { checkKeyStore(); if (!isKeyStoreExists()) { createKeyStore(); } /* * Because an empty keystore file is not valid, delete the key first * then let genkey create a new keystore */ if (isKeyStoreEmpty()) { if (!getKeyStoreFile().delete()) { throw new Exception("Could not delete key store."); } } CommandRunner runner = null; try { if (log.isInfoEnabled()) log.info("Importing certificate for " + alias + " from " + certFile.getAbsolutePath()); String keyPassword = getKeyStorePassword(); Vector<String> v = new Vector<String>(); v.add(KEY_TOOL); v.add("-import"); v.add("-trustcacerts"); v.add("-noprompt"); v.add("-file"); v.add(certFile.getAbsolutePath()); v.add("-alias"); v.add(alias); v.add("-keystore"); v.add(keyStoreFile.getAbsolutePath()); v.add("-storepass"); v.add(keyPassword); v.add("-keypass"); v.add(keyPass == null ? DEFAULT_KEY_PASSWORD : keyPass); v.add("-storetype"); v.add(keyStoreType.getName().toLowerCase()); runner = new CommandRunner(v); runner.runCommand(); updateRepository(false); } catch (Exception e) { log.error("Failed to import certficate.", e); throw new Exception(runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); } if (log.isInfoEnabled()) log.info("Certificate for " + alias + " imported from " + certFile.getAbsolutePath()); } /** * Generate a certificate sigining request for the key with the specfied * alias. * * @param alias alias to generate CSR for * @param keyPass * @return CSR as a string * @throws Exception on any error */ public String generateCSR(String alias, String keyPass) throws Exception { checkKeyStore(); if (!isKeyStoreExists()) { throw new Exception("Key store doesn't exists. CSR cannot be generated."); } CommandRunner runner = null; InputStream in = null; try { String keyPassword = getKeyStorePassword(); Vector<String> v = new Vector<String>(); v.add(KEY_TOOL); v.add("-certreq"); v.add("-alias"); v.add(alias); v.add("-keyalg"); v.add("RSA"); v.add("-keystore"); v.add(keyStoreFile.getAbsolutePath()); v.add("-storepass"); v.add(keyPassword); v.add("-file"); File csrFile = new File(ContextHolder.getContext().getConfDirectory(), "server-certificate.csr"); v.add(csrFile.getAbsolutePath()); v.add("-keypass"); v.add(keyPass == null ? DEFAULT_KEY_PASSWORD : keyPass); runner = new CommandRunner(v); runner.runCommand(); in = new FileInputStream(csrFile); return Util.loadStreamToString(in, null); } catch (Exception e) { log.error("Failed to create key.", e); throw new Exception(runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); } finally { Util.closeStream(in); } } /** * Create a new key store. * <p> * We dont actually create a keystore, we just create a zero length file as * there doesnt seem to be a way of creating an empty keystore using * keytool. * * @throws IOException on any error */ public void createKeyStore() throws IOException { if (isKeyStoreExists()) { throw new IOException("Key store already exists."); } FileOutputStream out = null; try { out = new FileOutputStream(getKeyStoreFile()); } finally { Util.closeStream(out); } } /** * Delete the key store. * * @throws IOException */ public void deleteKeyStore() throws IOException { if (!isKeyStoreExists()) { throw new IOException("Key store does not exist."); } if (!getKeyStoreFile().delete()) { throw new IOException("Failed to delete " + getKeyStoreFile().getAbsolutePath() + "."); } updateRepository(true); } /** * Delete a certificate from the key store given its alias. * * @param alias alias to remove * @throws Exception on any error */ public void deleteCertificate(String alias) throws Exception { checkKeyStore(); if (!isKeyStoreExists()) { throw new Exception("Key store doesn't exists. Certificate cannot be deleted."); } CommandRunner runner = null; try { if (log.isInfoEnabled()) log.info("Deleting certificate for " + alias); String keyPassword = getKeyStorePassword(); Vector<String> v = new Vector<String>(); v.add(KEY_TOOL); v.add("-delete"); v.add("-alias"); v.add(alias); v.add("-keystore"); v.add(keyStoreFile.getAbsolutePath()); v.add("-storepass"); v.add(keyPassword); runner = new CommandRunner(v); runner.runCommand(); updateRepository(false); } catch (Exception e) { log.error("Failed to delete certificate.", e); throw new Exception(runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); } if (log.isInfoEnabled()) log.info("Deleted certificate for " + alias); } /** * Get a {@link KeyStoreType} given its name. * * @param name key store type name * @return key store type */ public static KeyStoreType getKeyStoreType(String name) { for (Iterator i = keyStoreTypes.iterator(); i.hasNext();) { KeyStoreType t = (KeyStoreType) i.next(); if (t.getName().equals(name)) { return t; } } return null; } /** * Get a list of supported {@link KeyStoreType} objects. * * @return list of support key store types */ public List getSupportedKeyStoreTypes() { return keyStoreTypes; } /** * Set the key store type for this key store manager. * * @param keyStoreType key store type */ public void setKeyStoreType(KeyStoreType keyStoreType) { this.keyStoreType = keyStoreType; initKeyStoreFile(); } /** * Get the key store type for this key store manager. * * @return key store type */ public KeyStoreType getKeyStoreType() { return keyStoreType; } // Supporting methods void initKeyStoreFile() { this.keyStoreFile = new File(ContextHolder.getContext().getConfDirectory(), keyStoreName + ".keystore." + keyStoreType.getExtension()); } void synchronizeWithRepository() throws IOException { RepositoryStore store = RepositoryFactory.getRepository().getStore(KEYSTORE_REPOSITORY); if (!store.hasEntry(keyStoreFile.getName())) { keyStoreFile.createNewFile(); } else { InputStream in = null; OutputStream out = null; try { in = store.getEntryInputStream(keyStoreFile.getName()); out = new FileOutputStream(keyStoreFile); Util.copy(in, out); } finally { Util.closeStream(in); Util.closeStream(out); } } } void updateRepository(boolean remove) throws IOException { RepositoryStore store = RepositoryFactory.getRepository().getStore(KEYSTORE_REPOSITORY); if (remove) { store.removeEntry(keyStoreFile.getName()); } else { OutputStream out = null; InputStream in = null; try { out = store.getEntryOutputStream(keyStoreFile.getName()); in = new FileInputStream(keyStoreFile); Util.copy(in, out); } finally { Util.closeStream(in); Util.closeStream(out); } } } /** * Get the key store passwords * * @return keystore password * @throws Exception */ public String getKeyStorePassword() throws Exception { return storePassword; } boolean doIsCertificateTrused(String alias, KeyStore keyStore) throws Exception { Certificate[] certs = keyStore.getCertificateChain(alias); // try { // ((CustomSSLSocketFactory)CustomSSLSocketFactory.getDefault()).checkServerTrusted((X509Certificate[])certs, ""); // return true; // } catch(CertificateException ex) { if (certs == null) { if (log.isInfoEnabled()) log.info("No certs for " + alias + ", untrusted."); } else if (certs.length > 1) { X509Certificate x509cert = (X509Certificate) certs[certs.length - 1]; TrustedCACertStore store = new TrustedCACertStore(); ByteArrayInputStream bin = new ByteArrayInputStream(x509cert.getEncoded()); DERInputStream der = null; try { der = new DERInputStream(bin); ASN1Sequence certificate = (ASN1Sequence) der.readObject(); com.maverick.crypto.asn1.x509.X509Certificate x509 = new com.maverick.crypto.asn1.x509.X509Certificate( X509CertificateStructure.getInstance(certificate)); return store.isTrustedCertificate(x509, false, false); } finally { Util.closeStream(der); } } // } return false; } String parseKeytoolOutput(String output) { if (output.startsWith("keytool error: ")) { int idx = output.indexOf(':', 14); if (idx != -1) { output = output.substring(idx + 1); } } return output; } /** * Deregister a keystore * * @param name name of keystore */ public static void deregisterKeyStore(String name) { if (log.isInfoEnabled()) log.info("Deregistering keystore " + name); instances.remove(name); } }