summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
authorMark Spruiell <mes@zeroc.com>2006-04-13 21:20:25 +0000
committerMark Spruiell <mes@zeroc.com>2006-04-13 21:20:25 +0000
commit074bf1d6b113fffa7cf9df6433b90311f2199ea1 (patch)
treeaff0594b90bceb6f6786b81ebecbcb16e735bb5f /java/src
parentSSL fix (diff)
downloadice-074bf1d6b113fffa7cf9df6433b90311f2199ea1.tar.bz2
ice-074bf1d6b113fffa7cf9df6433b90311f2199ea1.tar.xz
ice-074bf1d6b113fffa7cf9df6433b90311f2199ea1.zip
cleanup, refactoring, align with C++/C#
Diffstat (limited to 'java/src')
-rw-r--r--java/src/IceSSL/AcceptorI.java242
-rw-r--r--java/src/IceSSL/CertificateVerifier.java23
-rw-r--r--java/src/IceSSL/ConnectorI.java355
-rw-r--r--java/src/IceSSL/Context.java453
-rw-r--r--java/src/IceSSL/Instance.java107
-rw-r--r--java/src/IceSSL/KeyManagerI.java74
-rw-r--r--java/src/IceSSL/Plugin.java28
-rw-r--r--java/src/IceSSL/PluginI.java14
-rw-r--r--java/src/IceSSL/VerifyInfo.java45
9 files changed, 1042 insertions, 299 deletions
diff --git a/java/src/IceSSL/AcceptorI.java b/java/src/IceSSL/AcceptorI.java
index 1c0b94a0d78..5229c896160 100644
--- a/java/src/IceSSL/AcceptorI.java
+++ b/java/src/IceSSL/AcceptorI.java
@@ -73,20 +73,150 @@ class AcceptorI implements IceInternal.Acceptor
}
_fd.setSoTimeout(timeout);
fd = (javax.net.ssl.SSLSocket)_fd.accept();
+
+ //
+ // Check whether this socket is the result of a call to connectToSelf.
+ // Despite the fact that connectToSelf immediately closes the socket,
+ // the server-side handshake process does not raise an exception.
+ // Furthermore, we can't simply proceed with the regular handshake
+ // process because we don't want to pass such a socket to the
+ // certificate verifier (if any).
+ //
+ // In order to detect a call to connectToSelf, we compare the remote
+ // address of the newly-accepted socket to that in _connectToSelfAddr.
+ //
+ java.net.SocketAddress remoteAddr = fd.getRemoteSocketAddress();
+ synchronized(this)
+ {
+ if(remoteAddr.equals(_connectToSelfAddr))
+ {
+ try
+ {
+ fd.close();
+ }
+ catch(java.io.IOException e)
+ {
+ }
+ return null;
+ }
+ }
+
fd.setUseClientMode(false);
+
+ //
+ // getSession blocks until the initial handshake completes.
+ //
+ if(timeout == 0)
+ {
+ fd.getSession();
+ }
+ else
+ {
+ HandshakeThread ht = new HandshakeThread(fd);
+ ht.start();
+ if(!ht.waitForHandshake(timeout))
+ {
+ throw new Ice.TimeoutException();
+ }
+ }
+
+ if(!_ctx.verifyPeer(fd, "", true))
+ {
+ try
+ {
+ fd.close();
+ }
+ catch(java.io.IOException e)
+ {
+ }
+ return null;
+ }
}
catch(java.net.SocketTimeoutException ex)
{
+ if(fd != null)
+ {
+ try
+ {
+ fd.close();
+ }
+ catch(java.io.IOException e)
+ {
+ }
+ }
Ice.TimeoutException e = new Ice.TimeoutException();
e.initCause(ex);
throw e;
}
+ catch(javax.net.ssl.SSLException ex)
+ {
+ if(fd != null)
+ {
+ try
+ {
+ fd.close();
+ }
+ catch(java.io.IOException e)
+ {
+ }
+ }
+
+ //
+ // Unfortunately, the situation where the cipher suite does not match
+ // the certificates is not detected until accept is called. If we were
+ // to throw a LocalException, the IncomingConnectionFactory would
+ // simply log it and call accept again, resulting in an infinite loop.
+ // To avoid this problem, we check for the special case and throw
+ // an exception that IncomingConnectionFactory doesn't trap.
+ //
+ if(ex.getMessage().toLowerCase().startsWith("no available certificate corresponds to the ssl cipher " +
+ "suites which are enabled"))
+ {
+ RuntimeException e = new RuntimeException();
+ e.initCause(ex);
+ throw e;
+ }
+
+ Ice.SecurityException e = new Ice.SecurityException();
+ e.initCause(ex);
+ throw e;
+ }
catch(java.io.IOException ex)
{
+ if(fd != null)
+ {
+ try
+ {
+ fd.close();
+ }
+ catch(java.io.IOException e)
+ {
+ }
+ }
+
+ if(IceInternal.Network.connectionLost(ex))
+ {
+ throw new Ice.ConnectionLostException();
+ }
+
Ice.SocketException e = new Ice.SocketException();
e.initCause(ex);
throw e;
}
+ catch(RuntimeException ex)
+ {
+ if(fd != null)
+ {
+ try
+ {
+ fd.close();
+ }
+ catch(java.io.IOException e)
+ {
+ }
+ }
+ throw ex;
+ }
if(_instance.networkTraceLevel() >= 1)
{
@@ -94,6 +224,11 @@ class AcceptorI implements IceInternal.Acceptor
_logger.trace(_instance.networkTraceCategory(), s);
}
+ if(_instance.securityTraceLevel() > 0)
+ {
+ _ctx.traceConnection(fd, true);
+ }
+
return new TransceiverI(_instance, fd);
}
@@ -102,7 +237,16 @@ class AcceptorI implements IceInternal.Acceptor
{
java.nio.channels.SocketChannel fd = IceInternal.Network.createTcpSocket();
IceInternal.Network.setBlock(fd, false);
- IceInternal.Network.doConnect(fd, _addr, -1);
+ synchronized(this)
+ {
+ //
+ // connectToSelf is called to wake up the thread blocked in
+ // accept. We remember the originating address for use in
+ // accept. See accept for details.
+ //
+ IceInternal.Network.doConnect(fd, _addr, -1);
+ _connectToSelfAddr = (java.net.InetSocketAddress)fd.socket().getLocalSocketAddress();
+ }
IceInternal.Network.closeSocket(fd);
}
@@ -175,6 +319,17 @@ class AcceptorI implements IceInternal.Acceptor
}
String[] cipherSuites = _ctx.filterCiphers(_fd.getSupportedCipherSuites(), _fd.getEnabledCipherSuites());
+ try
+ {
+ _fd.setEnabledCipherSuites(cipherSuites);
+ }
+ catch(IllegalArgumentException ex)
+ {
+ Ice.SecurityException e = new Ice.SecurityException();
+ e.reason = "invalid ciphersuite";
+ e.initCause(ex);
+ throw e;
+ }
if(_instance.securityTraceLevel() > 0)
{
StringBuffer s = new StringBuffer();
@@ -185,7 +340,22 @@ class AcceptorI implements IceInternal.Acceptor
}
_logger.trace(_instance.securityTraceCategory(), s.toString());
}
- _fd.setEnabledCipherSuites(cipherSuites);
+
+ String[] protocols = _ctx.getProtocols();
+ if(protocols != null)
+ {
+ try
+ {
+ _fd.setEnabledProtocols(protocols);
+ }
+ catch(IllegalArgumentException ex)
+ {
+ Ice.SecurityException e = new Ice.SecurityException();
+ e.reason = "invalid protocol";
+ e.initCause(ex);
+ throw e;
+ }
+ }
}
catch(java.io.IOException ex)
{
@@ -215,10 +385,78 @@ class AcceptorI implements IceInternal.Acceptor
super.finalize();
}
+ private static class HandshakeThread extends Thread
+ {
+ HandshakeThread(javax.net.ssl.SSLSocket fd)
+ {
+ _fd = fd;
+ _ok = false;
+ }
+
+ public void
+ run()
+ {
+ try
+ {
+ _fd.getSession();
+ synchronized(this)
+ {
+ _ok = true;
+ notifyAll();
+ }
+
+ }
+ catch(RuntimeException ex)
+ {
+ synchronized(this)
+ {
+ _ex = ex;
+ notifyAll();
+ }
+ }
+ }
+
+ boolean
+ waitForHandshake(int timeout)
+ {
+ boolean result = false;
+
+ synchronized(this)
+ {
+ while(!_ok && _ex == null)
+ {
+ try
+ {
+ wait(timeout);
+ break;
+ }
+ catch(InterruptedException ex)
+ {
+ continue;
+ }
+ }
+
+ if(_ex != null)
+ {
+ throw _ex;
+ }
+
+ result = _ok;
+ }
+
+ return result;
+ }
+
+ private javax.net.ssl.SSLSocket _fd;
+ private boolean _ok;
+ private RuntimeException _ex;
+ }
+
private Instance _instance;
private Context _ctx;
private Ice.Logger _logger;
private javax.net.ssl.SSLServerSocket _fd;
private int _backlog;
private java.net.InetSocketAddress _addr;
+ private java.net.InetSocketAddress _connectToSelfAddr;
}
diff --git a/java/src/IceSSL/CertificateVerifier.java b/java/src/IceSSL/CertificateVerifier.java
new file mode 100644
index 00000000000..485275fce98
--- /dev/null
+++ b/java/src/IceSSL/CertificateVerifier.java
@@ -0,0 +1,23 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package IceSSL;
+
+//
+// An application can customize the certificate verification process
+// by implementing the CertificateVerifier interface.
+//
+public interface CertificateVerifier
+{
+ //
+ // Return true to allow a connection using the provided certificate
+ // information, or false to reject the connection.
+ //
+ boolean verify(VerifyInfo info);
+}
diff --git a/java/src/IceSSL/ConnectorI.java b/java/src/IceSSL/ConnectorI.java
index 52d1269059e..785eb02d359 100644
--- a/java/src/IceSSL/ConnectorI.java
+++ b/java/src/IceSSL/ConnectorI.java
@@ -11,148 +11,14 @@ package IceSSL;
final class ConnectorI implements IceInternal.Connector
{
- private static class ConnectThread extends Thread
- {
- ConnectThread(javax.net.ssl.SSLContext ctx, java.net.InetSocketAddress addr)
- {
- _ctx = ctx;
- _addr = addr;
- }
-
- public void
- run()
- {
- try
- {
- javax.net.SocketFactory factory = _ctx.getSocketFactory();
- javax.net.ssl.SSLSocket fd =
- (javax.net.ssl.SSLSocket)factory.createSocket(_addr.getAddress(), _addr.getPort());
- synchronized(this)
- {
- _fd = fd;
- notifyAll();
- }
- }
- catch(java.io.IOException ex)
- {
- synchronized(this)
- {
- _ex = ex;
- notifyAll();
- }
- }
- }
-
- javax.net.ssl.SSLSocket
- getFd(int timeout)
- throws java.io.IOException
- {
- javax.net.ssl.SSLSocket fd = null;
-
- synchronized(this)
- {
- while(_fd == null && _ex == null)
- {
- try
- {
- wait(timeout);
- break;
- }
- catch(InterruptedException ex)
- {
- continue;
- }
- }
-
- if(_ex != null)
- {
- throw _ex;
- }
-
- fd = _fd;
- _fd = null;
- }
-
- return fd;
- }
-
- private javax.net.ssl.SSLContext _ctx;
- private java.net.InetSocketAddress _addr;
- private javax.net.ssl.SSLSocket _fd;
- private java.io.IOException _ex;
- }
-
- private static class HandshakeThread extends Thread
- {
- HandshakeThread(javax.net.ssl.SSLSocket fd)
- {
- _fd = fd;
- _ok = false;
- }
-
- public void
- run()
- {
- try
- {
- _fd.startHandshake();
- synchronized(this)
- {
- _ok = true;
- notifyAll();
- }
-
- }
- catch(java.io.IOException ex)
- {
- synchronized(this)
- {
- _ex = ex;
- notifyAll();
- }
- }
- }
-
- boolean
- waitForHandshake(int timeout)
- throws java.io.IOException
- {
- boolean result = false;
-
- synchronized(this)
- {
- while(!_ok && _ex == null)
- {
- try
- {
- wait(timeout);
- break;
- }
- catch(InterruptedException ex)
- {
- continue;
- }
- }
-
- if(_ex != null)
- {
- throw _ex;
- }
-
- result = _ok;
- }
-
- return result;
- }
-
- private javax.net.ssl.SSLSocket _fd;
- private boolean _ok;
- private java.io.IOException _ex;
- }
-
public IceInternal.Transceiver
connect(int timeout)
{
+ //
+ // The plugin may not be fully initialized.
+ //
+ Context ctx = _instance.clientContext();
+
if(_instance.networkTraceLevel() >= 2)
{
String s = "trying to establish ssl connection to " + toString();
@@ -167,7 +33,7 @@ final class ConnectorI implements IceInternal.Connector
//
if(timeout >= 0)
{
- ConnectThread ct = new ConnectThread(_ctx.sslContext(), _addr);
+ ConnectThread ct = new ConnectThread(ctx.sslContext(), _addr);
ct.start();
fd = ct.getFd(timeout == 0 ? 1 : timeout);
if(fd == null)
@@ -177,13 +43,24 @@ final class ConnectorI implements IceInternal.Connector
}
else
{
- javax.net.SocketFactory factory = _ctx.sslContext().getSocketFactory();
+ javax.net.SocketFactory factory = ctx.sslContext().getSocketFactory();
fd = (javax.net.ssl.SSLSocket)factory.createSocket(_addr.getAddress(), _addr.getPort());
}
fd.setUseClientMode(true);
- String[] cipherSuites = _ctx.filterCiphers(fd.getSupportedCipherSuites(), fd.getEnabledCipherSuites());
+ String[] cipherSuites = ctx.filterCiphers(fd.getSupportedCipherSuites(), fd.getEnabledCipherSuites());
+ try
+ {
+ fd.setEnabledCipherSuites(cipherSuites);
+ }
+ catch(IllegalArgumentException ex)
+ {
+ Ice.SecurityException e = new Ice.SecurityException();
+ e.reason = "invalid ciphersuite";
+ e.initCause(ex);
+ throw e;
+ }
if(_instance.securityTraceLevel() > 0)
{
StringBuffer s = new StringBuffer();
@@ -194,7 +71,22 @@ final class ConnectorI implements IceInternal.Connector
}
_logger.trace(_instance.securityTraceCategory(), s.toString());
}
- fd.setEnabledCipherSuites(cipherSuites);
+
+ String[] protocols = ctx.getProtocols();
+ if(protocols != null)
+ {
+ try
+ {
+ fd.setEnabledProtocols(protocols);
+ }
+ catch(IllegalArgumentException ex)
+ {
+ Ice.SecurityException e = new Ice.SecurityException();
+ e.reason = "invalid protocol";
+ e.initCause(ex);
+ throw e;
+ }
+ }
//
// If a connect timeout is specified, do the SSL handshake in a separate thread.
@@ -212,6 +104,13 @@ final class ConnectorI implements IceInternal.Connector
{
fd.startHandshake();
}
+
+ if(!ctx.verifyPeer(fd, _host, false))
+ {
+ Ice.SecurityException ex = new Ice.SecurityException();
+ ex.reason = "outgoing connection rejected by certificate verifier";
+ throw ex;
+ }
}
catch(java.net.ConnectException ex)
{
@@ -237,6 +136,22 @@ final class ConnectorI implements IceInternal.Connector
se.initCause(ex);
throw se;
}
+ catch(javax.net.ssl.SSLException ex)
+ {
+ if(fd != null)
+ {
+ try
+ {
+ fd.close();
+ }
+ catch(java.io.IOException e)
+ {
+ }
+ }
+ Ice.SecurityException e = new Ice.SecurityException();
+ e.initCause(ex);
+ throw e;
+ }
catch(java.io.IOException ex)
{
if(fd != null)
@@ -249,6 +164,12 @@ final class ConnectorI implements IceInternal.Connector
{
}
}
+
+ if(IceInternal.Network.connectionLost(ex))
+ {
+ throw new Ice.ConnectionLostException();
+ }
+
Ice.SocketException e = new Ice.SocketException();
e.initCause(ex);
throw e;
@@ -274,6 +195,11 @@ final class ConnectorI implements IceInternal.Connector
_logger.trace(_instance.networkTraceCategory(), s);
}
+ if(_instance.securityTraceLevel() > 0)
+ {
+ ctx.traceConnection(fd, false);
+ }
+
return new TransceiverI(_instance, fd);
}
@@ -289,14 +215,153 @@ final class ConnectorI implements IceInternal.Connector
ConnectorI(Instance instance, String host, int port)
{
_instance = instance;
- _ctx = instance.clientContext();
_logger = instance.communicator().getLogger();
+ _host = host;
_addr = IceInternal.Network.getAddress(host, port);
}
+ private static class ConnectThread extends Thread
+ {
+ ConnectThread(javax.net.ssl.SSLContext ctx, java.net.InetSocketAddress addr)
+ {
+ _ctx = ctx;
+ _addr = addr;
+ }
+
+ public void
+ run()
+ {
+ try
+ {
+ javax.net.SocketFactory factory = _ctx.getSocketFactory();
+ javax.net.ssl.SSLSocket fd =
+ (javax.net.ssl.SSLSocket)factory.createSocket(_addr.getAddress(), _addr.getPort());
+ synchronized(this)
+ {
+ _fd = fd;
+ notifyAll();
+ }
+ }
+ catch(java.io.IOException ex)
+ {
+ synchronized(this)
+ {
+ _ex = ex;
+ notifyAll();
+ }
+ }
+ }
+
+ javax.net.ssl.SSLSocket
+ getFd(int timeout)
+ throws java.io.IOException
+ {
+ javax.net.ssl.SSLSocket fd = null;
+
+ synchronized(this)
+ {
+ while(_fd == null && _ex == null)
+ {
+ try
+ {
+ wait(timeout);
+ break;
+ }
+ catch(InterruptedException ex)
+ {
+ continue;
+ }
+ }
+
+ if(_ex != null)
+ {
+ throw _ex;
+ }
+
+ fd = _fd;
+ _fd = null;
+ }
+
+ return fd;
+ }
+
+ private javax.net.ssl.SSLContext _ctx;
+ private java.net.InetSocketAddress _addr;
+ private javax.net.ssl.SSLSocket _fd;
+ private java.io.IOException _ex;
+ }
+
+ private static class HandshakeThread extends Thread
+ {
+ HandshakeThread(javax.net.ssl.SSLSocket fd)
+ {
+ _fd = fd;
+ _ok = false;
+ }
+
+ public void
+ run()
+ {
+ try
+ {
+ _fd.startHandshake();
+ synchronized(this)
+ {
+ _ok = true;
+ notifyAll();
+ }
+
+ }
+ catch(java.io.IOException ex)
+ {
+ synchronized(this)
+ {
+ _ex = ex;
+ notifyAll();
+ }
+ }
+ }
+
+ boolean
+ waitForHandshake(int timeout)
+ throws java.io.IOException
+ {
+ boolean result = false;
+
+ synchronized(this)
+ {
+ while(!_ok && _ex == null)
+ {
+ try
+ {
+ wait(timeout);
+ break;
+ }
+ catch(InterruptedException ex)
+ {
+ continue;
+ }
+ }
+
+ if(_ex != null)
+ {
+ throw _ex;
+ }
+
+ result = _ok;
+ }
+
+ return result;
+ }
+
+ private javax.net.ssl.SSLSocket _fd;
+ private boolean _ok;
+ private java.io.IOException _ex;
+ }
+
private Instance _instance;
- private Context _ctx;
private Ice.Logger _logger;
+ private String _host;
private java.net.InetSocketAddress _addr;
}
diff --git a/java/src/IceSSL/Context.java b/java/src/IceSSL/Context.java
index 5c99702d5e9..7db3d6450eb 100644
--- a/java/src/IceSSL/Context.java
+++ b/java/src/IceSSL/Context.java
@@ -11,152 +11,212 @@ package IceSSL;
class Context
{
- Context(String ciphers, String keystore, String password, String keystorePassword, String certs,
- String certsPassword, java.security.SecureRandom rand)
+ Context(Instance instance, boolean client, javax.net.ssl.SSLContext context, java.security.SecureRandom rand)
throws java.security.GeneralSecurityException
{
- java.util.ArrayList cipherList = new java.util.ArrayList();
+ _instance = instance;
+ _logger = instance.communicator().getLogger();
+
+ final String prefix = client ? "IceSSL.Client." : "IceSSL.Server.";
+ Ice.Properties properties = instance.communicator().getProperties();
+ String ciphers = properties.getProperty(prefix + "Ciphers");
+
if(ciphers.length() > 0)
{
- String[] expr = ciphers.split("[ \t]+");
- for(int i = 0; i < expr.length; ++i)
+ parseCiphers(ciphers);
+ }
+
+ //
+ // If the user doesn't supply an SSLContext, we need to create one based
+ // on property settings.
+ //
+ _ctx = context;
+ if(_ctx == null)
+ {
+ //
+ // Check for a default directory. We look in this directory for
+ // files mentioned in the configuration.
+ //
+ _defaultDir = properties.getProperty(prefix + "DefaultDir");
+
+ //
+ // The keystore holds private keys and associated certificates.
+ //
+ Ice.StringHolder keystorePath = new Ice.StringHolder(properties.getProperty(prefix + "Keystore"));
+
+ //
+ // The password for the keys.
+ //
+ final String password = properties.getProperty(prefix + "Password");
+
+ //
+ // The password for the keystore.
+ //
+ final String keystorePassword = properties.getProperty(prefix + "KeystorePassword");
+
+ //
+ // The default keystore type value is "JKS", but it can also be "PKCS12".
+ //
+ final String defaultType = java.security.KeyStore.getDefaultType();
+ final String keystoreType = properties.getPropertyWithDefault(prefix + "KeystoreType", defaultType);
+
+ //
+ // The alias of the key to use in authentication.
+ //
+ final String alias = properties.getProperty(prefix + "Alias");
+
+ //
+ // The truststore holds the certificates of trusted CAs.
+ //
+ Ice.StringHolder truststorePath = new Ice.StringHolder(
+ properties.getPropertyWithDefault(prefix + "Truststore", properties.getProperty(prefix + "Certs")));
+
+ //
+ // The password for the truststore.
+ //
+ final String truststorePassword =
+ properties.getPropertyWithDefault(prefix + "TruststorePassword",
+ properties.getProperty(prefix + "CertsPassword"));
+
+ //
+ // The truststore type defaults to "JKS", but it can also be "PKCS12".
+ //
+ String truststoreType =
+ properties.getPropertyWithDefault(prefix + "TruststoreType", java.security.KeyStore.getDefaultType());
+
+ //
+ // Parse the enabled protocols.
+ //
+ String protocols = properties.getProperty(prefix + "Protocols");
+ if(protocols.length() > 0)
{
- if(expr[i].equals("ALL"))
+ java.util.ArrayList l = new java.util.ArrayList();
+ String[] arr = protocols.split("[ \t,]+");
+ for(int i = 0; i < arr.length; ++i)
{
- if(i != 0)
+ String s = arr[i].toLowerCase();
+ if(s.equals("ssl3") || s.equals("sslv3"))
{
- Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
- ex.reason = "IceSSL: `ALL' must be first in cipher list `" + ciphers + "'";
- throw ex;
+ l.add("SSLv3");
}
- _allCiphers = true;
- }
- else if(expr[i].equals("NONE"))
- {
- if(i != 0)
+ else if(s.equals("tls") || s.equals("tls1") || s.equals("tlsv1"))
{
- Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
- ex.reason = "IceSSL: `NONE' must be first in cipher list `" + ciphers + "'";
- throw ex;
+ l.add("TLSv1");
+ }
+ else
+ {
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: unrecognized protocol `" + arr[i] + "'";
+ throw e;
}
- _noCiphers = true;
}
- else
+ _protocols = new String[l.size()];
+ l.toArray(_protocols);
+ }
+
+ //
+ // Collect the key managers.
+ //
+ javax.net.ssl.KeyManager[] keyManagers = null;
+ if(keystorePath.value.length() > 0)
+ {
+ if(!checkPath(keystorePath, false))
{
- CipherExpression ce = new CipherExpression();
- String exp = expr[i];
- if(exp.charAt(0) == '!')
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: keystore file not found:\n" + keystorePath.value;
+ throw e;
+ }
+ java.security.KeyStore keys = java.security.KeyStore.getInstance(keystoreType);
+ try
+ {
+ char[] passwordChars = null;
+ if(keystorePassword.length() > 0)
{
- ce.not = true;
- if(exp.length() > 1)
- {
- exp = exp.substring(1);
- }
- else
- {
- Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
- ex.reason = "IceSSL: invalid cipher expression `" + exp + "'";
- throw ex;
- }
+ passwordChars = keystorePassword.toCharArray();
}
- if(exp.charAt(0) == '(')
- {
- if(!exp.endsWith(")"))
- {
- Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
- ex.reason = "IceSSL: invalid cipher expression `" + exp + "'";
- throw ex;
- }
+ java.io.BufferedInputStream bis =
+ new java.io.BufferedInputStream(new java.io.FileInputStream(keystorePath.value));
+ keys.load(bis, passwordChars);
+ }
+ catch(java.io.IOException ex)
+ {
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: unable to load keystore:\n" + keystorePath.value;
+ e.initCause(ex);
+ throw e;
+ }
- try
- {
- ce.re = java.util.regex.Pattern.compile(exp.substring(1, exp.length() - 2));
- }
- catch(java.util.regex.PatternSyntaxException ex)
- {
- Ice.PluginInitializationException e = new Ice.PluginInitializationException();
- e.reason = "IceSSL: invalid cipher expression `" + exp + "'";
- e.initCause(ex);
- throw e;
- }
- }
- else
+ String algorithm = javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm();
+ javax.net.ssl.KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance(algorithm);
+ kmf.init(keys, password.toCharArray());
+ keyManagers = kmf.getKeyManagers();
+
+ //
+ // If the user selected a specific alias, we need to wrap the key managers
+ // in order to return the desired alias.
+ //
+ if(alias.length() > 0)
+ {
+ if(!keys.isKeyEntry(alias))
{
- ce.cipher = exp;
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: keystore does not contain an entry with alias `" + alias + "'";
+ throw e;
}
- cipherList.add(ce);
+ for(int i = 0; i < keyManagers.length; ++i)
+ {
+ keyManagers[i] = new KeyManagerI((javax.net.ssl.X509KeyManager)keyManagers[i], alias, client);
+ }
}
}
- _ciphers = new CipherExpression[cipherList.size()];
- cipherList.toArray(_ciphers);
- }
-
- final String ksType = java.security.KeyStore.getDefaultType();
- javax.net.ssl.KeyManager[] keyManagers = null;
- if(keystore != null && keystore.length() > 0)
- {
- _keys = java.security.KeyStore.getInstance(ksType);
- try
+ //
+ // Collect the trust managers.
+ //
+ javax.net.ssl.TrustManager[] trustManagers = null;
+ if(truststorePath.value.length() > 0)
{
- char[] pass = null;
- if(keystorePassword != null && keystorePassword.length() > 0)
+ if(!checkPath(truststorePath, false))
{
- pass = keystorePassword.toCharArray();
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: truststore file not found:\n" + truststorePath.value;
+ throw e;
}
+ java.security.KeyStore ts = java.security.KeyStore.getInstance(truststoreType);
+ try
+ {
+ char[] passwordChars = null;
+ if(truststorePassword.length() > 0)
+ {
+ passwordChars = truststorePassword.toCharArray();
+ }
- java.io.BufferedInputStream bis =
- new java.io.BufferedInputStream(new java.io.FileInputStream(keystore));
- _keys.load(bis, pass);
- }
- catch(java.io.IOException ex)
- {
- Ice.PluginInitializationException e = new Ice.PluginInitializationException();
- e.reason = "IceSSL: unable to load keystore from `" + keystore + "'";
- e.initCause(ex);
- throw e;
- }
-
- String algorithm = javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm();
- javax.net.ssl.KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance(algorithm);
- kmf.init(_keys, password.toCharArray());
- keyManagers = kmf.getKeyManagers();
- }
-
- javax.net.ssl.TrustManager[] trustManagers = null;
- if(certs != null && certs.length() > 0)
- {
- _certs = java.security.KeyStore.getInstance(ksType);
- try
- {
- char[] pass = null;
- if(certsPassword != null && certsPassword.length() > 0)
+ java.io.BufferedInputStream bis =
+ new java.io.BufferedInputStream(new java.io.FileInputStream(truststorePath.value));
+ ts.load(bis, passwordChars);
+ }
+ catch(java.io.IOException ex)
{
- pass = certsPassword.toCharArray();
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: unable to load truststore:\n" + truststorePath.value;
+ e.initCause(ex);
+ throw e;
}
- java.io.BufferedInputStream bis =
- new java.io.BufferedInputStream(new java.io.FileInputStream(certs));
- _certs.load(bis, pass);
- }
- catch(java.io.IOException ex)
- {
- Ice.PluginInitializationException e = new Ice.PluginInitializationException();
- e.reason = "IceSSL: unable to load keystore from `" + certs + "'";
- e.initCause(ex);
- throw e;
+ String algorithm = javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm();
+ javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance(algorithm);
+ tmf.init(ts);
+ trustManagers = tmf.getTrustManagers();
}
- String algorithm = javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm();
- javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance(algorithm);
- tmf.init(_certs);
- trustManagers = tmf.getTrustManagers();
+ //
+ // Initialize the SSL context.
+ //
+ _ctx = javax.net.ssl.SSLContext.getInstance("SSL");
+ _ctx.init(keyManagers, trustManagers, rand);
}
-
- _ctx = javax.net.ssl.SSLContext.getInstance("SSL");
- _ctx.init(keyManagers, trustManagers, rand);
}
javax.net.ssl.SSLContext
@@ -240,6 +300,165 @@ class Context
return arr;
}
+ String[]
+ getProtocols()
+ {
+ return _protocols;
+ }
+
+ void
+ traceConnection(javax.net.ssl.SSLSocket fd, boolean incoming)
+ {
+ javax.net.ssl.SSLSession session = fd.getSession();
+ String msg = "SSL summary for " + (incoming ? "incoming" : "outgoing") + " connection\n" +
+ "cipher = " + session.getCipherSuite() + "\n" +
+ "protocol = " + session.getProtocol() + "\n" +
+ IceInternal.Network.fdToString(fd);
+ _logger.trace(_instance.securityTraceCategory(), msg);
+ }
+
+ boolean
+ verifyPeer(javax.net.ssl.SSLSocket fd, String host, boolean incoming)
+ {
+ CertificateVerifier verifier = _instance.certificateVerifier();
+ if(verifier != null)
+ {
+ VerifyInfo info = new VerifyInfo();
+ info.incoming = incoming;
+ try
+ {
+ info.certs = fd.getSession().getPeerCertificates();
+ }
+ catch(javax.net.ssl.SSLPeerUnverifiedException ex)
+ {
+ // No peer certificates.
+ }
+ info.socket = fd;
+ info.address = host;
+ if(!verifier.verify(info))
+ {
+ if(_instance.securityTraceLevel() > 0)
+ {
+ _logger.trace(_instance.securityTraceCategory(),
+ (incoming ? "incoming" : "outgoing") +
+ " connection rejected by certificate verifier\n" +
+ IceInternal.Network.fdToString(fd));
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void
+ parseCiphers(String ciphers)
+ {
+ java.util.ArrayList cipherList = new java.util.ArrayList();
+ String[] expr = ciphers.split("[ \t]+");
+ for(int i = 0; i < expr.length; ++i)
+ {
+ if(expr[i].equals("ALL"))
+ {
+ if(i != 0)
+ {
+ Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
+ ex.reason = "IceSSL: `ALL' must be first in cipher list `" + ciphers + "'";
+ throw ex;
+ }
+ _allCiphers = true;
+ }
+ else if(expr[i].equals("NONE"))
+ {
+ if(i != 0)
+ {
+ Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
+ ex.reason = "IceSSL: `NONE' must be first in cipher list `" + ciphers + "'";
+ throw ex;
+ }
+ _noCiphers = true;
+ }
+ else
+ {
+ CipherExpression ce = new CipherExpression();
+ String exp = expr[i];
+ if(exp.charAt(0) == '!')
+ {
+ ce.not = true;
+ if(exp.length() > 1)
+ {
+ exp = exp.substring(1);
+ }
+ else
+ {
+ Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
+ ex.reason = "IceSSL: invalid cipher expression `" + exp + "'";
+ throw ex;
+ }
+ }
+
+ if(exp.charAt(0) == '(')
+ {
+ if(!exp.endsWith(")"))
+ {
+ Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
+ ex.reason = "IceSSL: invalid cipher expression `" + exp + "'";
+ throw ex;
+ }
+
+ try
+ {
+ ce.re = java.util.regex.Pattern.compile(exp.substring(1, exp.length() - 2));
+ }
+ catch(java.util.regex.PatternSyntaxException ex)
+ {
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: invalid cipher expression `" + exp + "'";
+ e.initCause(ex);
+ throw e;
+ }
+ }
+ else
+ {
+ ce.cipher = exp;
+ }
+
+ cipherList.add(ce);
+ }
+ }
+ _ciphers = new CipherExpression[cipherList.size()];
+ cipherList.toArray(_ciphers);
+ }
+
+ private boolean
+ checkPath(Ice.StringHolder path, boolean dir)
+ {
+ //
+ // Check if file exists. If not, try prepending the default
+ // directory and check again. If the file is found, the
+ // string argument is modified and true is returned. Otherwise
+ // false is returned.
+ //
+ java.io.File f = new java.io.File(path.value);
+ if(f.exists())
+ {
+ return dir ? f.isDirectory() : f.isFile();
+ }
+
+ if(_defaultDir.length() > 0)
+ {
+ String s = _defaultDir + java.io.File.separator + path.value;
+ f = new java.io.File(s);
+ if(f.exists() && ((!dir && f.isFile()) || (dir && f.isDirectory())))
+ {
+ path.value = s;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private static class CipherExpression
{
boolean not;
@@ -247,10 +466,12 @@ class Context
java.util.regex.Pattern re;
}
+ private Instance _instance;
+ private Ice.Logger _logger;
+ private String _defaultDir;
private CipherExpression[] _ciphers;
private boolean _allCiphers;
private boolean _noCiphers;
+ private String[] _protocols;
private javax.net.ssl.SSLContext _ctx;
- private java.security.KeyStore _keys;
- private java.security.KeyStore _certs;
}
diff --git a/java/src/IceSSL/Instance.java b/java/src/IceSSL/Instance.java
index e1dda63e598..b6494d69099 100644
--- a/java/src/IceSSL/Instance.java
+++ b/java/src/IceSSL/Instance.java
@@ -17,33 +17,62 @@ class Instance
_securityTraceLevel = communicator.getProperties().getPropertyAsIntWithDefault("IceSSL.Trace.Security", 0);
_securityTraceCategory = "Security";
- java.security.SecureRandom rand;
- try
- {
- //
- // Create a SecureRandom object. We call nextInt() in order to
- // force the object to perform any time-consuming initialization tasks now.
- //
- rand = java.security.SecureRandom.getInstance("SHA1PRNG");
-
- //
- // We call nextInt() in order to force the object to perform any time-consuming
- // initialization tasks now.
- //
- rand.nextInt();
-
+ //
+ // Initialize the plugin, unless IceSSL.DelayInit=1.
+ //
+ if(communicator.getProperties().getPropertyAsInt("IceSSL.DelayInit") == 0)
+ {
+ initialize(null, null);
}
- catch(java.security.GeneralSecurityException ex)
- {
+
+ //
+ // Register the endpoint factory.
+ //
+ _facade.addEndpointFactory(new EndpointFactoryI(this));
+ }
+
+ void
+ initialize(javax.net.ssl.SSLContext clientContext, javax.net.ssl.SSLContext serverContext)
+ {
+ if(_clientContext != null)
+ {
Ice.PluginInitializationException e = new Ice.PluginInitializationException();
- e.reason = "IceSSL: unable to initialize secure PRNG";
- e.initCause(ex);
+ e.reason = "plugin is already initialized";
throw e;
}
+ //
+ // If we have to initialize an SSLContext, we'll need a SecureRandom object.
+ //
+ java.security.SecureRandom rand = null;
+ if(clientContext == null || serverContext == null)
+ {
+ try
+ {
+ rand = java.security.SecureRandom.getInstance("SHA1PRNG");
+
+ //
+ // We call nextInt() in order to force the object to perform any time-consuming
+ // initialization tasks now.
+ //
+ rand.nextInt();
+ }
+ catch(java.security.GeneralSecurityException ex)
+ {
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: unable to initialize secure PRNG";
+ e.initCause(ex);
+ throw e;
+ }
+ }
+
+ //
+ // Create the client and server contexts. We always create both, even
+ // if only one is used.
+ //
try
{
- _clientContext = createContext("Client", rand);
+ _clientContext = new Context(this, true, clientContext, rand);
}
catch(java.security.GeneralSecurityException ex)
{
@@ -55,7 +84,7 @@ class Instance
try
{
- _serverContext = createContext("Server", rand);
+ _serverContext = new Context(this, false, serverContext, rand);
}
catch(java.security.GeneralSecurityException ex)
{
@@ -64,8 +93,12 @@ class Instance
e.initCause(ex);
throw e;
}
+ }
- _facade.addEndpointFactory(new EndpointFactoryI(this));
+ void
+ setCertificateVerifier(CertificateVerifier verifier)
+ {
+ _verifier = verifier;
}
Ice.Communicator
@@ -107,28 +140,31 @@ class Instance
Context
clientContext()
{
+ if(_clientContext == null)
+ {
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: plugin is not fully initialized";
+ throw e;
+ }
return _clientContext;
}
Context
serverContext()
{
+ if(_serverContext == null)
+ {
+ Ice.PluginInitializationException e = new Ice.PluginInitializationException();
+ e.reason = "IceSSL: plugin is not fully initialized";
+ throw e;
+ }
return _serverContext;
}
- private Context
- createContext(String mode, java.security.SecureRandom rand)
- throws java.security.GeneralSecurityException
- {
- final String prefix = "IceSSL." + mode + ".";
- Ice.Properties properties = communicator().getProperties();
- String ciphers = properties.getProperty(prefix + "Ciphers");
- String keyStore = properties.getProperty(prefix + "Keystore");
- String password = properties.getProperty(prefix + "Password");
- String keyStorePassword = properties.getProperty(prefix + "KeystorePassword");
- String certs = properties.getProperty(prefix + "Certs");
- String certsPassword = properties.getProperty(prefix + "CertsPassword");
- return new Context(ciphers, keyStore, password, keyStorePassword, certs, certsPassword, rand);
+ CertificateVerifier
+ certificateVerifier()
+ {
+ return _verifier;
}
private IceInternal.ProtocolPluginFacade _facade;
@@ -136,4 +172,5 @@ class Instance
private String _securityTraceCategory;
private Context _clientContext;
private Context _serverContext;
+ private CertificateVerifier _verifier;
}
diff --git a/java/src/IceSSL/KeyManagerI.java b/java/src/IceSSL/KeyManagerI.java
new file mode 100644
index 00000000000..4a6d76e33cc
--- /dev/null
+++ b/java/src/IceSSL/KeyManagerI.java
@@ -0,0 +1,74 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package IceSSL;
+
+final class KeyManagerI implements javax.net.ssl.X509KeyManager
+{
+ KeyManagerI(javax.net.ssl.X509KeyManager del, String alias, boolean client)
+ {
+ _delegate = del;
+ _alias = alias;
+ _client = client;
+ }
+
+ public String
+ chooseClientAlias(String[] keyType, java.security.Principal[] issuers, java.net.Socket socket)
+ {
+ if(_client)
+ {
+ return _alias;
+ }
+ else
+ {
+ return _delegate.chooseClientAlias(keyType, issuers, socket);
+ }
+ }
+
+ public String
+ chooseServerAlias(String keyType, java.security.Principal[] issuers, java.net.Socket socket)
+ {
+ if(!_client)
+ {
+ return _alias;
+ }
+ else
+ {
+ return _delegate.chooseServerAlias(keyType, issuers, socket);
+ }
+ }
+
+ public java.security.cert.X509Certificate[]
+ getCertificateChain(String alias)
+ {
+ return _delegate.getCertificateChain(alias);
+ }
+
+ public String[]
+ getClientAliases(String keyType, java.security.Principal[] issuers)
+ {
+ return _delegate.getClientAliases(keyType, issuers);
+ }
+
+ public String[]
+ getServerAliases(String keyType, java.security.Principal[] issuers)
+ {
+ return _delegate.getServerAliases(keyType, issuers);
+ }
+
+ public java.security.PrivateKey
+ getPrivateKey(String alias)
+ {
+ return _delegate.getPrivateKey(alias);
+ }
+
+ private javax.net.ssl.X509KeyManager _delegate;
+ private String _alias;
+ private boolean _client;
+}
diff --git a/java/src/IceSSL/Plugin.java b/java/src/IceSSL/Plugin.java
new file mode 100644
index 00000000000..a4316453f62
--- /dev/null
+++ b/java/src/IceSSL/Plugin.java
@@ -0,0 +1,28 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package IceSSL;
+
+public interface Plugin extends Ice.Plugin
+{
+ //
+ // Manually initialize the plugin. The application must set the property
+ // IceSSL.DelayInit=1 in order to use this method.
+ //
+ // It is legal to pass null as either argument, in which case the plugin
+ // obtains its certificates as directed by configuration properties.
+ //
+ void initialize(javax.net.ssl.SSLContext clientContext, javax.net.ssl.SSLContext serverContext);
+
+ //
+ // Establish the certificate verifier object. This should be
+ // done before any connections are established.
+ //
+ void setCertificateVerifier(CertificateVerifier verifier);
+}
diff --git a/java/src/IceSSL/PluginI.java b/java/src/IceSSL/PluginI.java
index 431847271cd..bba197882e3 100644
--- a/java/src/IceSSL/PluginI.java
+++ b/java/src/IceSSL/PluginI.java
@@ -9,7 +9,7 @@
package IceSSL;
-public class PluginI extends Ice.LocalObjectImpl implements Ice.Plugin
+class PluginI extends Ice.LocalObjectImpl implements Plugin
{
public
PluginI(Ice.Communicator communicator)
@@ -22,5 +22,17 @@ public class PluginI extends Ice.LocalObjectImpl implements Ice.Plugin
{
}
+ public void
+ initialize(javax.net.ssl.SSLContext clientContext, javax.net.ssl.SSLContext serverContext)
+ {
+ _instance.initialize(clientContext, serverContext);
+ }
+
+ public void
+ setCertificateVerifier(CertificateVerifier verifier)
+ {
+ _instance.setCertificateVerifier(verifier);
+ }
+
private Instance _instance;
}
diff --git a/java/src/IceSSL/VerifyInfo.java b/java/src/IceSSL/VerifyInfo.java
new file mode 100644
index 00000000000..95e603ada3a
--- /dev/null
+++ b/java/src/IceSSL/VerifyInfo.java
@@ -0,0 +1,45 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package IceSSL;
+
+//
+// VerifyInfo contains information that may be of use to a
+// CertificateVerifier implementation.
+//
+public class VerifyInfo
+{
+ //
+ // A value of true indicates an incoming (server) connection.
+ //
+ public boolean incoming;
+
+ //
+ // The peer's certificate chain, which can be null if the peer
+ // is unverified.
+ //
+ public java.security.cert.Certificate[] certs;
+
+ //
+ // The SSL socket that is being authenticated.
+ //
+ public javax.net.ssl.SSLSocket socket;
+
+ //
+ // The address of the server as specified by the proxy's
+ // endpoint. For example, in the following proxy:
+ //
+ // identity:ssl -h www.server.com -p 10000
+ //
+ // the value of address is "www.server.com".
+ //
+ // The value is an empty string for incoming connections.
+ //
+ public String address;
+}