blob: 77917d0525faeade094c1bc15588060bbc7024d4 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}