* I2CP: Move SSL client socket code to util,

move cert location to certificates/i2cp.
 * I2PTunnel: Support SSL for connection to local server
   for Standard, HTTP, and IRC server tunnels.
   Put server cert in certificates/i2ptunnel if necessary.
This commit is contained in:
zzz
2013-10-17 13:22:17 +00:00
parent 09548358fa
commit 5f7a761e42
12 changed files with 210 additions and 137 deletions

View File

@ -1,119 +0,0 @@
package net.i2p.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import net.i2p.I2PAppContext;
import net.i2p.crypto.KeyStoreUtil;
import net.i2p.util.Log;
/**
* Loads trusted ASCII certs from ~/.i2p/certificates/ and $CWD/certificates/.
* Keeps a single static SSLContext for the whole JVM.
*
* @author zzz
* @since 0.8.3
*/
class I2CPSSLSocketFactory {
private static final Object _initLock = new Object();
private static SSLSocketFactory _factory;
private static final String CERT_DIR = "certificates";
/**
* Initializes the static SSL Context if required, then returns a socket
* to the host.
*
* @param ctx just for logging
* @throws IOException on init error or usual socket errors
*/
public static Socket createSocket(I2PAppContext ctx, String host, int port) throws IOException {
synchronized(_initLock) {
if (_factory == null) {
initSSLContext(ctx);
if (_factory == null)
throw new IOException("Unable to create SSL Context for I2CP Client");
info(ctx, "I2CP Client-side SSL Context initialized");
}
}
return _factory.createSocket(host, port);
}
/**
* Loads certs from
* the ~/.i2p/certificates/ and $CWD/certificates/ directories.
*/
private static void initSSLContext(I2PAppContext context) {
KeyStore ks;
try {
ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, "".toCharArray());
} catch (GeneralSecurityException gse) {
error(context, "Key Store init error", gse);
return;
} catch (IOException ioe) {
error(context, "Key Store init error", ioe);
return;
}
File dir = new File(context.getConfigDir(), CERT_DIR);
int adds = KeyStoreUtil.addCerts(dir, ks);
int totalAdds = adds;
if (adds > 0)
info(context, "Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
File dir2 = new File(System.getProperty("user.dir"), CERT_DIR);
if (!dir.getAbsolutePath().equals(dir2.getAbsolutePath())) {
adds = KeyStoreUtil.addCerts(dir2, ks);
totalAdds += adds;
if (adds > 0)
info(context, "Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
}
if (totalAdds > 0) {
info(context, "Loaded total of " + totalAdds + " new trusted certificates");
} else {
error(context, "No trusted certificates loaded (looked in " +
dir.getAbsolutePath() + (dir.getAbsolutePath().equals(dir2.getAbsolutePath()) ? "" : (" and " + dir2.getAbsolutePath())) +
", I2CP SSL client connections will fail. " +
"Copy the file certificates/i2cp.local.crt from the router to the directory.", null);
// don't continue, since we didn't load the system keystore, we have nothing.
return;
}
try {
SSLContext sslc = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
sslc.init(null, tmf.getTrustManagers(), context.random());
_factory = sslc.getSocketFactory();
} catch (GeneralSecurityException gse) {
error(context, "SSL context init error", gse);
}
}
/** @since 0.9.8 */
private static void info(I2PAppContext ctx, String msg) {
log(ctx, Log.INFO, msg, null);
}
/** @since 0.9.8 */
private static void error(I2PAppContext ctx, String msg, Throwable t) {
log(ctx, Log.ERROR, msg, t);
}
/** @since 0.9.8 */
private static void log(I2PAppContext ctx, int level, String msg, Throwable t) {
Log l = ctx.logManager().getLog(I2CPSSLSocketFactory.class);
l.log(level, msg, t);
}
}

View File

@ -16,6 +16,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -43,6 +44,7 @@ import net.i2p.internal.I2CPMessageQueue;
import net.i2p.internal.InternalClientManager;
import net.i2p.internal.QueuedI2CPMessageReader;
import net.i2p.util.I2PAppThread;
import net.i2p.util.I2PSSLSocketFactory;
import net.i2p.util.LHMCache;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
@ -438,10 +440,18 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_queue = mgr.connect();
_reader = new QueuedI2CPMessageReader(_queue, this);
} else {
if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL)))
_socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
else
if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL))) {
try {
I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
_socket = fact.createSocket(_hostname, _portNum);
} catch (GeneralSecurityException gse) {
IOException ioe = new IOException("SSL Fail");
ioe.initCause(gse);
throw ioe;
}
} else {
_socket = new Socket(_hostname, _portNum);
}
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
OutputStream out = _socket.getOutputStream();
out.write(I2PClient.PROTOCOL_BYTE);

