http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/3ecb1291/taverna-credential-manager-impl/src/main/java/net/sf/taverna/t2/security/credentialmanager/impl/CredentialManagerImpl.java ---------------------------------------------------------------------- diff --git a/taverna-credential-manager-impl/src/main/java/net/sf/taverna/t2/security/credentialmanager/impl/CredentialManagerImpl.java b/taverna-credential-manager-impl/src/main/java/net/sf/taverna/t2/security/credentialmanager/impl/CredentialManagerImpl.java deleted file mode 100644 index 3d66c1a..0000000 --- a/taverna-credential-manager-impl/src/main/java/net/sf/taverna/t2/security/credentialmanager/impl/CredentialManagerImpl.java +++ /dev/null @@ -1,2657 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2008-2014 The University of Manchester - * - * Modifications to the initial code base are copyright of their - * respective authors, or their employers as appropriate. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - ******************************************************************************/ -package net.sf.taverna.t2.security.credentialmanager.impl; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Method; -import java.math.BigInteger; -import java.net.Authenticator; -import java.net.Socket; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.Key; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.SecureRandom; -import java.security.Security; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; -import static javax.security.auth.x500.X500Principal.RFC2253; -import net.sf.taverna.t2.lang.observer.MultiCaster; -import net.sf.taverna.t2.lang.observer.Observable; -import net.sf.taverna.t2.lang.observer.Observer; -import net.sf.taverna.t2.security.credentialmanager.CMException; -import net.sf.taverna.t2.security.credentialmanager.CredentialManager; -import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.KEYSTORE; -import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.TRUSTSTORE; -import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; -import net.sf.taverna.t2.security.credentialmanager.JavaTruststorePasswordProvider; -import net.sf.taverna.t2.security.credentialmanager.KeystoreChangedEvent; -import net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider; -import net.sf.taverna.t2.security.credentialmanager.ParsedDistinguishedName; -import net.sf.taverna.t2.security.credentialmanager.ServiceUsernameAndPasswordProvider; -import net.sf.taverna.t2.security.credentialmanager.TrustConfirmationProvider; -import net.sf.taverna.t2.security.credentialmanager.UsernamePassword; -import org.apache.commons.io.FileUtils; -import org.apache.log4j.Logger; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import uk.org.taverna.configuration.app.ApplicationConfiguration; - -/** - * Provides an implementation of {@link #CredentialManagerService}. - * - * @author Alex Nenadic - * @author Stian Soiland-Reyes - */ - -public class CredentialManagerImpl implements CredentialManager, - Observable<KeystoreChangedEvent> { - /** Various passwords to try for the Java's default truststore. */ - public static List<String> defaultTrustStorePasswords = Arrays.asList( - System.getProperty(PROPERTY_TRUSTSTORE_PASSWORD, ""), "changeit", - "changeme", ""); - - // For Taverna 2.2 and older - Keystore was BC-type with user-set password - // and Truststore was JKS-type with the default password - public static final String OLD_TRUSTSTORE_PASSWORD = "Tu/Ap%2_$dJt6*+Rca9v"; - public static final String OLD_T2TRUSTSTORE_FILE = "t2truststore.jks"; - - private static Logger logger = Logger - .getLogger(CredentialManagerImpl.class); - - // Multicaster of KeystoreChangedEventS - private MultiCaster<KeystoreChangedEvent> multiCaster = new MultiCaster<>( - this); - - /** - * A directory containing Credential Manager's Keystore/Truststore/etc. - * files. - */ - private File credentialManagerDirectory = null; - - /** - * Master password for Credential Manager - used to create/access the - * Keystore and Truststore. - */ - private String masterPassword; - - // Keystore file - private File keystoreFile = null; - - // Truststore file - private File truststoreFile = null; - - /** - * Keystore containing user's passwords and private keys with corresponding - * public key certificate chains. - */ - private KeyStore keystore; - - /** - * Truststore containing trusted certificates of CA authorities and services - * (servers). - */ - private KeyStore truststore; - - /** - * Has the Credential Manager been initialized (i.e. the Keystore/Truststore - * loaded, etc.) - */ - private boolean isInitialized = false; - - /* - * Whether SSLSocketFactory has been initialised with Taverna's - * Keystore/Truststore. Actually tavernaSSLSocketFactory==null? check tells - * us if Taverna's SSLSocketFactory has been initialised - */ - // private static boolean sslInitialized = false; - - private static SSLSocketFactory tavernaSSLSocketFactory; - - /** - * Observer of changes to the Keystore and Truststore that updates the - * default SSLContext and SSLSocketFactory at the single location rather - * than all over the code when changes to the keystores occur. - */ - private KeystoreChangedObserver keystoresChangedObserver = new KeystoreChangedObserver(); - - /** - * Cached list of all services that have a username/password entry in the - * Keystore - */ - private List<URI> cachedServiceURIsList = null; - - /** - * Cached map of all URI fragments to their original URIs for services that - * have a username/password entry in the Keystore. This is normally used to - * recursively discover the realm of the service for HTTP authentication so - * we do not have to ask user for their username and password for every - * service in the same realm. - */ - private HashMap<URI, URI> cachedServiceURIsMap = null; - - // Observer that clears the above list and map on any change to the Keystore - private ClearCachedServiceURIsObserver clearCachedServiceURIsObserver = new ClearCachedServiceURIsObserver(); - - /** A list of master password providers */ - private List<MasterPasswordProvider> masterPasswordProviders; - - /** - * A list of Java truststore password (used to encrypt/decrypt the Java's - * default truststore) providers - */ - private List<JavaTruststorePasswordProvider> javaTruststorePasswordProviders; - - /** A list of providers of usernames and passwords for services */ - private List<ServiceUsernameAndPasswordProvider> serviceUsernameAndPasswordProviders; - - /** A list of providers of trust confirmation for services */ - private List<TrustConfirmationProvider> trustConfirmationProviders; - - private ApplicationConfiguration applicationConfiguration; - - private File certificatesRevokedIndicatorFile; - - private DistinguishedNameParser dnParser = new DistinguishedNameParserImpl(); - - /** - * Return an array of URLs for 'special' trusted CAs' certificates contained in - * the resources folder that need to be loaded into Truststore, so that we can establish trust - * into services such as BioCatalogue, BiodiversityCatalogue, heater, etc. by default. - * */ - private static List<URL> getSpecialTrustedCertificates() { - List<URL> urls = new ArrayList<>(); - Class<?> c = CredentialManager.class; - urls.add(c.getResource("/trusted-certificates/TERENASSLCA.crt")); - urls.add(c.getResource("/trusted-certificates/UTNAddTrustServer_CA.crt")); - urls.add(c.getResource("/trusted-certificates/AddTrustExternalCARoot.crt")); - return urls; - } - - /** - * Connects this credential manager to the Java {@linkplain Authenticator HTTP authenticator mechanism}. - */ - public void installAuthenticator() { - Authenticator.setDefault(new CredentialManagerAuthenticator(this)); - } - - public void deleteRevokedCertificates(){ - - if (truststore != null){ - // Delete the old revoked or unnecessary BioCatalogue, - // BiodiversityCatalogue and heater's certificates, if present - - if (certificatesRevokedIndicatorFile == null){ - certificatesRevokedIndicatorFile = new File(credentialManagerDirectory, CERTIFICATES_REVOKED_INDICATOR_FILE_NAME); - } - - if (!certificatesRevokedIndicatorFile.exists()) { - - List<URL> certURLsToDelete = new ArrayList<>(); - Class<?> c = CredentialManager.class; - certURLsToDelete.add(c.getResource("/trusted-certificates/www.biocatalogue.org-revoked.pem")); - certURLsToDelete.add(c.getResource("/trusted-certificates/www.biodiversitycatalogue.org-revoked.pem")); - certURLsToDelete.add(c.getResource("/trusted-certificates/heater.cs.man.ac.uk-not-needed.pem")); - - for (URL certURLToDelete : certURLsToDelete){ - try (InputStream certStreamToDelete = certURLToDelete.openStream()) { - // We know there will be only one cert in the chain - CertificateFactory cf = CertificateFactory - .getInstance("X.509"); - Certificate certToDelete = cf.generateCertificates(certStreamToDelete).toArray(new Certificate[0])[0]; - String aliasToDelete = truststore - .getCertificateAlias(certToDelete); - if (aliasToDelete != null) { - truststore.deleteEntry(aliasToDelete); - logger.warn("Deleting revoked/unnecessary certificate " - + aliasToDelete); - } - } catch (Exception ex) { - logger.info("Can't delete revoked certificate " + certURLToDelete, ex); - } - } - - // Touch the file - try { - FileUtils - .touch(certificatesRevokedIndicatorFile); - } catch (IOException ioex) { - // Hmmm, ignore this? - logger.error("Failed to touch " + certificatesRevokedIndicatorFile.getAbsolutePath(), ioex); - } - } - - //Save changes - try{ - FileOutputStream fos = new FileOutputStream(truststoreFile); - truststore.store(fos, masterPassword.toCharArray()); - } - catch(Exception ex){ - String exMessage = "Failed to save Truststore after deleting revoked certificates."; - logger.error(exMessage, ex); - } - } - } - - public CredentialManagerImpl() throws CMException { - /* - * Make sure we have BouncyCastle provider installed, just in case - * (needed for some tests and reading PKCS#12 keystores) - */ - Security.addProvider(new BouncyCastleProvider()); - - /* - * Open the files stored in the (DEFAULT!!!) Credential Manager's - * directory - */ - // loadDefaultConfigurationFiles(); - // FIXME - /* - * Get the location of the directory containing the Keystore and - * Truststore somehow - from OSGi's Configuration Service - */ - - // initialize(); - } - - /** - * Initialize Credential Manager - load the Keystore and Truststore. - */ - private void initialize() throws CMException { - /* - * Only do this if the Credential Manager has not been initialized so - * far - */ - if (!isInitialized) { - masterPassword = getMasterPassword(); - - this.addObserver(clearCachedServiceURIsObserver); - this.addObserver(keystoresChangedObserver); - - // Load the Keystore - try { - loadKeystore(); - logger.info("loaded the Keystore"); - } catch (CMException cme) { - isInitialized = false; - masterPassword = null; // just in case we need to try again - // logger.error(cme.getMessage(), cme); - throw cme; - } - - // Load the Truststore - try { - loadTruststore(); - logger.info("loaded the Truststore"); - } catch (CMException cme) { - isInitialized = false; - masterPassword = null; // just in case we need to try again - // logger.error(cme.getMessage(), cme); - throw cme; - } - - isInitialized = true; - } - } - - /** - * Get the master password from the available providers. - * - * @return master password - * @throws CMException - * if none of the providers can provide a non-null master - * password - */ - private String getMasterPassword() throws CMException { - if (masterPassword != null) - return masterPassword; - - if (keystoreFile == null) - loadDefaultSecurityFiles(); - - boolean firstTime = !keystoreFile.exists(); - - /** - * Master password providers are already sorted by their priority by the - * OSGi framework - */ - for (MasterPasswordProvider masterPasswordProvider : masterPasswordProviders) { - // FIXME how to handle default password providers!? - String password = masterPasswordProvider - .getMasterPassword(firstTime); - if (password != null) - return password; - } - - /* - * We are in big trouble - we do not have a single master password - * provider. - */ - String exMessage = "Failed to obtain master password from providers: " - + masterPasswordProviders; - logger.error(exMessage); - throw new CMException(exMessage); - } - - /** - * Load Taverna's Keystore from a file on the disk. - */ - private void loadKeystore() throws CMException { - if (keystore == null) { - try { - // Try to create Taverna's Keystore as Bouncy Castle UBER-type - // keystore. - keystore = KeyStore.getInstance("UBER", "BC"); - } catch (Exception ex) { - // The requested keystore type is not available from security - // providers. - throw new CMException("Failed to instantiate Taverna's Keystore.", ex); - } - - if (keystoreFile.exists()) { // If the file exists, open it - // Try to load the Keystore - try (FileInputStream fis = new FileInputStream(keystoreFile)) { - // Load the Keystore from the file - keystore.load(fis, masterPassword.toCharArray()); - } catch (Exception ex) { - keystore = null; // make it null as it was just created but - // failed to load so it is not null - masterPassword = null; // it is probably the wrong password - // so do not remember it just in - // case - String exMessage = "Failed to load Taverna's Keystore from " - + keystoreFile.getAbsolutePath() - + ". Possible reason: incorrect password or corrupted file."; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } else { - // Otherwise create a new empty Keystore - try (FileOutputStream fos = new FileOutputStream(keystoreFile)) { - keystore.load(null, null); - // Immediately save the new (empty) Keystore to the file - keystore.store(fos, masterPassword.toCharArray()); - } catch (Exception ex) { - String exMessage = "Failed to generate a new empty Keystore."; - // logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /* - * Taverna distro for MAC contains info.plist file with some Java - * system properties set to use the Keychain which clashes with what - * we are setting here so we need to clear them - */ - System.clearProperty(PROPERTY_KEYSTORE_TYPE); - System.clearProperty(PROPERTY_KEYSTORE_PROVIDER); - - /* - * Not quite sure why we still need to set these two properties - * since we are creating our own SSLSocketFactory with our own - * KeyManager that uses Taverna's Keystore, but seem like after - * Taverna starts up and the first time it needs SSLSocketFactory - * for HTTPS connection it is still using the default Java's - * keystore unless these properties are set. Set the system property - * "javax.net.ssl.keystore" to use Taverna's keystore. - * - * Axis 1 likes reading from these properties but seems to work as - * well with Taverna's SSLSocetFactory as well. We do not want to - * expose these as they can be read from Beanshells. - */ - // System.setProperty(PROPERTY_KEYSTORE, keystoreFile.getAbsolutePath()); - // System.setProperty(PROPERTY_KEYSTORE_PASSWORD, masterPassword); - System.clearProperty(PROPERTY_KEYSTORE); - System.clearProperty(PROPERTY_KEYSTORE_PASSWORD); - } - } - - /** - * Load Taverna's Truststore from a file on a disk. If the Truststore does - * not already exist, a new empty one will be created and contents of Java's - * truststore located in <JAVA_HOME>/lib/security/cacerts will be copied - * over to the Truststore. - */ - private void loadTruststore() throws CMException { - if (truststore == null) { - try { - // Try to create Taverna's Truststore as Bouncy Castle UBER-type - // keystore. - truststore = KeyStore.getInstance("UBER", "BC"); - } catch (Exception ex) { - // The requested keystore type is not available from security - // providers. - throw new CMException("Failed to instantiate Taverna's Truststore", ex); - } - - if (truststoreFile.exists()) { - // If the Truststore file already exists, open it and load the - // Truststore - try (FileInputStream fis = new FileInputStream(truststoreFile)) { - // Load the Truststore from the file - truststore.load(fis, masterPassword.toCharArray()); - - // Delete the old revoked or unnecessary BioCatalogue, - // BiodiversityCatalogue and heater's certificates, if present - deleteRevokedCertificates(); - - } catch (Exception ex) { - /* Clear out things that are useless/hindering now */ - truststore = null; - masterPassword = null; - String exMessage = "Failed to load Taverna's Truststore from " - + truststoreFile.getAbsolutePath() - + ". Possible reason: incorrect password or corrupted file."; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } else { - /* - * Otherwise create a new empty Truststore and load it with - * certs from Java's truststore. - */ - File javaTruststoreFile = new File( - System.getProperty("java.home"), "lib/security/cacerts"); - KeyStore javaTruststore = null; - - // Java's truststore is of type "JKS" - try to load it - try { - javaTruststore = KeyStore.getInstance("JKS"); - } catch (Exception ex) { - // The requested keystore type is not available from the - // provider - throw new CMException("Failed to instantiate a 'JKS'-type keystore " - + "for reading Java's truststore.", ex); - } - - boolean loadedJavaTruststore = false; - /* - * Load Java's truststore from the file - try with the default - * Java truststore passwords. - */ - for (String password : defaultTrustStorePasswords) { - logger.info("Trying to load Java truststore using password: " - + password); - try (FileInputStream fis = new FileInputStream( - javaTruststoreFile)) { - javaTruststore.load(fis, password.toCharArray()); - loadedJavaTruststore = true; - break; - } catch (IOException ioex) { - /* - * If there is an I/O or format problem with the - * keystore data, or if the given password was incorrect - * (Thank you Sun, now I can't know if it is the file or - * the password..) - */ - logger.info(String - .format("Failed to load the Java truststore to copy " - + "over certificates using default password: " - + "%s from %s", password, - javaTruststoreFile)); - } catch (NoSuchAlgorithmException e) { - logger.error("Unknown encryption algorithm " - + "while loading Java truststore from " - + javaTruststoreFile, e); - break; - } catch (CertificateException e) { - logger.error("Certificate error while " - + "loading Java truststore from " - + javaTruststoreFile, e); - break; - } - } - - /* - * Default Java truststore passwords failed - possibly the user - * has changed it. Ask the Java truststore password providers if - * they can help - this will typically pop up a dialog to ask - * the user if we are in a graphical environment. If not, we - * will simply not copy the default truststore certificates into - * Credential Manager's Truststore. - */ - if (!loadedJavaTruststore) - if (!(loadJavaTruststoreUsingPasswordProviders( - javaTruststore, javaTruststoreFile))) { - String error = "Credential manager failed to load" - + " certificates from Java's truststore."; - String help = "Try using the system property -D" - + PROPERTY_TRUSTSTORE_PASSWORD - + "=TheTrustStorePassword"; - logger.error(error + " " + help); - // FIXME Writes to standard error! - System.err.println(error); - System.err.println(help); - } - - // Create a new empty Truststore for Taverna - try (FileOutputStream fos = new FileOutputStream(truststoreFile)) { - truststore.load(null, null); - if (loadedJavaTruststore) { - // Copy certificates into Taverna's Truststore from - // Java's truststore. - Enumeration<String> aliases = javaTruststore.aliases(); - while (aliases.hasMoreElements()) { - Certificate certificate = javaTruststore - .getCertificate(aliases.nextElement()); - if (certificate instanceof X509Certificate) - truststore - .setCertificateEntry( - createTrustedCertificateAlias((X509Certificate) certificate), - certificate); - } - } - - // Insert special trusted CA certificates - logger.info("Loading certificates of trusted CAs so as to establish trust into our services such as BioCatalogue, BiodiversityCatalogue, heater, etc."); - CertificateFactory cf = CertificateFactory - .getInstance("X.509"); - for (URL trustedCertURL : getSpecialTrustedCertificates()) - // Load the certificate (possibly a chain) from the - // stream - try (InputStream stream = trustedCertURL.openStream()) { - for (Certificate c : cf - .generateCertificates(stream)) - truststore - .setCertificateEntry( - createTrustedCertificateAlias((X509Certificate) c), - c); - } catch (Exception cex) { - logger.error("Failed to insert trusted certificate entry in the Truststore", cex); - } - - // Immediately save the new Truststore to the file - truststore.store(fos, masterPassword.toCharArray()); - } catch (Exception ex) { - /* - * make truststore null as it was just created but failed to - * save so we should retry next time - */ - truststore = null; - throw new CMException("Failed to generate new empty Taverna's Truststore", ex); - } - } - - /* - * Taverna distro for MAC contains info.plist file with some Java - * system properties set to use the Keychain which clashes with what - * we are setting here so we need to clear them. - */ - System.clearProperty(PROPERTY_TRUSTSTORE_TYPE); - System.clearProperty(PROPERTY_TRUSTSTORE_PROVIDER); - - /* - * Not quite sure why we still need to set these two properties - * since we are creating our own SSLSocketFactory with our own - * TrustManager that uses Taverna's Truststore, but seem like after - * Taverna starts up and the first time it needs SSLSocketFactory - * for HTTPS connection it is still using the default Java's - * truststore unless these properties are set. Set the system - * property "javax.net.ssl.Truststore" to use Taverna's truststore. - */ - - /* - * Axis 1 likes reading from these properties but seems to work as - * well with Taverna's SSLSocetFactory as well. We do not want to - * expose these as they can be read from Beanshells. - */ - // System.setProperty(PROPERTY_TRUSTSTORE, truststoreFile.getAbsolutePath()); - // System.setProperty(PROPERTY_TRUSTSTORE_PASSWORD, masterPassword); - System.clearProperty(PROPERTY_TRUSTSTORE); - System.clearProperty(PROPERTY_TRUSTSTORE_PASSWORD); - } - } - - /** - * Load the given keystore (which is Java's default truststore) from the - * given file (pointing to the Java's default truststore) using the - * {@link JavaTruststorePasswordProvider}s lookup to obtain the password for - * the keytore. - * - * @param javaTruststore - * Java's default truststore - * @param javaTruststoreFile - * Java's default truststore file - * @return true if managed to load the keystore using the provided - * passwords; false otherwise - */ - private boolean loadJavaTruststoreUsingPasswordProviders( - KeyStore javaTruststore, File javaTruststoreFile) { - String javaTruststorePassword = null; - for (JavaTruststorePasswordProvider provider : javaTruststorePasswordProviders) { - javaTruststorePassword = provider.getJavaTruststorePassword(); - if (javaTruststorePassword == null) - continue; - try (FileInputStream fis = new FileInputStream(javaTruststoreFile)) { - javaTruststore.load(fis, javaTruststorePassword.toCharArray()); - return true; - } catch (Exception ex) { - String exMessage = "Failed to load the Java truststore to copy over certificates" - + " using user-provided password from password provider " - + provider; - logger.warn(exMessage, ex); - return false; - } - } - String exMessage = "No Java truststore password provider could unlock " - + "Java's truststore. Creating a new empty " - + "Truststore for Taverna."; - logger.error(exMessage); - return false; - } - - /** - * Get a username and password pair for the given service, or null if it - * does not exit. The returned array contains username as the first element - * and password as the second. - * - * @deprecated Use - * {@link #getUsernameAndPasswordForService(URI, boolean, String)} - * instead - */ - @Deprecated - public String[] getUsernameAndPasswordForService(String serviceURL) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - UsernamePassword usernamePassword = getUsernameAndPasswordForService( - URI.create(serviceURL), false, null); - if (usernamePassword == null) - return null; - - String[] pair = new String[2]; - pair[0] = usernamePassword.getUsername(); - pair[1] = String.valueOf(usernamePassword.getPassword()); - usernamePassword.resetPassword(); - return pair; - } - - /** - * Get a username and password pair for the given service's URI, or null if - * it does not exit. - * <p> - * If the username and password are not available in the Keystore, it will - * invoke implementations of the {@link ServiceUsernameAndPasswordProvider} - * interface asking the user (typically through the UI) or resolving - * hard-coded credentials. - * <p> - * If the parameter <code>useURIPathRecursion</code> is true, then the - * Credential Manager will also attempt to look for stored credentials for - * each of the parent fragments of the URI. - * - * @param serviceURI - * The URI of the service for which we are providing the username - * and password - * @param useURIPathRecursion - * Whether to look for any username and passwords stored in the - * Keystore for the parent fragments of the service URI (for - * example, we are looking for the credentials for service - * http://somehost/some-fragment but we already have credentials - * stored for http://somehost which can be reused) - * @param requestingMessage - * The message to be presented to the user when asking for the - * username and password, normally useful for UI providers that - * pop up dialogs, can be ignored otherwise - * @return username and password pair for the given service - * @throws CMException - * if anything goes wrong during Keystore lookup, etc. - */ - @Override - public UsernamePassword getUsernameAndPasswordForService(URI serviceURI, - boolean usePathRecursion, String requestingMessage) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - synchronized (keystore) { - SecretKeySpec passwordKey = null; - LinkedHashSet<URI> possibleServiceURIsToLookup = getPossibleServiceURIsToLookup( - serviceURI, usePathRecursion); - Map<URI, URI> allServiceURIs = getFragmentMappedURIsForAllUsernameAndPasswordPairs(); - - try { - for (URI lookupURI : possibleServiceURIsToLookup) { - URI mappedURI = allServiceURIs.get(lookupURI); - if (mappedURI == null) - continue; - - // We found it - get the username and password in the - // Keystore associated with this service URI - String alias = null; - alias = "password#" + mappedURI.toASCIIString(); - passwordKey = (((SecretKeySpec) keystore.getKey(alias, - masterPassword.toCharArray()))); - if (passwordKey == null) { - // Unexpected, it was just there in the map! - logger.warn("Could not find alias " + alias - + " for known uri " + lookupURI - + ", just deleted?"); - // Remember we went outside synchronized(keystore) while - // looping - continue; - } - String unpasspair = new String(passwordKey.getEncoded(), - UTF_8); - /* - * decoded key contains string - * <USERNAME><SEPARATOR_CHARACTER><PASSWORD> - */ - - int separatorAt = unpasspair - .indexOf(USERNAME_AND_PASSWORD_SEPARATOR_CHARACTER); - if (separatorAt < 0) - throw new CMException("Invalid credentials stored for " - + lookupURI); - - String username = unpasspair.substring(0, separatorAt); - String password = unpasspair.substring(separatorAt + 1); - - UsernamePassword usernamePassword = new UsernamePassword(); - usernamePassword.setUsername(username); - usernamePassword.setPassword(password.toCharArray()); - return usernamePassword; - } - - // Nothing found in the Keystore, let's lookup using the service - // username and password providers - for (ServiceUsernameAndPasswordProvider provider : serviceUsernameAndPasswordProviders) { - UsernamePassword usernamePassword = provider - .getServiceUsernameAndPassword(serviceURI, - requestingMessage); - if (usernamePassword == null) - continue; - if (usernamePassword.isShouldSave()) { - URI uri = serviceURI; - if (usePathRecursion) - uri = normalizeServiceURI(serviceURI); - addUsernameAndPasswordForService(usernamePassword, uri); - } - return usernamePassword; - } - // Giving up - return null; - } catch (Exception ex) { - String exMessage = "Failed to get the username and password pair for service " - + serviceURI + " from the Keystore"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - } - - protected Map<URI, URI> getFragmentMappedURIsForAllUsernameAndPasswordPairs() - throws CMException { - synchronized (Security.class) {// FIXME synchonization on strange thing! - if (cachedServiceURIsMap == null) { - HashMap<URI, URI> map = new HashMap<>(); - // Get all service URIs that have username and password in the - // Keystore - for (URI serviceURI : getServiceURIsForAllUsernameAndPasswordPairs()) { - // Always store 1-1, with or without fragment - map.put(serviceURI, serviceURI); - if (serviceURI.getFragment() == null) - continue; - - // Look up the no-fragment uri as an additional mapping - URI noFragment; - try { - noFragment = dnParser - .setFragmentForURI(serviceURI, null); - } catch (URISyntaxException e) { - logger.warn("Could not reset fragment for service URI " - + serviceURI); - continue; - } - if (map.containsKey(noFragment)) { - if (map.get(noFragment).getFragment() != null) { - // No mapping for duplicates - map.remove(noFragment); - continue; - } // else it's noFragment -> noFragment, which is OK - } else { - // Brand new, put it in - map.put(noFragment, serviceURI); - } - } - cachedServiceURIsMap = map; - } - return cachedServiceURIsMap; - } - } - - /* - * Creates a list of possible URIs to look up when searching for username - * and password for a service with a given URI. This is mainly useful for - * HTTP AuthN when we save the realm URI rather than the exact service URI - * as we want that username and password pair to be used for the whole realm - * and not bother user for credentials every time them access a URL from - * that realm. - */ - protected LinkedHashSet<URI> getPossibleServiceURIsToLookup(URI serviceURI, - boolean usePathRecursion) { - try { - serviceURI = serviceURI.normalize(); - serviceURI = dnParser.setUserInfoForURI(serviceURI, null); - } catch (URISyntaxException ex) { - logger.warn("Could not strip userinfo from " + serviceURI, ex); - } - - /* - * We'll use a LinkedHashSet to avoid checking for duplicates, like if - * serviceURI.equals(withoutQuery) Only the first hit should be added to - * the set. - */ - LinkedHashSet<URI> possibles = new LinkedHashSet<URI>(); - - possibles.add(serviceURI); - if (!usePathRecursion || !serviceURI.isAbsolute()) - return possibles; - - /* - * We'll preserve the fragment, as it is used to indicate the realm - */ - String rawFragment = serviceURI.getRawFragment(); - if (rawFragment == null) - rawFragment = ""; - - URI withoutQuery = serviceURI.resolve(serviceURI.getRawPath()); - addFragmentedURI(possibles, withoutQuery, rawFragment); - - // Immediate parent - URI parent = withoutQuery.resolve("."); - addFragmentedURI(possibles, parent, rawFragment); - URI oldParent = null; - // Top parent (to be added later) - URI root = parent.resolve("/"); - while (!parent.equals(oldParent) && !parent.equals(root) - && parent.getPath().length() > 0) { - // Intermediate parents, but not for "http://bla.org" as we would - // find "http://bla.org.." - oldParent = parent; - parent = parent.resolve(".."); - addFragmentedURI(possibles, parent, rawFragment); - } - // In case while-loop did not do so, also include root - addFragmentedURI(possibles, root, rawFragment); - if (rawFragment.length() > 0) - // Add the non-fragment versions in the bottom of the list - for (URI withFragment : new ArrayList<>(possibles)) - try { - possibles - .add(dnParser.setFragmentForURI(withFragment, null)); - } catch (URISyntaxException e) { - logger.warn("Could not non-fragment URI " + withFragment); - } - return possibles; - } - - public void addFragmentedURI(LinkedHashSet<URI> possibles, URI uri, - String rawFragment) { - if (rawFragment != null && rawFragment.length() > 0) - uri = uri.resolve("#" + rawFragment); - possibles.add(uri); - } - - /** - * Get service URLs associated with all username/password pairs currently in - * the Keystore. - * - * @deprecated - * @see #getServiceURIsForAllUsernameAndPasswordPairs() - */ - @Deprecated - public ArrayList<String> getServiceURLsforAllUsernameAndPasswordPairs() - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - List<URI> uris = getServiceURIsForAllUsernameAndPasswordPairs(); - ArrayList<String> serviceURLs = new ArrayList<>(); - for (URI uri : uris) - serviceURLs.add(uri.toASCIIString()); - return serviceURLs; - } - - /** - * Insert a username and password pair for the given service URI in the - * Keystore. - * <p> - * Effectively, this method inserts a new secret key entry in the Keystore, - * where key contains <USERNAME>"\000"<PASSWORD> string, i.e. password is - * prepended with the username and separated by a \000 character (which - * hopefully will not appear in the username). - * <p> - * Username and password string is saved in the Keystore as byte array using - * SecretKeySpec (which constructs a secret key from the given byte array - * but does not check if the given bytes indeed specify a secret key of the - * specified algorithm). - * <p> - * An alias used to identify the username and password entry is constructed - * as "password#"<SERVICE_URL> using the service URL this username/password - * pair is to be used for. - * - * @param usernamePassword - * The {@link UsernamePassword} to store - * @param serviceURI - * The (possibly normalized) URI to store the credentials under - * @throws CMException - * If the credentials could not be stored - * @return the alias under which this username and password entry was saved - * in the Keystore - */ - @Override - public String addUsernameAndPasswordForService( - UsernamePassword usernamePassword, URI serviceURI) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - String uriString = serviceURI.toASCIIString(); - String alias = saveUsernameAndPasswordForService( - usernamePassword.getUsername(), - String.valueOf(usernamePassword.getPassword()), uriString); - return alias; - } - - /** - * Insert a new username and password pair in the Keystore for the given - * service URL string. - * <p> - * Effectively, this method inserts a new secret key entry in the Keystore, - * where key contains <USERNAME>"\000"<PASSWORD> string, i.e. password is - * prepended with the username and separated by a \000 character. - * <p> - * Username and password string is saved in the Keystore as byte array using - * SecretKeySpec (which constructs a secret key from the given byte array - * but does not check if the given bytes indeed specify a secret key of the - * specified algorithm). - * <p> - * An alias used to identify the username and password entry is constructed - * as "password#"<SERVICE_URL> using the service URL this username/password - * pair is to be used for. - * <p> - * - * @return the alias under which this username and password entry was saved - * in the Keystore - * @deprecated Use - * {@link #addUsernameAndPasswordForService(UsernamePassword, URI)} - * instead - */ - @Deprecated - public String saveUsernameAndPasswordForService(String username, - String password, String serviceURL) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - String alias = null; - - // Alias for the username and password entry - synchronized (keystore) { - alias = "password#" + serviceURL; - /* - * Password (together with its related username) is wrapped as a - * SecretKeySpec that implements SecretKey and constructs a secret - * key from the given password as a byte array. The reason for this - * is that we can only save instances of Key objects in the - * Keystore, and SecretKeySpec class is useful for raw secret keys - * (i.e. username and passwords concats) that can be represented as - * a byte array and have no key or algorithm parameters associated - * with them, e.g., DES or Triple DES. That is why we create it with - * the name "DUMMY" for algorithm name, as this is not checked for - * anyway. - * - * Use a separator character that will not appear in the username or - * password. - */ - String keyToSave = username - + USERNAME_AND_PASSWORD_SEPARATOR_CHARACTER + password; - - SecretKeySpec passwordKey; - try { - passwordKey = new SecretKeySpec(keyToSave.getBytes(UTF_8), - "DUMMY"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Could not find encoding " + UTF_8); - } - try { - keystore.setKeyEntry(alias, passwordKey, - masterPassword.toCharArray(), null); - saveKeystore(KEYSTORE); - multiCaster.notify(new KeystoreChangedEvent(KEYSTORE)); - } catch (Exception ex) { - String exMessage = "Failed to insert username and password pair for service " - + serviceURL + " in the Keystore"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - return alias; - } - - /** - * Delete a username and password pair for the given service URI from the - * Keystore. - */ - @Override - public void deleteUsernameAndPasswordForService(URI serviceURI) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - String uriString = serviceURI.toASCIIString(); - deleteUsernameAndPasswordForService(uriString); - } - - /** - * Delete a username and password pair for the given service URL string from - * the Keystore. - * - * @deprecated Use - * {@link #deleteUsernameAndPasswordForService(URI serviceURI)} - * instead. - */ - @Deprecated - public void deleteUsernameAndPasswordForService(String serviceURL) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - synchronized (keystore) { - deleteEntry(KEYSTORE, "password#" + serviceURL); - saveKeystore(KEYSTORE); - multiCaster.notify(new KeystoreChangedEvent(KEYSTORE)); - } - } - - /** - * Insert a new key entry containing private key and the corresponding - * public key certificate chain in the Keystore. - * - * An alias used to identify the keypair entry is constructed as: - * "keypair#"<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"#"< - * CERT_SERIAL_NUMBER> - * - * @return the alias under which this key entry was saved in the Keystore - */ - @Override - public String addKeyPair(Key privateKey, Certificate[] certs) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - String alias = null; - - synchronized (keystore) { - // Create an alias for the new key pair entry in the Keystore as - // "keypair#"<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"#"<CERT_SERIAL_NUMBER> - String ownerDN = ((X509Certificate) certs[0]) - .getSubjectX500Principal().getName(RFC2253); - ParsedDistinguishedName parsedDN = dnParser.parseDN(ownerDN); - String ownerCN = parsedDN.getCN(); // owner's common name - - // Get the hexadecimal representation of the certificate's serial - // number - String serialNumber = new BigInteger(1, - ((X509Certificate) certs[0]).getSerialNumber() - .toByteArray()).toString(16).toUpperCase(); - - String issuerDN = ((X509Certificate) certs[0]) - .getIssuerX500Principal().getName(RFC2253); - parsedDN = dnParser.parseDN(issuerDN); - String issuerCN = parsedDN.getCN(); // issuer's common name - - alias = "keypair#" + ownerCN + "#" + issuerCN + "#" + serialNumber; - - try { - keystore.setKeyEntry(alias, privateKey, - masterPassword.toCharArray(), certs); - saveKeystore(KEYSTORE); - multiCaster.notify(new KeystoreChangedEvent(KEYSTORE)); - - /* - * This is now done from the KeystoresChangedObserver's notify - * method. Update the default SSLSocketFactory used by the - * HttpsURLConnectionS - */ - // HttpsURLConnection.setDefaultSSLSocketFactory(createTavernaSSLSocketFactory()); - logger.debug("updating SSLSocketFactory after inserting a key pair"); - } catch (Exception ex) { - throw new CMException("failed to insert " - + "the key pair entry in the Keystore", ex); - } - } - return alias; - } - - /** - * Checks if the Keystore contains the given key pair entry (private key and - * its corresponding public key certificate chain). - */ - @Override - public boolean hasKeyPair(Key privateKey, Certificate[] certs) - throws CMException { - // Create an alias for the new key pair entry in the Keystore as - // "keypair#"<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"#"<CERT_SERIAL_NUMBER> - - String alias = createKeyPairAlias(privateKey, certs); - return hasEntryWithAlias(KEYSTORE, alias); - } - - /** - * Delete a key pair entry from the Keystore given its alias. - */ - @Override - public void deleteKeyPair(String alias) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - synchronized (keystore) { - deleteEntry(KEYSTORE, alias); - saveKeystore(KEYSTORE); - multiCaster.notify(new KeystoreChangedEvent(KEYSTORE)); - - /* - * This is now done from the KeyManager's nad TrustManager's notify - * methods. Update the default SSLSocketFactory used by the - * HttpsURLConnectionS - */ - // HttpsURLConnection.setDefaultSSLSocketFactory(createTavernaSSLSocketFactory()); - - logger.info("updating SSLSocketFactory after deleting a keypair"); - } - } - - /** - * Delete a key pair entry from the Keystore given its private and public - * key parts. - */ - @Override - public void deleteKeyPair(Key privateKey, Certificate[] certs) - throws CMException { - deleteKeyPair(createKeyPairAlias(privateKey, certs)); - } - - /** - * Export a key entry containing private key and public key certificate - * chain from the Keystore to a PKCS #12 file. - */ - @Override - public void exportKeyPair(String alias, File exportFile, - String pkcs12Password) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - synchronized (keystore) { - // Export the key pair - try { - // Get the private key for the alias - PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, - masterPassword.toCharArray()); - - // Get the related public key's certificate chain - Certificate[] certChain = getKeyPairsCertificateChain(alias); - - // Create a new PKCS #12 keystore - KeyStore newPkcs12 = KeyStore.getInstance("PKCS12", "BC"); - newPkcs12.load(null, null); - - // Place the private key and certificate chain into the PKCS #12 - // keystore. Construct a new alias as - // "<SUBJECT_COMMON_NAME>'s <ISSUER_ORGANISATION> ID" - - String sDN = ((X509Certificate) certChain[0]) - .getSubjectX500Principal().getName(RFC2253); - - ParsedDistinguishedName parsedDN = dnParser.parseDN(sDN); - String sCN = parsedDN.getCN(); - - String iDN = ((X509Certificate) certChain[0]) - .getIssuerX500Principal().getName(RFC2253); - parsedDN = dnParser.parseDN(iDN); - String iCN = parsedDN.getCN(); - - String pkcs12Alias = sCN + "'s " + iCN + " ID"; - newPkcs12.setKeyEntry(pkcs12Alias, privateKey, new char[0], - certChain); - - // Store the new PKCS #12 keystore on the disk - try (FileOutputStream fos = new FileOutputStream(exportFile)) { - newPkcs12.store(fos, pkcs12Password.toCharArray()); - } - } catch (Exception ex) { - String exMessage = "Failed to export the key pair from the Keystore"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - } - - /** - * Get certificate entry from the Keystore or Truststore. If the given alias - * name identifies a trusted certificate entry, the certificate associated - * with that entry is returned from the Truststore. If the given alias name - * identifies a key pair entry, the first element of the certificate chain - * of that entry is returned from the Keystore. - */ - @Override - public Certificate getCertificate(KeystoreType ksType, String alias) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - switch (ksType) { - case KEYSTORE: - synchronized (keystore) { - return keystore.getCertificate(alias); - } - case TRUSTSTORE: - synchronized (truststore) { - return truststore.getCertificate(alias); - } - default: - return null; - } - } catch (Exception ex) { - String exMessage = "Failed to fetch certificate from the " + ksType; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Get certificate chain for the key pair entry from the Keystore. This - * method works for the Keystore only as the Truststore does not contain key - * pair entries, but trusted certificate entries only. - */ - @Override - public Certificate[] getKeyPairsCertificateChain(String alias) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - synchronized (keystore) { - return keystore.getCertificateChain(alias); - } - } catch (Exception ex) { - String exMessage = "Failed to fetch certificate chain for the keypair from the Keystore"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Get the private key part of a key pair entry from the Keystore given its - * alias. - * <p> - * This method works for the Keystore only as the Truststore does not - * contain key pair entries, but trusted certificate entries only. - * - * @throws CMException - */ - @Override - public Key getKeyPairsPrivateKey(String alias) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - synchronized (keystore) { - return keystore.getKey(alias, masterPassword.toCharArray()); - } - } catch (Exception ex) { - String exMessage = "Failed to fetch private key for the keypair from the Keystore"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Insert a trusted certificate entry in the Truststore with an alias - * constructed as: - * - * "trustedcert#<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"# - * "<CERT_SERIAL_NUMBER> - * - * @return the alias under which this trusted certificate entry was saved in - * the Keystore - */ - @Override - public String addTrustedCertificate(X509Certificate cert) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - String alias = null; - - synchronized (truststore) { - // Create an alias for the new trusted certificate entry in the - // Truststore as - // "trustedcert#"<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"#"<CERT_SERIAL_NUMBER> - alias = createTrustedCertificateAlias(cert); - try { - truststore.setCertificateEntry(alias, cert); - saveKeystore(TRUSTSTORE); - multiCaster.notify(new KeystoreChangedEvent(TRUSTSTORE)); - - /* - * This is now done from the KeystoresChangedObserver's notify - * method. Update the default SSLSocketFactory used by the - * HttpsURLConnectionS - */ - // HttpsURLConnection.setDefaultSSLSocketFactory(createTavernaSSLSocketFactory()); - - logger.debug("Updating SSLSocketFactory after inserting a trusted certificate"); - } catch (Exception ex) { - throw new CMException( - "failed to insert trusted certificate entry in the Truststore", - ex); - } - } - - return alias; - } - - /** - * Create a Keystore alias that would be used for adding the given key pair - * (private and public key) entry to the Keystore. The alias is cretaed as - * "keypair#" - * <CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME> - * ;"#"<CERT_SERIAL_NUMBER> - * - * @param privateKey - * private key - * @param certs - * public key's certificate chain - * @return - */ - @Override - public String createKeyPairAlias(Key privateKey, Certificate certs[]) { - String ownerDN = ((X509Certificate) certs[0]).getSubjectX500Principal() - .getName(RFC2253); - ParsedDistinguishedName parsedDN = dnParser.parseDN(ownerDN); - String ownerCN = parsedDN.getCN(); // owner's common name - - /* - * Get the hexadecimal representation of the certificate's serial number - */ - String serialNumber = new BigInteger(1, ((X509Certificate) certs[0]) - .getSerialNumber().toByteArray()).toString(16).toUpperCase(); - - String issuerDN = ((X509Certificate) certs[0]).getIssuerX500Principal() - .getName(RFC2253); - parsedDN = dnParser.parseDN(issuerDN); - String issuerCN = parsedDN.getCN(); // issuer's common name - - String alias = "keypair#" + ownerCN + "#" + issuerCN + "#" - + serialNumber; - return alias; - } - - /** - * Create a Truststore alias that would be used for adding the given trusted - * X509 certificate to the Truststore. The alias is cretaed as - * "trustedcert#"<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"#"< - * CERT_SERIAL_NUMBER> - * - * @param cert - * certificate to generate the alias for - * @return the alias for the given certificate - */ - @Override - public String createTrustedCertificateAlias(X509Certificate cert) { - String ownerDN = cert.getSubjectX500Principal().getName(RFC2253); - ParsedDistinguishedName parsedDN = dnParser.parseDN(ownerDN); - String owner; - String ownerCN = parsedDN.getCN(); // owner's common name - String ownerOU = parsedDN.getOU(); - String ownerO = parsedDN.getO(); - if (!ownerCN.equals("none")) { // try owner's CN first - owner = ownerCN; - } // try owner's OU - else if (!ownerOU.equals("none")) { - owner = ownerOU; - } else if (!ownerO.equals("none")) { // finally use owner's Organisation - owner = ownerO; - } else { - owner = "<Not Part of Certificate>"; - } - - /* Get the hexadecimal representation of the certificate's serial number */ - String serialNumber = new BigInteger(1, cert.getSerialNumber() - .toByteArray()).toString(16).toUpperCase(); - - String issuerDN = cert.getIssuerX500Principal().getName(RFC2253); - parsedDN = dnParser.parseDN(issuerDN); - String issuer; - String issuerCN = parsedDN.getCN(); // issuer's common name - String issuerOU = parsedDN.getOU(); - String issuerO = parsedDN.getO(); - if (!issuerCN.equals("none")) { // try issuer's CN first - issuer = issuerCN; - } // try issuer's OU - else if (!issuerOU.equals("none")) { - issuer = issuerOU; - } else if (!issuerO.equals("none")) { // finally use issuer's - // Organisation - issuer = issuerO; - } else { - issuer = "<Not Part of Certificate>"; - } - - String alias = "trustedcert#" + owner + "#" + issuer + "#" - + serialNumber; - return alias; - } - - /** - * Checks if the Truststore contains the given public key certificate. - */ - @Override - public boolean hasTrustedCertificate(Certificate cert) throws CMException { - // Create an alias for the new trusted certificate entry in the - // Truststore as - // "trustedcert#"<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"#"<CERT_SERIAL_NUMBER> - String alias = createTrustedCertificateAlias((X509Certificate) cert); - return hasEntryWithAlias(TRUSTSTORE, alias); - } - - /** - * Delete a trusted certificate entry from the Truststore given its alias. - */ - @Override - public void deleteTrustedCertificate(String alias) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - synchronized (truststore) { - deleteEntry(TRUSTSTORE, alias); - saveKeystore(TRUSTSTORE); - multiCaster.notify(new KeystoreChangedEvent(TRUSTSTORE)); - - /* - * This is now done from the KeyManager's nad TrustManager's notify - * methods Update the default SSLSocketFactory used by the - * HttpsURLConnectionS - */ - // HttpsURLConnection.setDefaultSSLSocketFactory(createTavernaSSLSocketFactory()); - - logger.info("Updating SSLSocketFactory " - + "after deleting a trusted certificate"); - } - } - - /** - * Delete a trusted certificate entry from the Truststore given the - * certificate. - */ - @Override - public void deleteTrustedCertificate(X509Certificate cert) - throws CMException { - String alias = createTrustedCertificateAlias(cert); - deleteTrustedCertificate(alias); - } - - /** - * Check if the given alias identifies is a key entry in the Keystore. - */ - @Override - public boolean isKeyEntry(String alias) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - synchronized (keystore) { - return keystore.isKeyEntry(alias); - } - } catch (Exception ex) { - String exMessage = "failed to access the key entry in the Keystore"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Delete an entry from the Keystore or the Truststore. - */ - private void deleteEntry(KeystoreType ksType, String alias) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - switch (ksType) { - case KEYSTORE: - synchronized (keystore) { - if (keystore.containsAlias(alias)) - keystore.deleteEntry(alias); - return; - } - case TRUSTSTORE: - synchronized (truststore) { - if (truststore.containsAlias(alias)) - truststore.deleteEntry(alias); - return; - } - } - } catch (Exception ex) { - String exMessage = "failed to delete the entry with alias " + alias - + " from the " + ksType; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Check if the Keystore/Truststore contains an entry with the given alias. - */ - @Override - public boolean hasEntryWithAlias(KeystoreType ksType, String alias) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - switch (ksType) { - case KEYSTORE: - synchronized (keystore) { - return keystore.containsAlias(alias); - } - case TRUSTSTORE: - synchronized (truststore) { - return truststore.containsAlias(alias); - } - default: - return false; - } - } catch (Exception ex) { - String exMessage = "failed to access the " + ksType - + " to check if an alias exists"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Get all the aliases from the Keystore/Truststore or null if there was - * some error while accessing it. - */ - @Override - public ArrayList<String> getAliases(KeystoreType ksType) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - switch (ksType) { - case KEYSTORE: - synchronized (keystore) { - return Collections.list(keystore.aliases()); - } - case TRUSTSTORE: - synchronized (truststore) { - return Collections.list(truststore.aliases()); - } - default: - return null; - } - } catch (Exception ex) { - String exMessage = "failed to access the " + ksType - + " to get the aliases"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Get service URIs associated with all username/password pairs currently in - * the Keystore. - * - * @see #hasUsernamePasswordForService(URI) - */ - @Override - public List<URI> getServiceURIsForAllUsernameAndPasswordPairs() - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - synchronized (keystore) { - if (cachedServiceURIsList == null) { - List<URI> serviceURIs = new ArrayList<>(); - for (String alias : getAliases(KEYSTORE)) { - /* - * We are only interested in username/password entries here. - * Alias for such entries is constructed as - * "password#"<SERVICE_URI> where SERVICE_URI is the service - * this username/password pair is to be used for. - */ - if (!alias.startsWith("password#")) - continue; - String[] split = alias.split("#", 2); - if (split.length != 2) { - logger.warn("Invalid alias " + alias); - continue; - } - String uriStr = split[1]; - URI uri = URI.create(uriStr); - serviceURIs.add(uri); - } - cachedServiceURIsList = serviceURIs; - } - return cachedServiceURIsList; - } - } - - /** - * Load a PKCS12-type keystore from a file using the supplied password. - */ - @Override - public KeyStore loadPKCS12Keystore(File pkcs12File, String pkcs12Password) - throws CMException { - // Load the PKCS #12 keystore from the file - try (InputStream input = new FileInputStream(pkcs12File)) { - KeyStore pkcs12 = KeyStore.getInstance("PKCS12", "BC"); - pkcs12.load(input, pkcs12Password.toCharArray()); - return pkcs12; - } catch (Exception ex) { - String exMessage = "failed to open a PKCS12-type keystore"; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Add an observer of the changes to the Keystore or Truststore. - */ - @Override - public void addObserver(Observer<KeystoreChangedEvent> observer) { - multiCaster.addObserver(observer); - } - - /** - * Get all current observers of changes to the Keystore or Truststore. - */ - @Override - public List<Observer<KeystoreChangedEvent>> getObservers() { - return multiCaster.getObservers(); - } - - /** - * Remove an observer of the changes to the Keystore or Truststore. - */ - @Override - public void removeObserver(Observer<KeystoreChangedEvent> observer) { - multiCaster.removeObserver(observer); - } - - // /** - // * Checks if Credential Manager has been initialised. - // */ - // public boolean isInitialized() { - // return isInitialized; - // } - - // /** - // * Check if Keystore/Truststore file already exists on disk. - // */ - // private boolean exists(KeystoreType ksType) { - // - // if (ksType.equals(KEYSTORE)) - // return keystoreFile.exists(); - // else if (ksType.equals(TRUSTSTORE)) { - // return truststoreFile.exists(); - // } else - // return false; - // } - - /** - * Save the Keystore back to the file it was originally loaded from. - */ - private void saveKeystore(KeystoreType ksType) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - try { - switch (ksType) { - case KEYSTORE: - synchronized (keystore) { - try (FileOutputStream fos = new FileOutputStream( - keystoreFile)) { - keystore.store(fos, masterPassword.toCharArray()); - return; - } - } - case TRUSTSTORE: - synchronized (truststore) { - try (FileOutputStream fos = new FileOutputStream( - truststoreFile)) { - // Hard-coded trust store password - truststore.store(fos, masterPassword.toCharArray()); - return; - } - } - } - } catch (Exception ex) { - String exMessage = "failed to save the " + ksType; - logger.error(exMessage, ex); - throw new CMException(exMessage, ex); - } - } - - /** - * Checks if Keystore's master password is the same as the one provided. - */ - @Override - public boolean confirmMasterPassword(String password) throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - return (masterPassword != null) && masterPassword.equals(password); - } - - private static final boolean REALLY_DISABLED = false; - /** - * Change the Keystore and the Truststore's master password to the one - * provided. The Keystore and Truststore both use the same password. - */ - @Override - public void changeMasterPassword(String newMasterPassword) - throws CMException { - // Need to make sure we are initialized before we do anything else - // as Credential Manager can be created but not initialized - initialize(); - - String oldMasterPassword = masterPassword; - KeyStore oldKeystore = keystore; - KeyStore oldTruststore = truststore; - - try { - synchronized (keystore) { - // Create a new keystore and copy all items from the current - // one, encrypting them with the new password - KeyStore newKeystore = null; - try { - // Try to create Taverna's Keystore as Bouncy Castle - // UBER-type keystore. - newKeystore = KeyStore.getInstance("UBER", "BC"); - } catch (Exception ex) { - // The requested keystore type is not available from - // security providers. - String exMessage = "Failed to instantiate a new Bouncy Castle Keystore when changing master password."; - throw new CMException(exMessage, ex); - } - try { - // Initialize a new empty keystore - newKeystore.load(null, null); - } catch (Exception ex) { - String exMessage = "Failed to create a new empty Keystore to copy over the entries from the current one."; - throw new CMException(exMessage, ex); - } - - Enumeration<String> aliases = keystore.aliases(); - while (aliases.hasMoreElements()) { - String alias = aliases.nextElement(); - if (REALLY_DISABLED) { - if (alias.startsWith("password#")) { // a password entry - SecretKeySpec passwordKey = (((SecretKeySpec) keystore - .getKey(alias, masterPassword.toCharArray()))); - newKeystore.setKeyEntry(alias, passwordKey, - newMasterPassword.toCharArray(), null); - } else if (alias.startsWith("keypair#")) { // a private key entry - // Get the private key for the alias - PrivateKey privateKey = (PrivateKey) keystore - .getKey(alias, masterPassword.toCharArray()); - // Get the related public key's certificate chain - Certificate[] certChain = keystore - .getCertificateChain(alias); - newKeystore.setKeyEntry(alias, privateKey, - newMasterPassword.toCharArray(), certChain); - } - } - // Do all entries at once, not reason to separate password & - // key pair entries - newKeystore.setEntry( - alias, - keystore.getEntry(alias, - new KeyStore.PasswordProtection( - masterPassword.toCharArray())), - new KeyStore.PasswordProtection(newMasterPassword - .toCharArray())); - } - try (FileOutputStream fos = new FileOutputStream(keystoreFile)) { - newKeystore.store(fos, newMasterPassword.toCharArray()); - } - keystore = newKeystore; - } - - // Truststore does not need to be re-encrypeted item by item as - // entries there are not encrypted, just the whole truststore - synchronized (truststore) { - try (FileOutputStream fos = new FileOutputStream(truststoreFile)) { - truststore.store(fos, newMasterPassword.toCharArray()); - } - } - - // Set the new master password as well - masterPassword = newMasterPassword; - } catch (Exception ex) { - // rollback - keystore = oldKeystore; - truststore = oldTruststore; - masterPassword = oldMasterPassword; - saveKeystore(KEYSTORE); - saveKeystore(TRUSTSTORE); - - String exMessage = "Failed to change maaster password - reverting to the old one"; - logger.error(exMessage, ex); - throw (new CMException(exMessage)); - } - } - - @Override - public void initializeSSL() throws CMException { - /* - * We use the lazy initialization of Credential Manager from inside the - * Taverna's SSLSocketFactory (i.e. KeyManager's and TrustManager's - * init() methods) when it is actually needed so do not initialize it - * here. These init() methods will not be called unledd a SSL connection - * is attempted somewhere from Taverna and it is inside them that we - * actually call the initialize() method on Credential Manager (and not - * from the Credential Manager's constructor - hence lazy). - * - * Create Taverna's SSLSocketFactory and set the SSL socket factory from - * HttpsURLConnectionS to use it - */ - if (tavernaSSLSocketFactory == null) - HttpsURLConnection - .setDefaultSSLSocketFactory(createSSLSocketFactory()); - } - - /** - * Creates SSLSocketFactory based on Credential Manager's Keystore and - * Truststore but only initalizes Credential Manager when one of the methods - * needed for creating an HTTPS connection is invoked. - */ - private SSLSocketFactory createSSLSocketFactory() throws CMException { - SSLContext sc = null; - try { - sc = SSLContext.getInstance("SSLv3"); - } catch (NoSuchAlgorithmException e1) { - throw new CMException( - "Failed to create SSL socket factory: " - + "the SSL algorithm was not available from any crypto provider", - e1); - } - - KeyManager[] keyManagers = null; - try { - // Create our own KeyManager with (possibly not yet initialised) - // Taverna's Keystore - keyManagers = new KeyManager[] { new TavernaKeyManager() }; - } catch (Exception e) { - throw new CMException("Failed to create SSL socket factory: " - + "could not initiate SSL Key Manager", e); - } - - TrustManager[] trustManagers = null; - try { - // Create our own TrustManager with (possibly not yet initialised) - // Taverna's Truststore - trustManagers = new TrustManager[] { new TavernaTrustManager() }; - } catch (Exception e) { - throw new CMException("Failed to create SSL socket factory: " - + "could not initiate SSL Trust Manager", e); - } - - try { - sc.init(keyManagers, trustManagers, new SecureRandom()); - } catch (KeyManagementException kmex) { - throw new CMException("Failed to initiate the SSL socet factory", - kmex); - } - - /* - * Set the default SSLContext to be used for subsequent SSL sockets from - * Java - */ - SSLContext.setDefault(sc); - - /* - * Create SSL socket to be used for HTTPS connections from the JVM e.g. - * REST activity that uses Apache HTTP client library - */ - tavernaSSLSocketFactory = sc.getSocketFactory(); - - return tavernaSSLSocketFactory; - } - - @Override - public SSLSocketFactory getTavernaSSLSocketFactory() throws CMException { - if (tavernaSSLSocketFactory == null) - return createSSLSocketFactory(); - return tavernaSSLSocketFactory; - } - - @Override - public Authenticator getAuthenticator() { - return new CredentialManagerAuthenticator(this); - } - - /** - * Taverna's Key Manager is a customised X509KeyManager that initilises - * Credential Manager only if certain methods on it are invoked, i.e. if - * acces to Keystore is actually needed to authenticate the user. - */ - private class TavernaKeyManager extends X509ExtendedKeyManager { - /** - * The X509KeyManager as returned by the SunX509 provider, initialised - * with the Keystore. - */ - private X509KeyManager sunJSSEX509KeyManager = null; - - /** - * Lazy initialisation - unless we are actually asked to do some SSL - * stuff - do not initialise Credential Manager as it will most probably - * result in popping the master password window, which we want to avoid - * early on while Taverna is starting, unless we need to contact a - * secure service early, e.g. to populate the Service Panel. - */ - private void init() throws Exception { - logger.debug("inside TavernaKeyManager.init()"); - - // Create a "default" JSSE X509KeyManager. - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509", - "SunJSSE"); - - if (!isInitialized) - // If we have not initialised the Credential Manager so far - - // now is the time to do it - try { - logger.debug("Credential Manager has not been instantiated yet"); - initialize(); - logger.debug("Credential Manager instantiated"); - } catch (CMException cme) { - throw new Exception( - "Could not initialize Taverna's KeyManager for SSLSocketFactory:" - + " failed to initialise Credential Manager."); - } - - // Keystore and master password should not be null as we have just - // initalised Credential Manager - synchronized (keystore) { - logger.debug("Reinitialising the KeyManager."); - - kmf.init(keystore, masterPassword.toCharArray()); - - /* - * Iterate over the KeyManagers, look for an instance of - * X509KeyManager. If found, use that as our "default" key - * manager. - */ - for (KeyManager km : kmf.getKeyManagers()) - if (km instanceof X509KeyManager) { - sunJSSEX509KeyManager = (X509KeyManager) km; - return; - } - - // X509KeyManager not found - we have to fail the constructor. - throw new Exception( - "Could not initialize Taverna's KeyManager for SSLSocketFactory:" - + " could not find a SunJSSE X509 KeyManager."); - } - } - - @Override - public String chooseClientAlias(String[] keyType, Principal[] issuers, - Socket socket) { - logger.info("inside chooseClientAlias()"); - - // We have postponed initialization until we are actually asked to - // do something - try { - if (sunJSSEX509KeyManager == null) - init(); - } catch (Exception e) { - logger.error(e); - return null; - } - // Delegate the decision to the default key manager - return sunJSSEX509KeyManager.chooseClientAlias(keyType, issuers, - socket); - } - - @Override - public String chooseServerAlias(String keyType, Principal[] issuers, - Socket socket) { - // TODO Auto-generated method stub - return null; - } - - @Override - public X509Certificate[] getCertificateChain(String alias) { - logger.debug("inside getCertificateChain()"); - // We have postponed initialisation until we are actually asked to - // do something - try { - if (sunJSSEX509KeyManager == null) - init(); - } catch (Exception e) { - logger.error(e); - return null; - } - // Delegate the decision to the default key manager - return sunJSSEX509KeyManager.getCertificateChain(alias); - } - - @Override - public String[] getClientAliases(String keyType, Principal[] issuers) { - logger.debug("inside getClientAliases()"); - // We have postponed initialisation until we are actually asked to - // do something - try { - if (sunJSSEX509KeyManager == null) - init(); - } catch (Exception e) { - logger.error(e); - return null; - } - // Delegate the decision to the default key manager - return sunJSSEX509KeyManager.getClientAliases(keyType, issuers); - } - - @Override - public PrivateKey getPrivateKey(String alias) { - logger.debug("inside getPrivateKey()"); - // We have postponed initialisation until we are actually asked to - // do something - try { - if (sunJSSEX509KeyManager == null) - init(); - } catch (Exception e) { - logger.error(e); - return null; - } - // Delegate the decision to the default key manager - return sunJSSEX509KeyManager.getPrivateKey(alias); - } - - @Override - public String[] getServerAliases(String keyType, Principal[] issuers) { - // TODO Auto-generated method stub - return null; - } - } - - /** - * Taverna's Trust Manager is a customised X509TrustManager that initilizes - * Credential Manager only if certain methods on it are invoked, i.e. if - * acces to Truststore is actually needed to authenticate the remote - * service. - */ - private class TavernaTrustManager implements X509TrustManager { - /** - * The default X509TrustManager as returned by SunX509 provider, - * initialised with the Truststore. We delegate decisions to it, and - * fall back to ask the user if the default X509TrustManager does not - * trust the server's certificate. - */ - private X509TrustManager sunJSSEX509TrustManager = null; - - /** - * Lazy initialization - unless we are actually asked to do some SSL - * stuff - do not initialise Credential Manager as it will most probably - * result in popping the master password window, which we want to avoid - * early on while Taverna is starting, unless we need to contact a - * secure service early, e.g. to populate Service Panel. - */ - private void init() throws Exception { - logger.debug("inside TavernaTrustManager.init()"); - - // Create a "default" JSSE X509TrustManager. - TrustManagerFactory tmf = TrustManagerFactory.getInstance( - "SunX509", "SunJSSE"); - - if (!isInitialized) { - logger.debug("inside TavernaTrustManager.init() - " - + "Credential Manager has not been instantiated yet."); - // If we have not initialised the Credential Manager so far - - // now is the time to do it - try { - initialize(); - logger.debug("inside Taverna TrustManager.init() - " - + "Credential Manager instantiated."); - } catch (CMException cme) { - throw new Exception( - "Could not initialize Taverna's TrustManager for SSLSocketFactory: " - + "failed to initialise Credential Manager."); - } - } - - // Truststore should not be null as we have just initalised - // Credential Manager above - synchronized (truststore) { - logger.debug("inside TavernaTrustManager.init() - " - + "Reinitialising the TrustManager."); - SSLSocketFactory.getDefault(); - tmf.init(truststore); - - /* - * Iterate over the TrustManagers, look for an instance of - * X509TrustManager. If found, use that as our "default" trust - * manager. - */ - for (TrustManager tm : tmf.getTrustManagers()) - if (tm instanceof X509TrustManager) { - sunJSSEX509TrustManager = (X509TrustManager) tm; - return; - } - - // X509TrustManager not found - we have to fail the constructor. - throw new Exception( - "Could not initialize Taverna's TrustManager for SSLSocketFactory."); - } - } - - /* - * This method is called on the server-side for establishing trust with - * a client. - */ - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - } - - /* - * This method is called on the client-side for establishing trust with - * a server. We first try to delegate to the default trust manager that - * uses Taverna's Truststore. If that falls through we ask the user if - * they want to trust the certificate. - */ - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - // We have postponed initialisation until we are actually asked to - // do something - try { - if (sunJSSEX509TrustManager == null) - init(); - } catch (Exception e) { - logger.error(e); - throw new CertificateException(e); - } - // Delegate the decision to the default trust manager - try { - sunJSSEX509TrustManager.checkServerTrusted(chain, authType); - } catch (CertificateException excep) { - // Pop up a dialog box asking whether to trust the server's - // certificate chain. - if (!shouldTrust(chain)) - throw excep; - } - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - // We have postponed initialisation until we are actually asked to - // do something - try { - if (sunJSSEX509TrustManager == null) - init(); - } catch (Exception e) { - logger.error(e); - return null; - } - return sunJSSEX509TrustManager.getAcceptedIssuers(); - } - } - - /** - * Checks if a service is trusted and if not - asks user if they want to - * trust it. - */ - private boolean shouldTrust(final X509Certificate[] chain) - throws IllegalArgumentException { - if (chain == null || chain.length == 0) - throw new IllegalArgumentException( - "At least one certificate needed in chain"); - - /* - * If the certificate already exists in the truststore, it is implicitly - * trusted. This will try to avoid prompting user twice as - * checkServerTrusted() method gets called twice. - * - * Well, this is not working - checkServerTrusted() is still called - * twice. - */ - String alias = createTrustedCertificateAlias(chain[0]); - try { - if (truststore.containsAlias(alias)) - return true; - } catch (KeyStoreException e) { - // Ignore - } - - String name = chain[0].getSubjectX500Principal().getName(); - for (TrustConfirmationProvider trustConfirmationProvider : trustConfirmationProviders) { - Boolean trustConfirmation = trustConfirmationProvider - .shouldTrustCertificate(chain); - if (trustConfirmation == null) - // SPI can't say yes or no, try next one - continue; - - try { - if (trustConfirmation) { - // initialize(); // init the Credential Manager if needed - addTrustedCertificate((X509Certificate) chain[0]); - // this will initialize Cred. Manager - logger.info("Stored trusted certificate " + name); - } - } catch (CMException ex) { - logger.error("Credential Manager failed to " - + "save trusted certificate " + name, ex); - } - if (logger.isDebugEnabled()) { - if (trustConfirmation) { - logger.debug("Trusting " + name + " according to " - + trustConfirmationProvider); - } else { - logger.debug("Not trusting " + name + " according to " - + trustConfirmationProvider); - } - } - return trustConfirmation.booleanValue(); - } - logger.warn("No TrustConfirmationProvider instances could confirm or deny the trust in " - + name); - // None of the trust confirmation providers (if there were any at all) - // could confirm - return false; - } - - /** - * Normalize an URI for insertion as the basis for path-recursive lookups, - * ie. strip query and filename. For example: - * - * <pre> - * URI uri = URI.create("http://foo.org/dir1/dirX/../dir2/filename.html?q=x") - * System.out.println(CredentialManager.normalizeServiceURI(uri)); - * >>> http://foo.org/dir1/dir2/ - * uri = URI.create("http://foo.org/dir1/dir2/"); - * System.out.println(CredentialManager.normalizeServiceURI(uri)); - * >>> http://foo.org/dir1/dir2/ - * </pre> - * <p> - * Note that #fragments are preserved, as these are used to indicate HTTP - * Basic Auth realms - * - * @param serviceURI - * URI for a service that is to be normalized - * @return A normalized URI without query, userinfo or filename, ie. where - * uri.resolve(".").equals(uri). - */ - public URI normalizeServiceURI(URI serviceURI) { - try { - // Strip userinfo, keep fragment - URI normalized = dnParser.setUserInfoForURI(serviceURI, null) - .normalize(); - return dnParser.setFragmentForURI(normalized.resolve("."), - serviceURI.getFragment()); - } catch (URISyntaxException ex) { - return serviceURI; - } - } - - /** - * Reset the JVMs cache for authentication like HTTP Basic Auth. - * <p> - * Note that this method uses undocumented calls to - * <code>sun.net.www.protocol.http.AuthCacheValue</code> which might not be - * valid in virtual machines other than Sun Java 6. If these calls fail, - * this method will log the error and return <code>false</code>. - * - * @return <code>true</code> if the JVMs cache could be reset, or - * <code>false</code> otherwise. - */ - @Override - public boolean resetAuthCache() { - // Sun should expose an official API to do this - try { - Class<?> AuthCacheValue = Class - .forName("sun.net.www.protocol.http.AuthCacheValue"); - Class<?> AuthCacheImpl = Class - .forName("sun.net.www.protocol.http.AuthCacheImpl"); - Class<?> AuthCache = Class - .forName("sun.net.www.protocol.http.AuthCache"); - Method setAuthCache = AuthCacheValue.getMethod("setAuthCache", - AuthCache); - setAuthCache.invoke(null, AuthCacheImpl.newInstance()); - return true; - } catch (Exception ex) { - logger.warn( - "Could not reset authcache, non-Sun JVM or internal Sun classes changed", - ex); - return false; - } - } - - /** - * Checks if the Keystore contains a username and password for the given - * service URI. - */ - @Override - public boolean hasUsernamePasswordForService(URI serviceURI) - throws CMException { - Map<URI, URI> mappedServiceURIs = getFragmentMappedURIsForAllUsernameAndPasswordPairs(); - for (URI possible : getPossibleServiceURIsToLookup(serviceURI, true)) - if (mappedServiceURIs.containsKey(possible)) - return true; - return false; - } - - private void loadDefaultSecurityFiles() { - if (credentialManagerDirectory == null) - credentialManagerDirectory = dnParser - .getCredentialManagerDefaultDirectory(applicationConfiguration); - if (keystoreFile == null) - keystoreFile = new File(credentialManagerDirectory, - KEYSTORE_FILE_NAME); - if (truststoreFile == null) - truststoreFile = new File(credentialManagerDirectory, - TRUSTSTORE_FILE_NAME); - } - - /** - * Set the directory where Credential Manager's Keystore and Truststore - * files will be read from. If this method is not used, the directory will - * default to <TAVERNA_HOME>/security somewhere in user's home directory. - * - * If you want to use this method to change the location of Credential - * Manager's configuration directory then make sure you call it before any - * other method on Credential Manager. - * - * This was supposed to be done through OSGi services. - * - * @param credentialManagerDirectory - * @throws CMException - */ - @Override - public void setConfigurationDirectoryPath(File credentialManagerDirectory) - throws CMException { - if (credentialManagerDirectory == null) - throw new CMException( - "Credential Manager's configuration directory cannot be null."); - - try { - if (!credentialManagerDirectory.exists()) - credentialManagerDirectory.mkdir(); - } catch (Exception e) { - throw new CMException( - "Failed to open Credential Manager's directory " - + credentialManagerDirectory - + " to load the security files: " + e.getMessage(), - e); - } - - keystoreFile = new File(credentialManagerDirectory, KEYSTORE_FILE_NAME); - truststoreFile = new File(credentialManagerDirectory, - TRUSTSTORE_FILE_NAME); - - // Are we resetting the directory? Has stuff been initialized yet? - // Then we need to reset everything else. - if (isInitialized) { - masterPassword = null; - keystore = null; - truststore = null; - isInitialized = false; - } - } - - // private void loadSecurityFiles(String credentialManagerDirPath) - // throws CMException { - // - // // If credentialManagerDirPath is null (e.g. user did not specify -cmdir - // on the command line) - // // - try with Taverna's default one - // if (credentialManagerDirPath == null){ - // credentialManagerDirectory = - // CMUtils.getCredentialManagerDefaultDirectory(); - // } - // - // if (credentialManagerDirectory == null) { - // try { - // credentialManagerDirectory = new File(credentialManagerDirPath); - // } catch (Exception e) { - // throw new CMException( - // "Failed to open Credential Manager's directory to load the security files: " - // + e.getMessage(), - // e); - // } - // } - // if (keystoreFile == null){ - // keystoreFile = new File(credentialManagerDirectory, KEYSTORE_FILE_NAME); - // } - // if (truststoreFile == null){ - // truststoreFile = new File
<TRUNCATED>
