| /******************************************************************************* |
| * Copyright (c) 2008-2011 Chair for Applied Software Engineering, |
| * Technische Universitaet Muenchen. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Otto von Wesendonk - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.emf.emfstore.internal.client.model.connectionmanager; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.InvalidKeyException; |
| import java.security.KeyManagementException; |
| import java.security.KeyStore; |
| import java.security.KeyStoreException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PublicKey; |
| import java.security.UnrecoverableKeyException; |
| import java.security.cert.Certificate; |
| import java.security.cert.CertificateException; |
| import java.security.cert.CertificateFactory; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| |
| import javax.crypto.BadPaddingException; |
| import javax.crypto.Cipher; |
| import javax.crypto.IllegalBlockSizeException; |
| import javax.crypto.NoSuchPaddingException; |
| import javax.net.ssl.HostnameVerifier; |
| import javax.net.ssl.HttpsURLConnection; |
| import javax.net.ssl.KeyManagerFactory; |
| import javax.net.ssl.SSLContext; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.TrustManagerFactory; |
| |
| import org.apache.commons.codec.binary.Base64; |
| import org.eclipse.emf.emfstore.client.exceptions.ESCertificateException; |
| import org.eclipse.emf.emfstore.client.provider.ESClientConfigurationProvider; |
| import org.eclipse.emf.emfstore.client.provider.ESKeyStoreManager; |
| import org.eclipse.emf.emfstore.common.extensionpoint.ESExtensionPoint; |
| import org.eclipse.emf.emfstore.internal.client.model.Configuration; |
| import org.eclipse.emf.emfstore.internal.client.model.ServerInfo; |
| import org.eclipse.emf.emfstore.internal.client.model.util.WorkspaceUtil; |
| import org.eclipse.emf.emfstore.internal.common.model.util.FileUtil; |
| |
| /** |
| * The KeyStoreManager manages the client's KeyStore in which the SSL |
| * certificates for multiple EMFStore servers can be stored. |
| * |
| * @author wesendon |
| */ |
| |
| public final class KeyStoreManager implements ESKeyStoreManager { |
| |
| private static final String JAVAX_NET_SSL_TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword"; //$NON-NLS-1$ |
| private static final String JAVAX_NET_SSL_KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword"; //$NON-NLS-1$ |
| private static final String JAVAX_NET_SSL_KEY_STORE = "javax.net.ssl.keyStore"; //$NON-NLS-1$ |
| private static final String JAVAX_NET_SSL_TRUST_STORE = "javax.net.ssl.trustStore"; //$NON-NLS-1$ |
| private static final String PROVIDER_CLASS = "providerClass"; //$NON-NLS-1$ |
| private static final String ORG_ECLIPSE_EMF_EMFSTORE_CLIENT_DEFAULT_CONFIGURATION_PROVIDER = "org.eclipse.emf.emfstore.client.defaultConfigurationProvider"; //$NON-NLS-1$ |
| /** |
| * Name of keyStore file. |
| */ |
| public static final String KEYSTORENAME = "emfstoreClient.keystore"; //$NON-NLS-1$ |
| private static final String KEYSTOREPASSWORD = "654321"; //$NON-NLS-1$ |
| private static final String CERTIFICATE_TYPE = "X.509"; //$NON-NLS-1$ |
| private static final String CIPHER_ALGORITHM = "RSA"; //$NON-NLS-1$ |
| |
| /** |
| * Certificate Alias for development test certificate. |
| */ |
| public static final String DEFAULT_CERTIFICATE = "emfstore test certificate (do not use in production!)"; //$NON-NLS-1$ |
| |
| private static KeyStoreManager instance; |
| |
| private String defaultCertificate; |
| private KeyStore keyStore; |
| |
| private KeyStoreManager() { |
| defaultCertificate = null; |
| setupKeys(); |
| loadConfiguration(); |
| } |
| |
| private void loadConfiguration() { |
| final ESClientConfigurationProvider provider = new ESExtensionPoint( |
| ORG_ECLIPSE_EMF_EMFSTORE_CLIENT_DEFAULT_CONFIGURATION_PROVIDER).getClass(PROVIDER_CLASS, |
| ESClientConfigurationProvider.class); |
| if (provider == null) { |
| return; |
| } |
| provider.initDefaultCertificates(this); |
| |
| } |
| |
| /** |
| * Returns an instance of the {@link KeyStoreManager}. |
| * |
| * @return {@link KeyStoreManager} |
| */ |
| public static synchronized KeyStoreManager getInstance() { |
| if (instance == null) { |
| instance = new KeyStoreManager(); |
| } |
| return instance; |
| } |
| |
| /** |
| * This method sets the JVM properties in order to use SSL encryption. |
| */ |
| public void setupKeys() { |
| // No changes to exception handling here, due to call nature. |
| if (!keyStoreExists()) { |
| // create directory ~/.emfstore/ if necessary |
| final File emfstoreDir = new File(Configuration.getFileInfo().getWorkspaceDirectory()); |
| if (!emfstoreDir.exists()) { |
| emfstoreDir.mkdir(); |
| } |
| final InputStream inputStream = getClass().getResourceAsStream(KEYSTORENAME); |
| try { |
| // configure file |
| final File clientKeyTarget = new File(Configuration.getFileInfo().getWorkspaceDirectory() |
| + KEYSTORENAME); |
| // copy to destination |
| FileUtil.copyFile(inputStream, clientKeyTarget); |
| } catch (final IOException e) { |
| // TODO OW: exception? - now the user will be alerted to the |
| // problem as soon as he tries to connect. |
| // throw new ConnectionException("Couldn't find keystore."); |
| } finally { |
| try { |
| inputStream.close(); |
| } catch (final IOException e) { |
| // TODO: ignore exception for now, as above |
| } |
| } |
| } |
| |
| System.setProperty(JAVAX_NET_SSL_TRUST_STORE, getPathToKeyStore()); |
| System.setProperty(JAVAX_NET_SSL_KEY_STORE, getPathToKeyStore()); |
| System.setProperty(JAVAX_NET_SSL_KEY_STORE_PASSWORD, KEYSTOREPASSWORD); |
| System.setProperty(JAVAX_NET_SSL_TRUST_STORE_PASSWORD, KEYSTOREPASSWORD); |
| } |
| |
| /** |
| * Lists all certificates in the client's KeyStore. |
| * |
| * @return string representation of the certificates |
| * @throws ESCertificateException |
| * is thrown when problems occur with the CertificateStore, i.e. |
| * illegal operations. |
| */ |
| public ArrayList<String> getCertificates() throws ESCertificateException { |
| loadKeyStore(); |
| final ArrayList<String> certificates = new ArrayList<String>(); |
| try { |
| final Enumeration<String> aliases = keyStore.aliases(); |
| for (; aliases.hasMoreElements();) { |
| final String tmp = aliases.nextElement(); |
| certificates.add(tmp); |
| } |
| } catch (final KeyStoreException e) { |
| final String message = Messages.KeyStoreManager_Loading_Certificate_Failed; |
| WorkspaceUtil.logException(message, e); |
| throw new ESCertificateException(message, e); |
| } |
| return certificates; |
| } |
| |
| /** |
| * Deletes a certificate in the keystore. |
| * |
| * @param alias |
| * alias of certificate |
| * @throws ESCertificateException |
| * is thrown when problems occur with the CertificateStore, i.e. |
| * illegal operations. |
| */ |
| public void deleteCertificate(String alias) throws ESCertificateException { |
| if (isDefaultCertificate(alias)) { |
| throw new ESCertificateException(Messages.KeyStoreManager_Cannot_Delete_Default_Certificate); |
| } |
| |
| loadKeyStore(); |
| try { |
| keyStore.deleteEntry(alias); |
| storeKeyStore(); |
| } catch (final KeyStoreException e) { |
| final String message = Messages.KeyStoreManager_Deleting_Certificate_Failed; |
| WorkspaceUtil.logException(message, e); |
| throw new ESCertificateException(message, e); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.emfstore.client.provider.ESKeyStoreManager#addCertificate(java.lang.String, |
| * java.lang.String) |
| */ |
| public void addCertificate(String alias, String path) throws ESCertificateException { |
| FileInputStream fileInputStream = null; |
| try { |
| fileInputStream = new FileInputStream(path); |
| addCertificate(alias, fileInputStream); |
| } catch (final FileNotFoundException e) { |
| final String message = Messages.KeyStoreManager_Storing_Certificate_Failed; |
| WorkspaceUtil.logException(message, e); |
| throw new ESCertificateException(message, e); |
| } finally { |
| if (fileInputStream != null) { |
| try { |
| fileInputStream.close(); |
| } catch (final IOException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logException(message, e); |
| throw new ESCertificateException(message, e); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Remove certificate with the given alias. |
| * |
| * @param alias the certificate alias |
| * @throws ESCertificateException if removal fails |
| */ |
| public void removeCertificate(String alias) throws ESCertificateException { |
| try { |
| keyStore.deleteEntry(alias); |
| storeKeyStore(); |
| } catch (final KeyStoreException e) { |
| final String message = Messages.KeyStoreManager_Keystore_Not_Initialized; |
| WorkspaceUtil.logException(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final ESCertificateException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logException(message, e); |
| throw new ESCertificateException(message, e); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.emfstore.client.provider.ESKeyStoreManager#addCertificate(java.lang.String, |
| * java.io.InputStream) |
| */ |
| public void addCertificate(String alias, InputStream certificate) throws ESCertificateException { |
| if (!isDefaultCertificate(alias)) { |
| loadKeyStore(); |
| try { |
| final CertificateFactory factory = CertificateFactory.getInstance(CERTIFICATE_TYPE); |
| final Certificate newCertificate = factory.generateCertificate(certificate); |
| keyStore.setCertificateEntry(alias, newCertificate); |
| storeKeyStore(); |
| } catch (final CertificateException e) { |
| final String message = Messages.KeyStoreManager_Choose_Valid_Certificate; |
| throw new ESCertificateException(message); |
| } catch (final KeyStoreException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logException(message, e); |
| throw new ESCertificateException(message, e); |
| } |
| } |
| } |
| |
| private void storeKeyStore() throws ESCertificateException { |
| loadKeyStore(); |
| try { |
| final FileOutputStream fileOutputStream = new FileOutputStream(getPathToKeyStore()); |
| keyStore.store(fileOutputStream, KEYSTOREPASSWORD.toCharArray()); |
| fileOutputStream.close(); |
| } catch (final KeyStoreException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final NoSuchAlgorithmException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final CertificateException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final FileNotFoundException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final IOException e) { |
| final String message = "Storing certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } |
| } |
| |
| /** |
| * Reloads the keystore. |
| * |
| * @throws ESCertificateException |
| * in case of failure |
| */ |
| public void reloadKeyStore() throws ESCertificateException { |
| keyStore = null; |
| loadKeyStore(); |
| } |
| |
| private void loadKeyStore() throws ESCertificateException { |
| if (keyStore == null) { |
| try { |
| keyStore = KeyStore.getInstance("JKS"); //$NON-NLS-1$ |
| final FileInputStream fileInputStream = new FileInputStream(getPathToKeyStore()); |
| keyStore.load(fileInputStream, KEYSTOREPASSWORD.toCharArray()); |
| fileInputStream.close(); |
| } catch (final KeyStoreException e) { |
| final String message = "Loading certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final NoSuchAlgorithmException e) { |
| final String message = "Loading certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final CertificateException e) { |
| final String message = "Loading certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final FileNotFoundException e) { |
| final String message = "Loading certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } catch (final IOException e) { |
| final String message = "Loading certificate failed!"; //$NON-NLS-1$ |
| WorkspaceUtil.logWarning(message, e); |
| throw new ESCertificateException(message, e); |
| } |
| } |
| } |
| |
| /** |
| * Returns a SSL Context. This is need for encryption, used by the |
| * SSLSocketFactory. |
| * |
| * @return SSL Context |
| * @throws ESCertificateException |
| * in case of failure retrieving the context |
| */ |
| public SSLContext getSSLContext() throws ESCertificateException { |
| try { |
| loadKeyStore(); |
| final KeyManagerFactory managerFactory = KeyManagerFactory.getInstance("SunX509"); //$NON-NLS-1$ |
| managerFactory.init(keyStore, KEYSTOREPASSWORD.toCharArray()); |
| final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); //$NON-NLS-1$ |
| trustManagerFactory.init(keyStore); |
| final SSLContext sslContext = SSLContext.getInstance("TLS"); //$NON-NLS-1$ |
| sslContext.init(managerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); |
| |
| HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { |
| public boolean verify(String hostname, SSLSession session) { |
| return true; |
| } |
| }); |
| |
| return sslContext; |
| } catch (final NoSuchAlgorithmException e) { |
| throw new ESCertificateException(Messages.KeyStoreManager_29, e); |
| } catch (final UnrecoverableKeyException e) { |
| throw new ESCertificateException("Loading certificate failed!", e); //$NON-NLS-1$ |
| } catch (final KeyStoreException e) { |
| throw new ESCertificateException("Loading certificate failed!", e); //$NON-NLS-1$ |
| } catch (final KeyManagementException e) { |
| throw new ESCertificateException("Loading certificate failed!", e); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * True if a KeyStore file exists. |
| * |
| * @return boolean |
| */ |
| public boolean keyStoreExists() { |
| final File keyStore = new File(getPathToKeyStore()); |
| return keyStore.exists(); |
| } |
| |
| /** |
| * Returns the path to the KeyStore. |
| * |
| * @return a path |
| */ |
| public String getPathToKeyStore() { |
| return Configuration.getFileInfo().getWorkspaceDirectory() + KEYSTORENAME; |
| } |
| |
| /** |
| * Encrypts a password. |
| * |
| * @param password |
| * the password to be encrypted |
| * @param server |
| * the server from which to fetch the public key that is used for encryption |
| * @return the encrypted password |
| */ |
| public String encrypt(String password, ServerInfo server) { |
| try { |
| final Certificate publicKey = getCertificateForEncryption(server); |
| final PublicKey key = publicKey.getPublicKey(); |
| final byte[] inpBytes = password.getBytes(); |
| final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); |
| cipher.init(Cipher.ENCRYPT_MODE, key); |
| final byte[] encryptededByteAr = cipher.doFinal(inpBytes); |
| final byte[] base64EncodedByteAr = Base64.encodeBase64(encryptededByteAr); |
| return new String(base64EncodedByteAr); |
| // TODO: OW When new login proxy object with encryption handler is |
| // implemented, handle exceptions |
| } catch (final NoSuchAlgorithmException e) { |
| // nothing to do |
| e.printStackTrace(); |
| } catch (final NoSuchPaddingException e) { |
| // nothing to do |
| e.printStackTrace(); |
| } catch (final InvalidKeyException e) { |
| // nothing to do |
| e.printStackTrace(); |
| } catch (final IllegalBlockSizeException e) { |
| // nothing to do |
| e.printStackTrace(); |
| } catch (final BadPaddingException e) { |
| // nothing to do |
| e.printStackTrace(); |
| } catch (final ESCertificateException e) { |
| // Auto-generated catch block |
| e.printStackTrace(); |
| } |
| WorkspaceUtil.logException(Messages.KeyStoreManager_Could_Not_Encrypt_Password, new ESCertificateException( |
| Messages.KeyStoreManager_34)); |
| return ""; //$NON-NLS-1$ |
| } |
| |
| private Certificate getCertificateForEncryption(ServerInfo server) throws ESCertificateException { |
| Certificate publicKey; |
| if (server == null) { |
| publicKey = getCertificate(getDefaultCertificate()); |
| } else { |
| publicKey = getCertificate(server.getCertificateAlias()); |
| } |
| if (publicKey == null) { |
| publicKey = getCertificate(getDefaultCertificate()); |
| if (publicKey == null) { |
| throw new ESCertificateException(Messages.KeyStoreManager_Unable_To_Get_Password); |
| } |
| } |
| return publicKey; |
| } |
| |
| /** |
| * Test whether a given alias is the default certificate alias. |
| * |
| * @param alias |
| * alias under test |
| * @return true if default, false else |
| */ |
| public boolean isDefaultCertificate(String alias) { |
| return getDefaultCertificate().equals(alias); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.emfstore.client.provider.ESKeyStoreManager#getDefaultCertificate() |
| */ |
| public String getDefaultCertificate() { |
| if (defaultCertificate != null) { |
| return defaultCertificate; |
| } else if (Configuration.getVersioningInfo().isDeveloperVersion()) { |
| return DEFAULT_CERTIFICATE; |
| } else { |
| return DEFAULT_CERTIFICATE; |
| } |
| } |
| |
| /** |
| * Returns true if the given alias maps to an existing certificate. |
| * |
| * @param alias |
| * Certificate alias |
| * @return boolean |
| * @throws ESCertificateException |
| * is thrown when problems occur with the CertificateStore, i.e. |
| * illegal operations. |
| */ |
| public boolean contains(String alias) throws ESCertificateException { |
| if (getCertificate(alias) == null) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.emfstore.client.provider.ESKeyStoreManager#setDefaultCertificate(java.lang.String) |
| */ |
| public void setDefaultCertificate(String defaultCertificate) { |
| this.defaultCertificate = defaultCertificate; |
| } |
| |
| /** |
| * Returns the certificate mapped by the given alias. Returns null if no |
| * such certificate exists. |
| * |
| * @param alias |
| * String |
| * @return Certificate |
| * @throws ESCertificateException |
| * is thrown when problems occur with the CertificateStore, i.e. |
| * illegal operations. |
| */ |
| public Certificate getCertificate(String alias) throws ESCertificateException { |
| if (alias == null) { |
| return null; |
| } |
| loadKeyStore(); |
| try { |
| return keyStore.getCertificate(alias); |
| } catch (final KeyStoreException e) { |
| throw new ESCertificateException("Loading certificate failed!"); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.emfstore.client.provider.ESKeyStoreManager#certificateExists(java.lang.String) |
| */ |
| public boolean certificateExists(String alias) throws ESCertificateException { |
| try { |
| return getCertificate(alias) != null; |
| } catch (final ESCertificateException e) { |
| if (!(e.getCause() instanceof FileNotFoundException)) { |
| throw e; |
| } |
| } |
| return false; |
| } |
| } |