View File

@ -11,6 +11,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.Properties;
import net.i2p.I2PAppContext;
@ -19,6 +20,7 @@ import net.i2p.data.i2cp.DestReplyMessage;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.internal.InternalClientManager;
import net.i2p.internal.QueuedI2CPMessageReader;
import net.i2p.util.I2PSSLSocketFactory;
/**
* Create a new session for doing naming and bandwidth queries only. Do not create a Destination.
@ -67,10 +69,18 @@ class I2PSimpleSession extends I2PSessionImpl2 {
_queue = mgr.connect();
_reader = new QueuedI2CPMessageReader(_queue, this);
} else {
if (Boolean.parseBoolean(getOptions().getProperty(PROP_ENABLE_SSL)))
_socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
else
if (Boolean.parseBoolean(getOptions().getProperty(PROP_ENABLE_SSL))) {
try {
I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
_socket = fact.createSocket(_hostname, _portNum);
} catch (GeneralSecurityException gse) {
IOException ioe = new IOException("SSL Fail");
ioe.initCause(gse);
throw ioe;
}
} else {
_socket = new Socket(_hostname, _portNum);
}
OutputStream out = _socket.getOutputStream();
out.write(I2PClient.PROTOCOL_BYTE);
out.flush();

View File

@ -0,0 +1,109 @@
package net.i2p.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import net.i2p.I2PAppContext;
import net.i2p.crypto.KeyStoreUtil;
/**
* Loads trusted ASCII certs from ~/.i2p/certificates/ and $I2P/certificates/.
*
* @author zzz
* @since 0.9.9 moved from ../client, original since 0.8.3
*/
public class I2PSSLSocketFactory {
private final SSLSocketFactory _factory;
/**
* @param relativeCertPath e.g. "certificates/i2cp"
* @since 0.9.9 was static
*/
public I2PSSLSocketFactory(I2PAppContext context, boolean loadSystemCerts, String relativeCertPath)
throws GeneralSecurityException {
_factory = initSSLContext(context, loadSystemCerts, relativeCertPath);
}
/**
* Returns a socket to the host.
*/
public Socket createSocket(String host, int port) throws IOException {
return _factory.createSocket(host, port);
}
/**
* Returns a socket to the host.
* @since 0.9.9
*/
public Socket createSocket(InetAddress host, int port) throws IOException {
return _factory.createSocket(host, port);
}
/**
* Loads certs from
* the ~/.i2p/certificates/ and $I2P/certificates/ directories.
*/
private SSLSocketFactory initSSLContext(I2PAppContext context, boolean loadSystemCerts, String relativeCertPath)
throws GeneralSecurityException {
Log log = context.logManager().getLog(I2PSSLSocketFactory.class);
KeyStore ks;
if (loadSystemCerts) {
ks = KeyStoreUtil.loadSystemKeyStore();
if (ks == null)
throw new GeneralSecurityException("Key Store init error");
} else {
try {
ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, "".toCharArray());
} catch (IOException ioe) {
throw new GeneralSecurityException("Key Store init error", ioe);
}
}
File dir = new File(context.getConfigDir(), relativeCertPath);
int adds = KeyStoreUtil.addCerts(dir, ks);
int totalAdds = adds;
if (adds > 0) {
if (log.shouldLog(Log.INFO))
log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
}
File dir2 = new File(context.getBaseDir(), relativeCertPath);
if (!dir.getAbsolutePath().equals(dir2.getAbsolutePath())) {
adds = KeyStoreUtil.addCerts(dir2, ks);
totalAdds += adds;
if (adds > 0) {
if (log.shouldLog(Log.INFO))
log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
}
}
if (totalAdds > 0 || loadSystemCerts) {
if (log.shouldLog(Log.INFO))
log.info("Loaded total of " + totalAdds + " new trusted certificates");
} else {
String msg = "No trusted certificates loaded (looked in " +
dir.getAbsolutePath() + (dir.getAbsolutePath().equals(dir2.getAbsolutePath()) ? "" : (" and " + dir2.getAbsolutePath())) +
", SSL connections will fail. " +
"Copy the cert in " + relativeCertPath + " from the router to the directory.";
// don't continue, since we didn't load the system keystore, we have nothing.
throw new GeneralSecurityException(msg);
}
SSLContext sslc = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
sslc.init(null, tmf.getTrustManagers(), context.random());
return sslc.getSocketFactory();
}
}