package org.limewire.security.certificate;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.limewire.io.IOUtils;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
/**
* Initially loads the key store from the network, and then caches it to disk in
* the preferences directory.
*/
@Singleton
class KeyStoreProviderImpl implements KeyStoreProvider {
private volatile KeyStore keyStore = null;
private final Provider<HttpClient> httpClient;
private volatile File keyStoreLocation;
private volatile char[] keyStorePassword;
private static final Log LOG = LogFactory.getLog(KeyStoreProviderImpl.class);
@Inject
KeyStoreProviderImpl(Provider<HttpClient> httpClient) {
this.httpClient = httpClient;
this.keyStoreLocation = CertificateTools.getKeyStoreLocation();
this.keyStorePassword = CertificateTools.getKeyStorePassword();
}
void setKeyStoreLocation(File location) {
this.keyStoreLocation = location;
}
void setKeyStorePassword(char[] password) {
this.keyStorePassword = password;
}
public KeyStore getKeyStore() throws IOException {
KeyStore ks = keyStore;
if (isValid(ks)) {
return ks;
}
// Not cached, try to load into memory from disk...
try {
ks = getKeyStoreFromDisk();
keyStore = ks;
return ks;
} catch (IOException ex) {
// Failed to load from disk, ignore and try to pull from network...
LOG.debug("IOException trying to load keystore from disk.", ex);
}
// Not on disk, try to load from network, load into memory and save to
// disk.
OutputStream out = null;
try {
ks = getKeyStoreFromNetwork();
keyStore = ks;
keyStoreLocation.getParentFile().mkdirs();
out = new FileOutputStream(keyStoreLocation);
ks.store(out, keyStorePassword);
return ks;
} catch (KeyStoreException ex) {
throw IOUtils.getIOException("KeyStoreException while saving keystore: ", ex);
} catch (NoSuchAlgorithmException ex) {
throw IOUtils.getIOException("NoSuchAlgorithmException while saving keystore: ", ex);
} catch (CertificateException ex) {
throw IOUtils.getIOException("CertificateException while saving keystore: ", ex);
} finally {
IOUtils.close(out);
}
}
KeyStore getKeyStoreFromNetwork() throws IOException {
HttpClient client = httpClient.get();
HttpGet get = new HttpGet(CertificateTools.getKeyStoreURI());
KeyStore newKeyStore;
try {
newKeyStore = KeyStore.getInstance("jks");
} catch (KeyStoreException ex) {
throw new IOException("KeyStoreException instantiating keystore: " + ex.getMessage());
}
HttpResponse response = null;
try {
response = client.execute(get);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
try {
newKeyStore.load(response.getEntity().getContent(), keyStorePassword);
} catch (NoSuchAlgorithmException ex) {
throw IOUtils.getIOException(
"NoSuchAlgorithmException while parsing keystore: ", ex);
} catch (CertificateException ex) {
throw IOUtils.getIOException("CertificateException while parsing keystore: ",
ex);
}
return newKeyStore;
} else {
throw new IOException("Failed to download new keystore ("
+ CertificateTools.getKeyStoreURI().toString() + "): "
+ response.getStatusLine());
}
} finally {
if(response != null && response.getEntity() != null) {
try {
response.getEntity().consumeContent();
} catch (IOException e) {
LOG.error(e);
}
}
}
}
/**
* @return the loaded and initialized key store, or throws an exception
* @throws IOException if loading fails for any reason (missing file,
* certificate issues, etc)
*/
KeyStore getKeyStoreFromDisk() throws IOException {
InputStream in = null;
try {
KeyStore newKeyStore = KeyStore.getInstance("jks");
in = new BufferedInputStream(new FileInputStream(keyStoreLocation));
newKeyStore.load(in, keyStorePassword);
return newKeyStore;
} catch (KeyStoreException ex) {
throw IOUtils.getIOException("KeyStoreException while creating keystore in memory: ",
ex);
} catch (NoSuchAlgorithmException ex) {
throw IOUtils.getIOException("NoSuchAlgorithmException while parsing keystore: ", ex);
} catch (CertificateException ex) {
throw IOUtils.getIOException("CertificateException while parsing keystore: ", ex);
} finally {
IOUtils.close(in);
}
}
/*
* Invalidates by dereferencing our in-memory copy as well as deleting the
* on-disk version.
*/
public void invalidateKeyStore() {
keyStore = null;
keyStoreLocation.delete();
}
private boolean isValid(KeyStore ks) {
if (ks == null)
return false;
try {
if (ks.size() > 0)
return true;
} catch (KeyStoreException ex) {
// keyStore is non-null but not initialized, treat it as null
}
return false;
}
public boolean isCached() {
if (isValid(keyStore))
return true;
if (keyStoreLocation.exists()) {
try {
if (isValid(getKeyStoreFromDisk()))
return true;
} catch (IOException ignored) {
// File exists but doesn't look to be valid...
}
}
return false;
}
}