diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
index 57f6ec870..1e8ea3f62 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
@@ -148,7 +148,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
setEntry(headers, "Accept-encoding", "");
socket.setReadTimeout(readTimeout);
- Socket s = new Socket(remoteHost, remotePort);
+ Socket s = getSocket(remoteHost, remotePort);
long afterSocket = getTunnel().getContext().clock().now();
// instead of i2ptunnelrunner, use something that reads the HTTP
// request from the socket, modifies the headers, sends the request to the
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
index 36f998242..1b651c34a 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java
@@ -137,7 +137,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
buf.append("\r\n");
modifiedRegistration = buf.toString();
}
- Socket s = new Socket(remoteHost, remotePort);
+ Socket s = getSocket(remoteHost, remotePort);
new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(), null);
} catch (SocketException ex) {
try {
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
index 1b16007b7..47cea2bb3 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
@@ -14,6 +14,7 @@ import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
+import java.security.GeneralSecurityException;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.Executors;
@@ -33,6 +34,7 @@ import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.data.Base64;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PAppThread;
+import net.i2p.util.I2PSSLSocketFactory;
import net.i2p.util.Log;
public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
@@ -43,11 +45,13 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
private final Object lock = new Object();
protected final Object slock = new Object();
+ protected final Object sslLock = new Object();
protected final InetAddress remoteHost;
protected final int remotePort;
private final boolean _usePool;
protected final Logging l;
+ private I2PSSLSocketFactory _sslFactory;
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000;
/** default timeout to 5 minutes - override if desired */
@@ -56,6 +60,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/** do we use threads? default true (ignored for standard servers, always false) */
private static final String PROP_USE_POOL = "i2ptunnel.usePool";
private static final boolean DEFAULT_USE_POOL = true;
+ public static final String PROP_USE_SSL = "useSSL";
/** apparently unused */
protected static volatile long __serverId = 0;
/** max number of threads - this many slowlorisses will DOS this server, but too high could OOM the JVM */
@@ -462,17 +467,17 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
if (_log.shouldLog(Log.INFO))
_log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() +
" from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort());
- long afterAccept = I2PAppContext.getGlobalContext().clock().now();
+ long afterAccept = getTunnel().getContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
socket.setReadTimeout(readTimeout);
- Socket s = new Socket(remoteHost, remotePort);
- afterSocket = I2PAppContext.getGlobalContext().clock().now();
+ Socket s = getSocket(remoteHost, remotePort);
+ afterSocket = getTunnel().getContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, null);
- long afterHandle = I2PAppContext.getGlobalContext().clock().now();
+ long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request for " + remoteHost + ':' + remotePort +
@@ -487,5 +492,31 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
_log.error("Error while waiting for I2PConnections", ex);
}
}
+
+ /**
+ * Get a regular or SSL socket depending on config
+ *
+ * @since 0.9.9
+ */
+ protected Socket getSocket(InetAddress remoteHost, int remotePort) throws IOException {
+ String opt = getTunnel().getClientOptions().getProperty(PROP_USE_SSL);
+ if (Boolean.parseBoolean(opt)) {
+ synchronized(sslLock) {
+ if (_sslFactory == null) {
+ try {
+ _sslFactory = new I2PSSLSocketFactory(getTunnel().getContext(),
+ true, "certificates/i2ptunnel");
+ } catch (GeneralSecurityException gse) {
+ IOException ioe = new IOException("SSL Fail");
+ ioe.initCause(gse);
+ throw ioe;
+ }
+ }
+ }
+ return _sslFactory.createSocket(remoteHost, remotePort);
+ } else {
+ return new Socket(remoteHost, remotePort);
+ }
+ }
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
index 3a5c001f6..f5fefdbd0 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
@@ -407,7 +407,7 @@ public class TunnelController implements Logging {
/**
* These are the ones stored with a prefix of "option."
*
- * @return keys with the "option." prefix stripped
+ * @return keys with the "option." prefix stripped, non-null
* @since 0.9.1 Much better than getClientOptions()
*/
public Properties getClientOptionProps() {
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
index 5d8ec7dcc..10316f999 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
@@ -31,6 +31,7 @@ import net.i2p.i2ptunnel.I2PTunnelConnectClient;
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
+import net.i2p.i2ptunnel.I2PTunnelServer;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.util.Addresses;
@@ -770,6 +771,21 @@ public class IndexBean {
_booleanOptions.add(I2PTunnelIRCClient.PROP_DCC);
}
+ /** @since 0.9.9 */
+ public void setUseSSL(String moo) {
+ _booleanOptions.add(I2PTunnelServer.PROP_USE_SSL);
+ }
+
+ /** @since 0.9.9 */
+ public boolean isSSLEnabled(int tunnel) {
+ TunnelController tun = getController(tunnel);
+ if (tun != null) {
+ Properties opts = tun.getClientOptionProps();
+ return Boolean.parseBoolean(opts.getProperty(I2PTunnelServer.PROP_USE_SSL));
+ }
+ return false;
+ }
+
protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList";
protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList";
@@ -1149,7 +1165,8 @@ public class IndexBean {
I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH
};
private static final String _booleanServerOpts[] = {
- "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST
+ "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST,
+ I2PTunnelServer.PROP_USE_SSL
};
private static final String _otherClientOpts[] = {
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime",
diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp
index dce9e1f79..ea212eefb 100644
--- a/apps/i2ptunnel/jsp/editServer.jsp
+++ b/apps/i2ptunnel/jsp/editServer.jsp
@@ -121,6 +121,14 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
+ <% if (!"streamrserver".equals(tunnelType)) { %>
+
+
+ class="tickbox" />
+
+ <% } /* !streamrserver */ %>
<% if ("httpbidirserver".equals(tunnelType)) { %>
diff --git a/core/java/src/net/i2p/client/I2CPSSLSocketFactory.java b/core/java/src/net/i2p/client/I2CPSSLSocketFactory.java
deleted file mode 100644
index d6bba72a5..000000000
--- a/core/java/src/net/i2p/client/I2CPSSLSocketFactory.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 29389bdea..ab81dd8fa 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -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);
diff --git a/core/java/src/net/i2p/client/I2PSimpleSession.java b/core/java/src/net/i2p/client/I2PSimpleSession.java
index 35b6422e2..e3a911e19 100644
--- a/core/java/src/net/i2p/client/I2PSimpleSession.java
+++ b/core/java/src/net/i2p/client/I2PSimpleSession.java
@@ -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();
diff --git a/core/java/src/net/i2p/util/I2PSSLSocketFactory.java b/core/java/src/net/i2p/util/I2PSSLSocketFactory.java
new file mode 100644
index 000000000..0751cdc28
--- /dev/null
+++ b/core/java/src/net/i2p/util/I2PSSLSocketFactory.java
@@ -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();
+ }
+}
diff --git a/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java b/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java
index 9e8ba08ce..d65f75bce 100644
--- a/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java
+++ b/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java
@@ -118,8 +118,8 @@ class SSLClientListenerRunner extends ClientListenerRunner {
* so the clients can get to it.
*/
private void exportCert(File ks) {
- File sdir = new SecureDirectory(_context.getConfigDir(), "certificates");
- if (sdir.exists() || sdir.mkdir()) {
+ File sdir = new SecureDirectory(_context.getConfigDir(), "certificates/i2cp");
+ if (sdir.exists() || sdir.mkdirs()) {
String ksPass = _context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
File out = new File(sdir, ASCII_KEYFILE);
boolean success = KeyStoreUtil.exportCert(ks, ksPass, KEY_ALIAS, out);