i2ptunnel: Add alt names in standard and irc client tunnel certs

This commit is contained in:
zzz
2018-02-25 16:31:48 +00:00
parent 4c02c1f58b
commit 27042f9930
3 changed files with 78 additions and 8 deletions

View File

@@ -14,8 +14,10 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicLong;
@@ -700,8 +702,29 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
Properties opts = getTunnel().getClientOptions();
boolean useSSL = Boolean.parseBoolean(opts.getProperty(PROP_USE_SSL));
if (useSSL) {
// was already done in web/IndexBean.java when saving the config
boolean wasCreated = SSLClientUtil.verifyKeyStore(opts);
// was already done in GeneralHelper.updateTunnelConfig() when saving the config
// we should never be generating the cert here.
// add the local interface and all targets to the cert
Set<String> altNames = new HashSet<String>(4);
String intfc = getTunnel().listenHost;
if (intfc != null && !intfc.equals("0.0.0.0") && !intfc.equals("::") &&
!intfc.equals("0:0:0:0:0:0:0:0"))
altNames.add(intfc);
// We can't easily get to the targetDestination property,
// or the _addrs List in I2PTunnelClient, or the target argument in I2PTunnel from here,
// but it shouldn't matter, we should never be generating the cert here.
//String targets = ...
//if (targets != null) {
// StringTokenizer tok = new StringTokenizer(targets, ", ");
// while (tok.hasMoreTokens()) {
// String h = tok.nextToken();
// int colon = h.indexOf(':');
// if (colon >= 0)
// h = h.substring(0, colon);
// altNames.add(h);
// }
//}
boolean wasCreated = SSLClientUtil.verifyKeyStore(opts, "", altNames);
if (wasCreated) {
// From here, we can't save the config.
// We shouldn't get here, as SSL isn't the default, so it would

View File

@@ -6,7 +6,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLServerSocketFactory;
@@ -56,6 +58,22 @@ public class SSLClientUtil {
* @throws IOException on creation fail
*/
public static boolean verifyKeyStore(Properties opts, String optPfx) throws IOException {
return verifyKeyStore(opts, optPfx, null);
}
/**
* Create a new selfsigned cert and keystore and pubkey cert if they don't exist.
* May take a while.
*
* @param opts in/out, updated if rv is true
* @param optPfx add this prefix when getting/setting options
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
* @return false if it already exists; if true, caller must save opts
* @throws IOException on creation fail
* @since 0.9.34 added altNames param
*/
public static boolean verifyKeyStore(Properties opts, String optPfx, Set<String> altNames) throws IOException {
String name = opts.getProperty(optPfx + PROP_KEY_ALIAS);
if (name == null) {
name = KeyStoreUtil.randomString();
@@ -79,7 +97,7 @@ public class SSLClientUtil {
if (!sdir.mkdirs())
throw new IOException("Unable to create keystore " + ks);
}
boolean rv = createKeyStore(ks, name, opts, optPfx);
boolean rv = createKeyStore(ks, name, opts, optPfx, altNames);
if (!rv)
throw new IOException("Unable to create keystore " + ks);
@@ -92,20 +110,22 @@ public class SSLClientUtil {
/**
* Call out to keytool to create a new keystore with a keypair in it.
* Create a new keystore with a keypair in it.
*
* @param name used in CNAME
* @param opts in/out, updated if rv is true, must contain PROP_KEY_ALIAS
* @param optPfx add this prefix when getting/setting options
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
* @return success, if true, opts will have password properties added to be saved
*/
private static boolean createKeyStore(File ks, String name, Properties opts, String optPfx) {
private static boolean createKeyStore(File ks, String name, Properties opts, String optPfx, Set<String> altNames) {
// make a random 48 character password (30 * 8 / 5)
String keyPassword = KeyStoreUtil.randomString();
String cname = "localhost";
String keyName = opts.getProperty(optPfx + PROP_KEY_ALIAS);
boolean success = KeyStoreUtil.createKeys(ks, keyName, cname, "I2PTUNNEL", keyPassword);
boolean success = KeyStoreUtil.createKeys(ks, keyName, cname, altNames, "I2PTUNNEL", keyPassword);
if (success) {
success = ks.exists();
if (success) {
@@ -115,7 +135,8 @@ public class SSLClientUtil {
}
if (success) {
logAlways("Created self-signed certificate for " + cname + " in keystore: " + ks.getAbsolutePath() + "\n" +
"The certificate was generated randomly, and is not associated with your " +
"The certificate was generated randomly.\n" +
"Unless you have changed the default settings, the certificate is not associated with your " +
"IP address, host name, router identity, or destination keys.");
} else {
error("Failed to create I2PTunnel SSL keystore.\n" +

View File

@@ -3,9 +3,11 @@ package net.i2p.i2ptunnel.ui;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import net.i2p.I2PAppContext;
@@ -14,6 +16,7 @@ import net.i2p.client.I2PClient;
import net.i2p.crypto.SigType;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKeyFile;
import net.i2p.i2ptunnel.I2PTunnelClientBase;
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
@@ -25,6 +28,7 @@ import net.i2p.i2ptunnel.SSLClientUtil;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.i2ptunnel.web.Messages;
import net.i2p.util.ConvertToHash;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
import net.i2p.util.SecureFile;
@@ -94,8 +98,30 @@ public class GeneralHelper {
// Down in I2PTunnelClientBase it's very hard to save the config.
//
if (Boolean.parseBoolean(props.getProperty(OPT + I2PTunnelClientBase.PROP_USE_SSL))) {
// add the local interface and all targets to the cert
String intfc = props.getProperty(TunnelController.PROP_INTFC);
Set<String> altNames = new HashSet<String>(4);
if (intfc != null && !intfc.equals("0.0.0.0") && !intfc.equals("::") &&
!intfc.equals("0:0:0:0:0:0:0:0"))
altNames.add(intfc);
String tgts = props.getProperty(TunnelController.PROP_DEST);
if (tgts != null) {
altNames.add(intfc);
String[] hosts = DataHelper.split(tgts, "[ ,]");
for (String h : hosts) {
int colon = h.indexOf(':');
if (colon >= 0)
h = h.substring(0, colon);
altNames.add(h);
if (!h.endsWith(".b32.i2p")) {
Hash hash = ConvertToHash.getHash(h);
if (hash != null)
altNames.add(hash.toBase32());
}
}
}
try {
boolean created = SSLClientUtil.verifyKeyStore(props, OPT);
boolean created = SSLClientUtil.verifyKeyStore(props, OPT, altNames);
if (created) {
// config now contains new keystore props
String name = props.getProperty(TunnelController.PROP_NAME, "");