Skip to content

Commit

Permalink
First pass at refactoring for #453 to set the global trust store prop…
Browse files Browse the repository at this point in the history
…erty that only trust the specific sites we need for #473
  • Loading branch information
myleshorton committed Feb 15, 2013
1 parent 8adf815 commit df86114
Show file tree
Hide file tree
Showing 15 changed files with 420 additions and 127 deletions.
Binary file added certs/DigiCertHighAssuranceCA-3.cer
Binary file not shown.
Binary file added certs/equifaxsecureca.cer
Binary file not shown.
File renamed without changes.
3 changes: 3 additions & 0 deletions src/main/java/org/lantern/DefaultXmppHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,9 @@ public void onSocket(String arg0, Socket arg1) throws IOException {
this.client.set(P2P.newXmppP2PHttpClient("shoot", natPmpService,
upnpService, this.mappedServer,
//newTlsSocketFactory(),ç SSLServerSocketFactory.getDefault(),//newTlsServerSocketFactory(),

// Major issue here -- these TLS factories don't take into account
// future changes in SSL certs we trust.
this.socketsUtil.newTlsSocketFactory(),
this.socketsUtil.newTlsServerSocketFactory(),
//SocketFactory.getDefault(), ServerSocketFactory.getDefault(),
Expand Down
55 changes: 22 additions & 33 deletions src/main/java/org/lantern/LanternKeyStoreManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,24 @@ public class LanternKeyStoreManager implements KeyStoreManager {

private String localCert;

private final TrustManager[] trustManagers;
//private final TrustManager[] trustManagers;

private final LanternTrustManager lanternTrustManager;
//private final LanternTrustManager lanternTrustManager;

private final Model model;

private final LanternTrustStore trustStore;

@Inject
public LanternKeyStoreManager(final Model model,
final CertTracker certTracker) {
this(null, model, certTracker);
final CertTracker certTracker, final LanternTrustStore trustStore) {
this(null, model, certTracker, trustStore);
}

public LanternKeyStoreManager(final File rootDir, final Model model,
final CertTracker certTracker) {
final CertTracker certTracker, final LanternTrustStore trustStore) {
this.model = model;
this.trustStore = trustStore;
CONFIG_DIR = rootDir != null ? rootDir : LanternConstants.CONFIG_DIR;
KEYSTORE_FILE =
new File(CONFIG_DIR, "lantern_keystore.jks");
Expand All @@ -77,14 +80,16 @@ public LanternKeyStoreManager(final File rootDir, final Model model,
}
}
reset();
createTrustStore();
//createTrustStore();

/*
this.lanternTrustManager =
new LanternTrustManager(this, TRUSTSTORE_FILE, PASS, certTracker);
trustManagers = new TrustManager[] {
lanternTrustManager
};
*/
Runtime.getRuntime().addShutdownHook(new Thread (new Runnable() {
@Override
public void run() {
Expand Down Expand Up @@ -132,7 +137,7 @@ private void reset() {

createKeyStore();

waitForFile(KEYSTORE_FILE);
LanternUtils.waitForFile(KEYSTORE_FILE);
/*
log.info("Importing cert");
nativeCall("keytool", "-import", "-noprompt", "-file", CERT_FILE.getName(),
Expand Down Expand Up @@ -187,7 +192,7 @@ private void generateLocalCert(final String jid) {
"-file", CERT_FILE.getAbsolutePath());

log.info("Result of keytool -exportcert call: {}", exportCertResult);
waitForFile(CERT_FILE);
LanternUtils.waitForFile(CERT_FILE);

try {
final InputStream is = new FileInputStream(CERT_FILE);
Expand All @@ -208,29 +213,6 @@ private String getKey(final String normalizedAlias) {
PASS, "-keystore", KEYSTORE_FILE.getAbsolutePath());
}

/**
* The completion of the native calls is dependent on OS process
* scheduling, so we need to wait until files actually exist.
*
* @param file The file to wait for.
*/
private void waitForFile(final File file) {
int i = 0;
while (!file.isFile() && i < 100) {
try {
Thread.sleep(80);
i++;
} catch (final InterruptedException e) {
log.error("Interrupted?", e);
}
}
if (!file.isFile()) {
log.error("Still could not create file at: {}", file);
} else {
log.info("Successfully created file at: {}", file);
}
}

@Override
public String getBase64Cert(final String id) {
if (StringUtils.isBlank(localCert)) {
Expand Down Expand Up @@ -272,15 +254,22 @@ public char[] getKeyStorePassword() {
@Override
public void addBase64Cert(final String id, final String base64Cert)
throws IOException {
this.lanternTrustManager.addBase64Cert(id, base64Cert);
this.trustStore.addBase64Cert(id, base64Cert);
}

@Override
public TrustManager[] getTrustManagers() {
return Arrays.copyOf(trustManagers, trustManagers.length);
//return Arrays.copyOf(trustManagers, trustManagers.length);
return new TrustManager[] {};
}

public LanternTrustStore getTrustStore() {
return this.trustStore;
}

/*
public LanternTrustManager getTrustManager() {
return this.lanternTrustManager;
}
*/
}
6 changes: 5 additions & 1 deletion src/main/java/org/lantern/LanternSocketsUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ public SSLServerSocketFactory newTlsServerSocketFactory() {

// Initialize the SSLContext to work with our key managers.
final SSLContext serverContext = SSLContext.getInstance("TLS");
serverContext.init(kmf.getKeyManagers(), ksm.getTrustManagers(), null);

// TODO: We probably still need our own trust manager to verify
// peer certs.
//serverContext.init(kmf.getKeyManagers(), ksm.getTrustManagers(), null);
serverContext.init(kmf.getKeyManagers(), null, null);
return wrappedServerSocketFactory(serverContext.getServerSocketFactory());
} catch (final NoSuchAlgorithmException e) {
throw new Error("Could not create SSL server socket factory.", e);
Expand Down
167 changes: 167 additions & 0 deletions src/main/java/org/lantern/LanternTrustStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package org.lantern;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.SecureRandom;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.littleshoot.util.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LanternTrustStore {

private final Logger log = LoggerFactory.getLogger(getClass());

private static final String KEYSIZE = "2048";

private static final String PASS =
String.valueOf(new SecureRandom().nextLong());

private static final String ALG = "RSA";

private static final File TRUSTSTORE_FILE =
new File(LanternConstants.CONFIG_DIR,
String.valueOf(new SecureRandom().nextLong()));

private final CertTracker certTracker;

public LanternTrustStore(final CertTracker certTracker) {
this.certTracker = certTracker;
configureTrustStore();
}

public void addBase64Cert(final String fullJid, final String base64Cert)
throws IOException {
log.debug("Adding base 64 cert");
this.certTracker.addCert(base64Cert, fullJid);
// Alright, we need to decode the certificate from base 64, write it
// to a file, and then use keytool to import it.

// Here's the keytool doc:
/*
* -importcert [-v] [-noprompt] [-trustcacerts] [-protected]
[-alias <alias>]
[-file <cert_file>] [-keypass <keypass>]
[-keystore <keystore>] [-storepass <storepass>]
[-storetype <storetype>] [-providername <name>]
[-providerclass <provider_class_name> [-providerarg <arg>]] ...
[-providerpath <pathlist>]
*/
final byte[] decoded = Base64.decodeBase64(base64Cert);
final String normalizedAlias =
FileUtils.removeIllegalCharsFromFileName(fullJid);
final File certFile = new File(normalizedAlias);
OutputStream os = null;
try {
os = new FileOutputStream(certFile);
IOUtils.copy(new ByteArrayInputStream(decoded), os);
} catch (final IOException e) {
log.error("Could not write to file: " + certFile, e);
throw e;
} finally {
IOUtils.closeQuietly(os);
}
/*
* -delete [-v] [-protected] -alias <alias>
[-keystore <keystore>] [-storepass <storepass>]
[-storetype <storetype>] [-providername <name>]
[-providerclass <provider_class_name> [-providerarg <arg>]] ...
[-providerpath <pathlist>]
*/
// Make sure we delete the old one (will fail when it doesn't exist -
// this is expected).
final String deleteResult = LanternUtils.runKeytool("-delete",
"-alias", normalizedAlias, "-keystore",
TRUSTSTORE_FILE.getAbsolutePath(), "-storepass", PASS);
log.debug("Result of deleting old cert: {}", deleteResult);


// TODO: We should be able to just add it to the trust store here
// without saving

addCert(normalizedAlias, certFile);

/*
// TODO: Check importcert versus straight import.
final String importResult = LanternUtils.runKeytool("-importcert",
"-noprompt", "-alias", normalizedAlias, "-keystore",
TRUSTSTORE_FILE.getAbsolutePath(),
"-file", certFile.getAbsolutePath(),
"-keypass", PASS, "-storepass", PASS);
log.debug("Result of importing new cert: {}", importResult);
*/

// We need to reload the keystore with the latest data.
//this.trustStore = loadTrustStore();

// get rid of our imported file
certFile.delete();
certFile.deleteOnExit();
}


private void configureTrustStore() {
TRUSTSTORE_FILE.delete();
TRUSTSTORE_FILE.deleteOnExit();
createTrustStore();
addStaticCerts();
System.setProperty("javax.net.ssl.trustStore",
TRUSTSTORE_FILE.getAbsolutePath());
}

private void createTrustStore() {
if (TRUSTSTORE_FILE.isFile()) {
log.info("Trust store already exists");
return;
}
final String dummyCn = String.valueOf(new SecureRandom().nextLong());
//final String dummyCn = model.getNodeId();
log.debug("Dummy CN is: {}", dummyCn);
final String result = LanternUtils.runKeytool("-genkey", "-alias",
"foo", "-keysize", KEYSIZE, "-validity", "365", "-keyalg", ALG,
"-dname", "CN="+dummyCn, "-keystore",
TRUSTSTORE_FILE.getAbsolutePath(), "-keypass", PASS,
"-storepass", PASS);
log.debug("Got result of creating trust store: {}", result);
LanternUtils.waitForFile(TRUSTSTORE_FILE);
}

private void addStaticCerts() {
addCert("digicerthighassurancerootca", "certs/DigiCertHighAssuranceCA-3.cer");
addCert("littleproxy", "certs/littleporxy.cer");
addCert("equifaxsecureca", "certs/equifaxsecureca.cer");
}

private void addCert(final String alias, final String fileName) {
final File cert = new File(fileName);
addCert(alias, cert);
}

private void addCert(final String alias, final File cert) {
if (!cert.isFile()) {
log.error("No cert at "+cert);
System.exit(1);
}
log.debug("Importing cert");
final String result = LanternUtils.runKeytool("-import",
"-noprompt", "-file", cert.getName(),
"-alias", alias, "-keystore",
TRUSTSTORE_FILE.getAbsolutePath(), "-storepass", PASS);

log.debug("Result of running keytool: {}", result);
}

public String getTrustStorePath() {
return TRUSTSTORE_FILE.getAbsolutePath();
}

public String getTrustStorePassword() {
return PASS;
}
}
23 changes: 23 additions & 0 deletions src/main/java/org/lantern/LanternUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,29 @@ public static boolean isTrueOrFalse(final String str) {
}
return false;
}

/**
* The completion of the native calls is dependent on OS process
* scheduling, so we need to wait until files actually exist.
*
* @param file The file to wait for.
*/
public static void waitForFile(final File file) {
int i = 0;
while (!file.isFile() && i < 100) {
try {
Thread.sleep(80);
i++;
} catch (final InterruptedException e) {
LOG.error("Interrupted?", e);
}
}
if (!file.isFile()) {
LOG.error("Still could not create file at: {}", file);
} else {
LOG.info("Successfully created file at: {}", file);
}
}
}


10 changes: 7 additions & 3 deletions src/main/java/org/lantern/LanternXmppUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,13 @@ public ConnectionConfiguration xmppConfig(final ProxyInfo proxyInfo) {
config.setVerifyChainEnabled(true);
config.setVerifyRootCAEnabled(true);
config.setSelfSignedCertificateEnabled(false);
final LanternTrustManager tm = this.keyStoreManager.getTrustManager();
config.setTruststorePath(tm.getTruststorePath());
config.setTruststorePassword(tm.getTruststorePassword());
//final LanternTrustManager tm = this.keyStoreManager.getTrustManager();
//config.setTruststorePath(tm.getTruststorePath());
//config.setTruststorePassword(tm.getTruststorePassword());

final LanternTrustStore ts = this.keyStoreManager.getTrustStore();
config.setTruststorePath(ts.getTrustStorePath());
config.setTruststorePassword(ts.getTrustStorePassword());

final String[] cipherSuites = new String[] {
//"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
Expand Down
Loading

0 comments on commit df86114

Please sign in to comment.