You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/02/17 12:32:14 UTC
[08/50] [abbrv] incubator-taverna-engine git commit:
taverna-credential-manager*
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/6475d582/credential-manager-impl/src/main/java/net/sf/taverna/t2/security/credentialmanager/impl/CredentialManagerImpl.java
----------------------------------------------------------------------
diff --git a/credential-manager-impl/src/main/java/net/sf/taverna/t2/security/credentialmanager/impl/CredentialManagerImpl.java b/credential-manager-impl/src/main/java/net/sf/taverna/t2/security/credentialmanager/impl/CredentialManagerImpl.java
deleted file mode 100644
index 5a087df..0000000
--- a/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("/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.error(ex.getMessage(), 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(credentialManagerDirectory,
- // TRUSTSTORE_FILE_NAME);
- // }
- // }
-
- /**
- * Clear
<TRUNCATED>