diff options
author | Benoit Foucher <benoit@zeroc.com> | 2007-11-26 10:27:33 +0100 |
---|---|---|
committer | Benoit Foucher <benoit@zeroc.com> | 2007-11-26 10:27:33 +0100 |
commit | a680e1c9cb73caa66d2cab436e28e95924dd8dbd (patch) | |
tree | 473abb5c732c0cc5cb61b44ed5f74fc74d83b1ec /java/src | |
parent | Merge branch 'master' of ssh://cvs.zeroc.com/home/git/ice (diff) | |
download | ice-a680e1c9cb73caa66d2cab436e28e95924dd8dbd.tar.bz2 ice-a680e1c9cb73caa66d2cab436e28e95924dd8dbd.tar.xz ice-a680e1c9cb73caa66d2cab436e28e95924dd8dbd.zip |
Remove JDK 1.4 IceSSL plugin
Diffstat (limited to 'java/src')
-rw-r--r-- | java/src/IceInternal/Time.java | 19 | ||||
-rw-r--r-- | java/src/IceSSL/AcceptorI.java | 318 | ||||
-rw-r--r-- | java/src/IceSSL/CertificateVerifier.java | 23 | ||||
-rw-r--r-- | java/src/IceSSL/ConnectionInfo.java | 52 | ||||
-rw-r--r-- | java/src/IceSSL/ConnectionInvalidException.java | 30 | ||||
-rw-r--r-- | java/src/IceSSL/ConnectorI.java | 221 | ||||
-rw-r--r-- | java/src/IceSSL/EndpointFactoryI.java | 50 | ||||
-rw-r--r-- | java/src/IceSSL/EndpointI.java | 518 | ||||
-rw-r--r-- | java/src/IceSSL/Instance.java | 995 | ||||
-rw-r--r-- | java/src/IceSSL/PasswordCallback.java | 36 | ||||
-rw-r--r-- | java/src/IceSSL/Plugin.java | 54 | ||||
-rw-r--r-- | java/src/IceSSL/PluginFactory.java | 19 | ||||
-rw-r--r-- | java/src/IceSSL/PluginI.java | 68 | ||||
-rw-r--r-- | java/src/IceSSL/RFC2253.java | 417 | ||||
-rw-r--r-- | java/src/IceSSL/TransceiverI.java | 854 | ||||
-rw-r--r-- | java/src/IceSSL/TrustManager.java | 295 | ||||
-rw-r--r-- | java/src/IceSSL/Util.java | 127 | ||||
-rw-r--r-- | java/src/IceSSL/X509KeyManagerI.java | 70 | ||||
-rw-r--r-- | java/src/IceSSL/X509TrustManagerI.java | 50 |
19 files changed, 4216 insertions, 0 deletions
diff --git a/java/src/IceInternal/Time.java b/java/src/IceInternal/Time.java new file mode 100644 index 00000000000..d77879ef836 --- /dev/null +++ b/java/src/IceInternal/Time.java @@ -0,0 +1,19 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 IceInternal; + +final public class Time +{ + static public long + currentMonotonicTimeMillis() + { + return System.nanoTime() / 1000000; + } +}; diff --git a/java/src/IceSSL/AcceptorI.java b/java/src/IceSSL/AcceptorI.java new file mode 100644 index 00000000000..04f2b3331ab --- /dev/null +++ b/java/src/IceSSL/AcceptorI.java @@ -0,0 +1,318 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 AcceptorI implements IceInternal.Acceptor +{ + public java.nio.channels.ServerSocketChannel + fd() + { + return _fd; + } + + public void + close() + { + if(_instance.networkTraceLevel() >= 1) + { + String s = "stopping to accept ssl connections at " + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + java.nio.channels.ServerSocketChannel fd; + java.nio.channels.Selector selector; + synchronized(this) + { + fd = _fd; + selector = _selector; + _fd = null; + _selector = null; + } + if(fd != null) + { + try + { + fd.close(); + } + catch(java.io.IOException ex) + { + // Ignore. + } + } + if(selector != null) + { + try + { + selector.close(); + } + catch(java.io.IOException ex) + { + // Ignore. + } + } + } + + public void + listen() + { + // Nothing to do. + + if(_instance.networkTraceLevel() >= 1) + { + String s = "accepting ssl connections at " + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + } + + public IceInternal.Transceiver + accept(int timeout) + { + // + // The plugin may not be fully initialized. + // + if(!_instance.initialized()) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: plugin is not initialized"; + throw ex; + } + + java.nio.channels.SocketChannel fd = null; + while(fd == null) + { + try + { + fd = _fd.accept(); + if(fd == null) + { + if(_selector == null) + { + _selector = java.nio.channels.Selector.open(); + } + + while(true) + { + try + { + java.nio.channels.SelectionKey key = + _fd.register(_selector, java.nio.channels.SelectionKey.OP_ACCEPT); + if(timeout > 0) + { + if(_selector.select(timeout) == 0) + { + throw new Ice.TimeoutException(); + } + } + else if(timeout == 0) + { + if(_selector.selectNow() == 0) + { + throw new Ice.TimeoutException(); + } + } + else + { + _selector.select(); + } + + break; + } + catch(java.io.IOException ex) + { + if(IceInternal.Network.interrupted(ex)) + { + continue; + } + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + } + } + catch(java.io.IOException ex) + { + if(IceInternal.Network.interrupted(ex)) + { + continue; + } + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + + // + // 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.socket().getRemoteSocketAddress(); + synchronized(this) + { + if(remoteAddr.equals(_connectToSelfAddr)) + { + try + { + fd.close(); + } + catch(java.io.IOException e) + { + } + return null; + } + } + + javax.net.ssl.SSLEngine engine = null; + try + { + try + { + java.net.Socket socket = fd.socket(); + socket.setTcpNoDelay(true); + socket.setKeepAlive(true); + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + + IceInternal.Network.setBlock(fd, false); + IceInternal.Network.setTcpBufSize(fd, _instance.communicator().getProperties(), _logger); + + engine = _instance.createSSLEngine(true); + } + catch(RuntimeException ex) + { + try + { + fd.close(); + } + catch(java.io.IOException e) + { + // Ignore. + } + throw ex; + } + + if(_instance.networkTraceLevel() >= 1) + { + _logger.trace(_instance.networkTraceCategory(), "attempting to accept ssl connection\n" + + IceInternal.Network.fdToString(fd)); + } + + return new TransceiverI(_instance, engine, fd, "", true, _adapterName); + } + + public void + connectToSelf() + { + java.nio.channels.SocketChannel fd = IceInternal.Network.createTcpSocket(); + IceInternal.Network.setBlock(fd, false); + 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); + } + + public String + toString() + { + return IceInternal.Network.addrToString(_addr); + } + + int + effectivePort() + { + return _addr.getPort(); + } + + AcceptorI(Instance instance, String adapterName, String host, int port) + { + _instance = instance; + _adapterName = adapterName; + _logger = instance.communicator().getLogger(); + _backlog = 0; + + if(_backlog <= 0) + { + _backlog = 5; + } + + try + { + _fd = IceInternal.Network.createTcpServerSocket(); + IceInternal.Network.setBlock(_fd, false); + IceInternal.Network.setTcpBufSize(_fd, _instance.communicator().getProperties(), _logger); + if(!System.getProperty("os.name").startsWith("Windows")) + { + // + // Enable SO_REUSEADDR on Unix platforms to allow + // re-using the socket even if it's in the TIME_WAIT + // state. On Windows, this doesn't appear to be + // necessary and enabling SO_REUSEADDR would actually + // not be a good thing since it allows a second + // process to bind to an address even it's already + // bound by another process. + // + // TODO: using SO_EXCLUSIVEADDRUSE on Windows would + // probably be better but it's only supported by recent + // Windows versions (XP SP2, Windows Server 2003). + // + IceInternal.Network.setReuseAddress(_fd, true); + } + _addr = new java.net.InetSocketAddress(host, port); + if(_instance.networkTraceLevel() >= 2) + { + String s = "attempting to bind to ssl socket " + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + _addr = IceInternal.Network.doBind(_fd, _addr); + } + catch(RuntimeException ex) + { + _fd = null; + throw ex; + } + } + + protected synchronized void + finalize() + throws Throwable + { + IceUtil.Assert.FinalizerAssert(_fd == null); + + super.finalize(); + } + + private Instance _instance; + private String _adapterName; + private Ice.Logger _logger; + private java.nio.channels.ServerSocketChannel _fd; + private int _backlog; + private java.net.InetSocketAddress _addr; + private java.nio.channels.Selector _selector; + 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..8426eae8ffe --- /dev/null +++ b/java/src/IceSSL/CertificateVerifier.java @@ -0,0 +1,23 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 false if the connection should be rejected, or true to + // allow it. + // + boolean verify(ConnectionInfo info); +} diff --git a/java/src/IceSSL/ConnectionInfo.java b/java/src/IceSSL/ConnectionInfo.java new file mode 100644 index 00000000000..ed7340a44a1 --- /dev/null +++ b/java/src/IceSSL/ConnectionInfo.java @@ -0,0 +1,52 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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; + +// +// ConnectionInfo contains information that may be of use to a +// CertificateVerifier or an application that wants information +// about its peer. +// +public class ConnectionInfo +{ + // + // The certificate chain. This may be null if the peer did not + // supply a certificate. The peer's certificate (if any) is the + // first one in the chain. + // + public java.security.cert.Certificate[] certs; + + // + // The name of the negotiated cipher. + // + public String cipher; + + // + // The local TCP/IP host & port. + // + public java.net.InetSocketAddress localAddr; + + // + // The remote TCP/IP host & port. + // + public java.net.InetSocketAddress remoteAddr; + + // + // If the connection is incoming this bool is true, false + // otherwise. + // + boolean incoming; + + // + // The name of the object adapter that hosts this endpoint, if + // any. + // + String adapterName; +} diff --git a/java/src/IceSSL/ConnectionInvalidException.java b/java/src/IceSSL/ConnectionInvalidException.java new file mode 100644 index 00000000000..3b7921ae9cf --- /dev/null +++ b/java/src/IceSSL/ConnectionInvalidException.java @@ -0,0 +1,30 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 final class ConnectionInvalidException extends Ice.LocalException +{ + public ConnectionInvalidException() + { + } + + public ConnectionInvalidException(String reason) + { + this.reason = reason; + } + + public String + ice_name() + { + return "Ice::ConnectionInvalidException"; + } + + public String reason; +} diff --git a/java/src/IceSSL/ConnectorI.java b/java/src/IceSSL/ConnectorI.java new file mode 100644 index 00000000000..9836a78753a --- /dev/null +++ b/java/src/IceSSL/ConnectorI.java @@ -0,0 +1,221 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 ConnectorI implements IceInternal.Connector, java.lang.Comparable +{ + final static short TYPE = 2; + + public IceInternal.Transceiver + connect(int timeout) + { + // + // The plugin may not be fully initialized. + // + if(!_instance.initialized()) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: plugin is not initialized"; + throw ex; + } + + if(_instance.networkTraceLevel() >= 2) + { + String s = "trying to establish ssl connection to " + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + java.nio.channels.SocketChannel fd = IceInternal.Network.createTcpSocket(); + IceInternal.Network.setBlock(fd, false); + IceInternal.Network.setTcpBufSize(fd, _instance.communicator().getProperties(), _logger); + IceInternal.Network.doConnect(fd, _addr, timeout); + + TransceiverI transceiver = null; + try + { + javax.net.ssl.SSLEngine engine = _instance.createSSLEngine(false); + + transceiver = new TransceiverI(_instance, engine, fd, _host, false, ""); +/* + transceiver.waitForHandshake(timeout); + + // + // Check IceSSL.VerifyPeer. + // + int verifyPeer = + _instance.communicator().getProperties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); + if(verifyPeer > 0) + { + try + { + engine.getSession().getPeerCertificates(); + } + catch(javax.net.ssl.SSLPeerUnverifiedException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: server did not supply a certificate"; + e.initCause(ex); + throw e; + } + } +*/ + +/* + if(!ctx.verifyPeer(fd, _host, false)) + { + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = "IceSSL: outgoing connection rejected by certificate verifier"; + throw ex; + } +*/ + } + catch(RuntimeException ex) + { + try + { + fd.close(); + } + catch(java.io.IOException e) + { + // Ignore. + } + throw ex; + } + + if(_instance.networkTraceLevel() >= 1) + { + String s = "ssl connection established\n" + IceInternal.Network.fdToString(fd); + _logger.trace(_instance.networkTraceCategory(), s); + } + + return transceiver; + } + + public short + type() + { + return TYPE; + } + + public String + toString() + { + return IceInternal.Network.addrToString(_addr); + } + + public int + hashCode() + { + return _hashCode; + } + + final boolean + equivalent(String host, int port) + { + java.net.InetSocketAddress addr; + try + { + addr = IceInternal.Network.getAddress(host, port); + } + catch(Ice.DNSException ex) + { + return false; + } + return addr.equals(_addr); + } + + // + // Only for use by EndpointI. + // + ConnectorI(Instance instance, java.net.InetSocketAddress addr, int timeout, String connectionId) + { + _instance = instance; + _logger = instance.communicator().getLogger(); + _host = addr.getHostName(); + _addr = addr; + _timeout = timeout; + _connectionId = connectionId; + + _hashCode = _addr.getAddress().getHostAddress().hashCode(); + _hashCode = 5 * _hashCode + _addr.getPort(); + _hashCode = 5 * _hashCode + _timeout; + _hashCode = 5 * _hashCode + _connectionId.hashCode(); + } + + // + // Compare connectors for sorting purposes + // + public boolean + equals(java.lang.Object obj) + { + return compareTo(obj) == 0; + } + + public int + compareTo(java.lang.Object obj) // From java.lang.Comparable + { + ConnectorI p = null; + + try + { + p = (ConnectorI)obj; + } + catch(ClassCastException ex) + { + try + { + IceInternal.Connector c = (IceInternal.Connector)obj; + return type() < c.type() ? -1 : 1; + } + catch(ClassCastException ee) + { + assert(false); + } + } + + if(this == p) + { + return 0; + } + + if(_timeout < p._timeout) + { + return -1; + } + else if(p._timeout < _timeout) + { + return 1; + } + + if(!_connectionId.equals(p._connectionId)) + { + return _connectionId.compareTo(p._connectionId); + } + + if(_timeout < p._timeout) + { + return -1; + } + else if(p._timeout < _timeout) + { + return 1; + } + + return IceInternal.Network.compareAddress(_addr, p._addr); + } + + private Instance _instance; + private Ice.Logger _logger; + private String _host; + private java.net.InetSocketAddress _addr; + private int _timeout; + private String _connectionId; + private int _hashCode; +} diff --git a/java/src/IceSSL/EndpointFactoryI.java b/java/src/IceSSL/EndpointFactoryI.java new file mode 100644 index 00000000000..41f1d1eb12c --- /dev/null +++ b/java/src/IceSSL/EndpointFactoryI.java @@ -0,0 +1,50 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 EndpointFactoryI implements IceInternal.EndpointFactory +{ + EndpointFactoryI(Instance instance) + { + _instance = instance; + } + + public short + type() + { + return EndpointI.TYPE; + } + + public String + protocol() + { + return "ssl"; + } + + public IceInternal.EndpointI + create(String str, boolean server) + { + return new EndpointI(_instance, str, server); + } + + public IceInternal.EndpointI + read(IceInternal.BasicStream s) + { + return new EndpointI(_instance, s); + } + + public void + destroy() + { + _instance = null; + } + + private Instance _instance; +} diff --git a/java/src/IceSSL/EndpointI.java b/java/src/IceSSL/EndpointI.java new file mode 100644 index 00000000000..8cf0f559fa4 --- /dev/null +++ b/java/src/IceSSL/EndpointI.java @@ -0,0 +1,518 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 EndpointI extends IceInternal.EndpointI +{ + final static short TYPE = 2; + + public + EndpointI(Instance instance, String ho, int po, int ti, String conId, boolean co) + { + _instance = instance; + _host = ho; + _port = po; + _timeout = ti; + _connectionId = conId; + _compress = co; + calcHashValue(); + } + + public + EndpointI(Instance instance, String str, boolean server) + { + _instance = instance; + _host = null; + _port = 0; + _timeout = -1; + _compress = false; + + String[] arr = str.split("[ \t\n\r]+"); + + int i = 0; + while(i < arr.length) + { + if(arr[i].length() == 0) + { + i++; + continue; + } + + String option = arr[i++]; + if(option.length() != 2 || option.charAt(0) != '-') + { + throw new Ice.EndpointParseException("ssl " + str); + } + + String argument = null; + if(i < arr.length && arr[i].charAt(0) != '-') + { + argument = arr[i++]; + } + + switch(option.charAt(1)) + { + case 'h': + { + if(argument == null) + { + throw new Ice.EndpointParseException("ssl " + str); + } + + _host = argument; + break; + } + + case 'p': + { + if(argument == null) + { + throw new Ice.EndpointParseException("ssl " + str); + } + + try + { + _port = Integer.parseInt(argument); + } + catch(NumberFormatException ex) + { + throw new Ice.EndpointParseException("ssl " + str); + } + + if(_port < 0 || _port > 65535) + { + throw new Ice.EndpointParseException("ssl " + str); + } + + break; + } + + case 't': + { + if(argument == null) + { + throw new Ice.EndpointParseException("ssl " + str); + } + + try + { + _timeout = Integer.parseInt(argument); + } + catch(NumberFormatException ex) + { + throw new Ice.EndpointParseException("ssl " + str); + } + + break; + } + + case 'z': + { + if(argument != null) + { + throw new Ice.EndpointParseException("ssl " + str); + } + + _compress = true; + break; + } + + default: + { + throw new Ice.EndpointParseException("ssl " + str); + } + } + } + + if(_host == null) + { + _host = _instance.defaultHost(); + if(_host == null) + { + if(server) + { + _host = "0.0.0.0"; + } + else + { + _host = "127.0.0.1"; + } + } + } + else if(_host.equals("*")) + { + _host = "0.0.0.0"; + } + calcHashValue(); + } + + public + EndpointI(Instance instance, IceInternal.BasicStream s) + { + _instance = instance; + s.startReadEncaps(); + _host = s.readString(); + _port = s.readInt(); + _timeout = s.readInt(); + _compress = s.readBool(); + s.endReadEncaps(); + calcHashValue(); + } + + // + // Marshal the endpoint + // + public void + streamWrite(IceInternal.BasicStream s) + { + s.writeShort(TYPE); + s.startWriteEncaps(); + s.writeString(_host); + s.writeInt(_port); + s.writeInt(_timeout); + s.writeBool(_compress); + s.endWriteEncaps(); + } + + // + // Convert the endpoint to its string form + // + public String + _toString() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + String s = "ssl -h " + _host + " -p " + _port; + if(_timeout != -1) + { + s += " -t " + _timeout; + } + if(_compress) + { + s += " -z"; + } + return s; + } + + // + // Return the endpoint type + // + public short + type() + { + return TYPE; + } + + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + public int + timeout() + { + return _timeout; + } + + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + public IceInternal.EndpointI + timeout(int timeout) + { + if(timeout == _timeout) + { + return this; + } + else + { + return new EndpointI(_instance, _host, _port, timeout, _connectionId, _compress); + } + } + + // + // Return a new endpoint with a different connection id. + // + public IceInternal.EndpointI + connectionId(String connectionId) + { + if(connectionId.equals(_connectionId)) + { + return this; + } + else + { + return new EndpointI(_instance, _host, _port, _timeout, connectionId, _compress); + } + } + + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + public boolean + compress() + { + return _compress; + } + + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + public IceInternal.EndpointI + compress(boolean compress) + { + if(compress == _compress) + { + return this; + } + else + { + return new EndpointI(_instance, _host, _port, _timeout, _connectionId, compress); + } + } + + // + // Return true if the endpoint is datagram-based. + // + public boolean + datagram() + { + return false; + } + + // + // Return true if the endpoint is secure. + // + public boolean + secure() + { + return true; + } + + // + // Return true if the endpoint type is unknown. + // + public boolean + unknown() + { + return false; + } + + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. In case a + // transceiver is created, this operation also returns a new + // "effective" endpoint, which might differ from this endpoint, + // for example, if a dynamic port number is assigned. + // + public IceInternal.Transceiver + transceiver(IceInternal.EndpointIHolder endpoint) + { + endpoint.value = this; + return null; + } + + // + // Return connectors for this endpoint, or empty list if no connector + // is available. + // + public java.util.ArrayList + connectors() + { + java.util.ArrayList connectors = new java.util.ArrayList(); + java.util.ArrayList addresses = IceInternal.Network.getAddresses(_host, _port); + java.util.Iterator p = addresses.iterator(); + while(p.hasNext()) + { + connectors.add(new ConnectorI(_instance, (java.net.InetSocketAddress)p.next(), _timeout, _connectionId)); + } + return connectors; + } + + // + // Return an acceptor for this endpoint, or null if no acceptors + // is available. In case an acceptor is created, this operation + // also returns a new "effective" endpoint, which might differ + // from this endpoint, for example, if a dynamic port number is + // assigned. + // + public IceInternal.Acceptor + acceptor(IceInternal.EndpointIHolder endpoint, String adapterName) + { + AcceptorI p = new AcceptorI(_instance, adapterName, _host, _port); + endpoint.value = new EndpointI(_instance, _host, p.effectivePort(), _timeout, _connectionId, _compress); + return p; + } + + // + // Expand endpoint out in to separate endpoints for each local + // host if listening on INADDR_ANY. + // + public java.util.ArrayList + expand() + { + java.util.ArrayList endps = new java.util.ArrayList(); + if(_host.equals("0.0.0.0")) + { + java.util.ArrayList hosts = IceInternal.Network.getLocalHosts(); + java.util.Iterator iter = hosts.iterator(); + while(iter.hasNext()) + { + String host = (String)iter.next(); + if(hosts.size() == 1 || !host.equals("127.0.0.1")) + { + endps.add(new EndpointI(_instance, host, _port, _timeout, _connectionId, _compress)); + + } + } + } + else + { + endps.add(this); + } + return endps; + } + + // + // Check whether the endpoint is equivalent to a specific Connector. + // + public boolean + equivalent(IceInternal.Connector connector) + { + ConnectorI sslConnector = null; + try + { + sslConnector = (ConnectorI)connector; + } + catch(ClassCastException ex) + { + return false; + } + return sslConnector.equivalent(_host, _port); + } + + public int + hashCode() + { + return _hashCode; + } + + // + // Compare endpoints for sorting purposes + // + public boolean + equals(java.lang.Object obj) + { + return compareTo(obj) == 0; + } + + public int + compareTo(java.lang.Object obj) // From java.lang.Comparable + { + EndpointI p = null; + + try + { + p = (EndpointI)obj; + } + catch(ClassCastException ex) + { + try + { + IceInternal.EndpointI e = (IceInternal.EndpointI)obj; + return type() < e.type() ? -1 : 1; + } + catch(ClassCastException ee) + { + assert(false); + } + } + + if(this == p) + { + return 0; + } + + if(_port < p._port) + { + return -1; + } + else if(p._port < _port) + { + return 1; + } + + if(!_connectionId.equals(p._connectionId)) + { + return _connectionId.compareTo(p._connectionId); + } + + if(_timeout < p._timeout) + { + return -1; + } + else if(p._timeout < _timeout) + { + return 1; + } + + if(!_compress && p._compress) + { + return -1; + } + else if(!p._compress && _compress) + { + return 1; + } + + return _host.compareTo(p._host); + } + + public boolean + requiresThreadPerConnection() + { + return false; + } + + private void + calcHashValue() + { + try + { + java.net.InetSocketAddress addr = IceInternal.Network.getAddress(_host, _port); + _hashCode = addr.getAddress().getHostAddress().hashCode(); + } + catch(Ice.DNSException ex) + { + _hashCode = _host.hashCode(); + } + _hashCode = 5 * _hashCode + _port; + _hashCode = 5 * _hashCode + _timeout; + _hashCode = 5 * _hashCode + _connectionId.hashCode(); + _hashCode = 5 * _hashCode + (_compress ? 1 : 0); + } + + private Instance _instance; + private String _host; + private int _port; + private int _timeout; + private String _connectionId = ""; + private boolean _compress; + private int _hashCode; +} diff --git a/java/src/IceSSL/Instance.java b/java/src/IceSSL/Instance.java new file mode 100644 index 00000000000..0e6bb03a571 --- /dev/null +++ b/java/src/IceSSL/Instance.java @@ -0,0 +1,995 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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; + +class Instance +{ + Instance(Ice.Communicator communicator) + { + _logger = communicator.getLogger(); + _facade = Ice.Util.getProtocolPluginFacade(communicator); + _securityTraceLevel = communicator.getProperties().getPropertyAsIntWithDefault("IceSSL.Trace.Security", 0); + _securityTraceCategory = "Security"; + _trustManager = new TrustManager(communicator); + + // + // Register the endpoint factory. We have to do this now, rather than + // in initialize, because the communicator may need to interpret + // proxies before the plugin is fully initialized. + // + _facade.addEndpointFactory(new EndpointFactoryI(this)); + } + + void + initialize() + { + if(_initialized) + { + return; + } + + final String prefix = "IceSSL."; + Ice.Properties properties = communicator().getProperties(); + + // + // Parse the cipher list. + // + String ciphers = properties.getProperty(prefix + "Ciphers"); + if(ciphers.length() > 0) + { + parseCiphers(ciphers); + } + + // + // Select protocols. + // + String protocols = properties.getProperty(prefix + "Protocols"); + if(protocols.length() > 0) + { + java.util.ArrayList<String> l = new java.util.ArrayList<String>(); + String[] arr = protocols.split("[ \t,]+"); + for(int i = 0; i < arr.length; ++i) + { + String s = arr[i].toLowerCase(); + if(s.equals("ssl3") || s.equals("sslv3")) + { + l.add("SSLv3"); + } + else if(s.equals("tls") || s.equals("tls1") || s.equals("tlsv1")) + { + l.add("TLSv1"); + } + else + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unrecognized protocol `" + arr[i] + "'"; + throw e; + } + } + _protocols = new String[l.size()]; + l.toArray(_protocols); + } + + // + // CheckCertName determines whether we compare the name in a peer's + // certificate against its hostname. + // + _checkCertName = properties.getPropertyAsIntWithDefault(prefix + "CheckCertName", 0) > 0; + + // + // VerifyDepthMax establishes the maximum length of a peer's certificate + // chain, including the peer's certificate. A value of 0 means there is + // no maximum. + // + _verifyDepthMax = properties.getPropertyAsIntWithDefault(prefix + "VerifyDepthMax", 2); + + // + // Check for a certificate verifier. + // + final String certVerifierClass = properties.getProperty(prefix + "CertVerifier"); + if(certVerifierClass.length() > 0) + { + if(_verifier != null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: certificate verifier already installed"; + throw e; + } + + Class cls = null; + try + { + cls = Class.forName(certVerifierClass); + } + catch(Throwable ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to load certificate verifier class " + certVerifierClass; + e.initCause(ex); + throw e; + } + + try + { + _verifier = (CertificateVerifier)cls.newInstance(); + } + catch(Throwable ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to instantiate certificate verifier class " + certVerifierClass; + e.initCause(ex); + throw e; + } + } + + // + // Check for a password callback. + // + final String passwordCallbackClass = properties.getProperty(prefix + "PasswordCallback"); + if(passwordCallbackClass.length() > 0) + { + if(_passwordCallback != null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: password callback already installed"; + throw e; + } + + Class cls = null; + try + { + cls = Class.forName(passwordCallbackClass); + } + catch(Throwable ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to load password callback class " + passwordCallbackClass; + e.initCause(ex); + throw e; + } + + try + { + _passwordCallback = (PasswordCallback)cls.newInstance(); + } + catch(Throwable ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to instantiate password callback class " + passwordCallbackClass; + e.initCause(ex); + throw e; + } + } + + // + // If the user doesn't supply an SSLContext, we need to create one based + // on property settings. + // + if(_context == null) + { + try + { + // + // Check for a default directory. We look in this directory for + // files mentioned in the configuration. + // + _defaultDir = properties.getProperty(prefix + "DefaultDir"); + + // + // We need a SecureRandom object. + // + // NOTE: The JDK recommends obtaining a SecureRandom object like this: + // + // java.security.SecureRandom rand = java.security.SecureRandom.getInstance("SHA1PRNG"); + // + // However, there is a bug (6202721) which causes it to always use /dev/random, + // which can lead to long delays at program startup. The workaround is to use + // the default constructor. + // + java.security.SecureRandom rand = new java.security.SecureRandom(); + + // + // Check for seed data for the random number generator. + // + final String seedFiles = properties.getProperty(prefix + "Random"); + if(seedFiles.length() > 0) + { + byte[] seed = null; + int start = 0; + final String[] arr = seedFiles.split(java.io.File.pathSeparator); + for(int i = 0; i < arr.length; ++i) + { + Ice.StringHolder seedFile = new Ice.StringHolder(arr[i]); + if(!checkPath(seedFile, false)) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: random seed file not found:\n" + arr[i]; + throw e; + } + java.io.File f = new java.io.File(seedFile.value); + int num = (int)f.length(); + if(seed == null) + { + seed = new byte[num]; + } + else + { + byte[] tmp = new byte[seed.length + num]; + System.arraycopy(seed, 0, tmp, 0, seed.length); + start = seed.length; + seed = tmp; + } + try + { + java.io.FileInputStream in = new java.io.FileInputStream(f); + in.read(seed, start, num); + in.close(); + } + catch(java.io.IOException ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: error while reading random seed file:\n" + arr[i]; + e.initCause(ex); + throw e; + } + } + rand.setSeed(seed); + } + + // + // We call nextInt() in order to force the object to perform any time-consuming + // initialization tasks now. + // + rand.nextInt(); + + // + // The keystore holds private keys and associated certificates. + // + Ice.StringHolder keystorePath = new Ice.StringHolder(properties.getProperty(prefix + "Keystore")); + + // + // The password for the keys. + // + String password = properties.getProperty(prefix + "Password"); + + // + // The password for the keystore. + // + 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.getProperty(prefix + "Truststore")); + + // + // The password for the truststore. + // + String truststorePassword = properties.getProperty(prefix + "TruststorePassword"); + + // + // The truststore type defaults to "JKS", but it can also be "PKCS12". + // + final String truststoreType = + properties.getPropertyWithDefault(prefix + "TruststoreType", + java.security.KeyStore.getDefaultType()); + + // + // Collect the key managers. + // + javax.net.ssl.KeyManager[] keyManagers = null; + if(keystorePath.value.length() > 0) + { + if(!checkPath(keystorePath, false)) + { + 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) + { + passwordChars = keystorePassword.toCharArray(); + } + else if(_passwordCallback != null) + { + passwordChars = _passwordCallback.getKeystorePassword(); + } + + java.io.BufferedInputStream bis = + new java.io.BufferedInputStream(new java.io.FileInputStream(keystorePath.value)); + keys.load(bis, passwordChars); + + if(passwordChars != null) + { + java.util.Arrays.fill(passwordChars, '\0'); + } + keystorePassword = null; + } + 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; + } + + String algorithm = javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm(); + javax.net.ssl.KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance(algorithm); + char[] passwordChars = new char[0]; // This password cannot be null. + if(password.length() > 0) + { + passwordChars = password.toCharArray(); + } + else if(_passwordCallback != null) + { + passwordChars = _passwordCallback.getPassword(alias); + } + kmf.init(keys, passwordChars); + if(passwordChars.length > 0) + { + java.util.Arrays.fill(passwordChars, '\0'); + } + password = null; + 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)) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: keystore does not contain an entry with alias `" + alias + "'"; + throw e; + } + + for(int i = 0; i < keyManagers.length; ++i) + { + keyManagers[i] = new X509KeyManagerI((javax.net.ssl.X509KeyManager)keyManagers[i], alias); + } + } + } + + // + // Collect the trust managers. + // + javax.net.ssl.TrustManager[] trustManagers = null; + if(truststorePath.value.length() > 0) + { + if(!checkPath(truststorePath, false)) + { + 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(); + } + else if(_passwordCallback != null) + { + passwordChars = _passwordCallback.getTruststorePassword(); + } + + java.io.BufferedInputStream bis = + new java.io.BufferedInputStream(new java.io.FileInputStream(truststorePath.value)); + ts.load(bis, passwordChars); + + if(passwordChars != null) + { + java.util.Arrays.fill(passwordChars, '\0'); + } + truststorePassword = null; + } + catch(java.io.IOException ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to load truststore:\n" + truststorePath.value; + 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(); + } + + // + // The default TrustManager implementation in IBM's JDK does not accept + // anonymous ciphers, so we have to install our own. + // + if(trustManagers == null) + { + trustManagers = new javax.net.ssl.TrustManager[1]; + trustManagers[0] = new X509TrustManagerI(null); + } + else + { + for(int i = 0; i < trustManagers.length; ++i) + { + trustManagers[i] = new X509TrustManagerI((javax.net.ssl.X509TrustManager)trustManagers[i]); + } + } + + // + // Initialize the SSL context. + // + _context = javax.net.ssl.SSLContext.getInstance("SSL"); + _context.init(keyManagers, trustManagers, rand); + } + catch(java.security.GeneralSecurityException ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to initialize context"; + e.initCause(ex); + throw e; + } + } + + _initialized = true; + } + + void + context(javax.net.ssl.SSLContext context) + { + if(_initialized) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: plugin is already initialized"; + throw ex; + } + + _context = context; + } + + javax.net.ssl.SSLContext + context() + { + return _context; + } + + void + setCertificateVerifier(CertificateVerifier verifier) + { + _verifier = verifier; + } + + CertificateVerifier + getCertificateVerifier() + { + return _verifier; + } + + void + setPasswordCallback(PasswordCallback callback) + { + _passwordCallback = callback; + } + + PasswordCallback + getPasswordCallback() + { + return _passwordCallback; + } + + Ice.Communicator + communicator() + { + return _facade.getCommunicator(); + } + + String + defaultHost() + { + return _facade.getDefaultHost(); + } + + int + networkTraceLevel() + { + return _facade.getNetworkTraceLevel(); + } + + String + networkTraceCategory() + { + return _facade.getNetworkTraceCategory(); + } + + int + securityTraceLevel() + { + return _securityTraceLevel; + } + + String + securityTraceCategory() + { + return _securityTraceCategory; + } + + boolean + initialized() + { + return _initialized; + } + + javax.net.ssl.SSLEngine + createSSLEngine(boolean incoming) + { + javax.net.ssl.SSLEngine engine = _context.createSSLEngine(); + engine.setUseClientMode(!incoming); + + String[] cipherSuites = filterCiphers(engine.getSupportedCipherSuites(), engine.getEnabledCipherSuites()); + try + { + engine.setEnabledCipherSuites(cipherSuites); + } + catch(IllegalArgumentException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: invalid ciphersuite"; + e.initCause(ex); + throw e; + } + + if(_securityTraceLevel >= 1) + { + StringBuffer s = new StringBuffer(); + s.append("enabling SSL ciphersuites:"); + for(int i = 0; i < cipherSuites.length; ++i) + { + s.append("\n " + cipherSuites[i]); + } + _logger.trace(_securityTraceCategory, s.toString()); + } + + if(_protocols != null) + { + try + { + engine.setEnabledProtocols(_protocols); + } + catch(IllegalArgumentException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: invalid protocol"; + e.initCause(ex); + throw e; + } + } + + if(incoming) + { + int verifyPeer = communicator().getProperties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); + if(verifyPeer == 0) + { + engine.setWantClientAuth(false); + engine.setNeedClientAuth(false); + } + else if(verifyPeer == 1) + { + engine.setWantClientAuth(true); + } + else + { + engine.setNeedClientAuth(true); + } + } + + try + { + engine.beginHandshake(); + } + catch(javax.net.ssl.SSLException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: handshake error"; + e.initCause(ex); + throw e; + } + + return engine; + } + + String[] + filterCiphers(String[] supportedCiphers, String[] defaultCiphers) + { + java.util.LinkedList<String> result = new java.util.LinkedList<String>(); + if(_allCiphers) + { + for(int i = 0; i < supportedCiphers.length; ++i) + { + result.add(supportedCiphers[i]); + } + } + else if(!_noCiphers) + { + for(int i = 0; i < defaultCiphers.length; ++i) + { + result.add(defaultCiphers[i]); + } + } + + if(_ciphers != null) + { + for(int i = 0; i < _ciphers.length; ++i) + { + CipherExpression ce = (CipherExpression)_ciphers[i]; + if(ce.not) + { + java.util.Iterator e = result.iterator(); + while(e.hasNext()) + { + String cipher = (String)e.next(); + if(ce.cipher != null) + { + if(ce.cipher.equals(cipher)) + { + e.remove(); + } + } + else + { + assert(ce.re != null); + java.util.regex.Matcher m = ce.re.matcher(cipher); + if(m.find()) + { + e.remove(); + } + } + } + } + else + { + if(ce.cipher != null) + { + result.add(0, ce.cipher); + } + else + { + assert(ce.re != null); + for(int j = 0; j < supportedCiphers.length; ++j) + { + java.util.regex.Matcher m = ce.re.matcher(supportedCiphers[j]); + if(m.find()) + { + result.add(0, supportedCiphers[j]); + } + } + } + } + } + } + + String[] arr = new String[result.size()]; + result.toArray(arr); + return arr; + } + + String[] + protocols() + { + return _protocols; + } + + void + traceConnection(java.nio.channels.SocketChannel fd, javax.net.ssl.SSLEngine engine, boolean incoming) + { + javax.net.ssl.SSLSession session = engine.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(_securityTraceCategory, msg); + } + + void + verifyPeer(ConnectionInfo info, java.nio.channels.SelectableChannel fd, String address, boolean incoming) + { + if(_verifyDepthMax > 0 && info.certs != null && info.certs.length > _verifyDepthMax) + { + String msg = (incoming ? "incoming" : "outgoing") + " connection rejected:\n" + + "length of peer's certificate chain (" + info.certs.length + ") exceeds maximum of " + + _verifyDepthMax + "\n" + + IceInternal.Network.fdToString(fd); + if(_securityTraceLevel >= 1) + { + _logger.trace(_securityTraceCategory, msg); + } + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = msg; + throw ex; + } + + // + // Extract the IP addresses and the DNS names from the subject + // alternative names. + // + if(info.certs != null) + { + try + { + java.util.Collection subjectAltNames = + ((java.security.cert.X509Certificate)info.certs[0]).getSubjectAlternativeNames(); + java.util.ArrayList<String> ipAddresses = new java.util.ArrayList<String>(); + java.util.ArrayList<String> dnsNames = new java.util.ArrayList<String>(); + if(subjectAltNames != null) + { + java.util.Iterator i = subjectAltNames.iterator(); + while(i.hasNext()) + { + java.util.List l = (java.util.List)i.next(); + assert(!l.isEmpty()); + Integer n = (Integer)l.get(0); + if(n.intValue() == 7) + { + ipAddresses.add((String)l.get(1)); + } + else if(n.intValue() == 2) + { + dnsNames.add(((String)l.get(1)).toLowerCase()); + } + } + } + + // + // Compare the peer's address against the dnsName and ipAddress values. + // This is only relevant for an outgoing connection. + // + if(address.length() > 0) + { + boolean certNameOK = ipAddresses.contains(address); + if(!certNameOK) + { + certNameOK = dnsNames.contains(address.toLowerCase()); + } + + // + // Log a message if the name comparison fails. If CheckCertName is defined, + // we also raise an exception to abort the connection. Don't log a message if + // CheckCertName is not defined and a verifier is present. + // + if(!certNameOK && (_checkCertName || (_securityTraceLevel >= 1 && _verifier == null))) + { + StringBuffer sb = new StringBuffer(); + sb.append("IceSSL: "); + if(!_checkCertName) + { + sb.append("ignoring "); + } + sb.append("certificate validation failure:\npeer certificate does not contain `" + + address + "' in its subjectAltName extension"); + if(!dnsNames.isEmpty()) + { + sb.append("\nDNS names found in certificate: "); + for(int j = 0; j < dnsNames.size(); ++j) + { + if(j > 0) + { + sb.append(", "); + } + sb.append(dnsNames.get(j).toString()); + } + } + if(!ipAddresses.isEmpty()) + { + sb.append("\nIP addresses found in certificate: "); + for(int j = 0; j < ipAddresses.size(); ++j) + { + if(j > 0) + { + sb.append(", "); + } + sb.append(ipAddresses.get(j).toString()); + } + } + if(_securityTraceLevel >= 1) + { + _logger.trace(_securityTraceCategory, sb.toString()); + } + if(_checkCertName) + { + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = sb.toString(); + throw ex; + } + } + } + } + catch(java.security.cert.CertificateParsingException ex) + { + assert(false); + } + } + + if(!_trustManager.verify(info)) + { + String msg = (incoming ? "incoming" : "outgoing") + " connection rejected by trust manager\n" + + IceInternal.Network.fdToString(fd); + if(_securityTraceLevel >= 1) + { + _logger.trace(_securityTraceCategory, msg); + } + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = msg; + throw ex; + } + + if(_verifier != null && !_verifier.verify(info)) + { + String msg = (incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier\n" + + IceInternal.Network.fdToString(fd); + + if(_securityTraceLevel > 0) + { + _logger.trace(_securityTraceCategory, msg); + } + + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = msg; + throw ex; + } + } + + private void + parseCiphers(String ciphers) + { + java.util.ArrayList<CipherExpression> cipherList = new java.util.ArrayList<CipherExpression>(); + 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; + String cipher; + java.util.regex.Pattern re; + } + + private Ice.Logger _logger; + private IceInternal.ProtocolPluginFacade _facade; + private int _securityTraceLevel; + private String _securityTraceCategory; + private boolean _initialized; + private javax.net.ssl.SSLContext _context; + private String _defaultDir; + private CipherExpression[] _ciphers; + private boolean _allCiphers; + private boolean _noCiphers; + private String[] _protocols; + private boolean _checkCertName; + private int _verifyDepthMax; + private CertificateVerifier _verifier; + private PasswordCallback _passwordCallback; + private TrustManager _trustManager; +} diff --git a/java/src/IceSSL/PasswordCallback.java b/java/src/IceSSL/PasswordCallback.java new file mode 100644 index 00000000000..55aa3144ba5 --- /dev/null +++ b/java/src/IceSSL/PasswordCallback.java @@ -0,0 +1,36 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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; + +// +// A password callback is an alternate way of supplying the plugin with +// passwords that avoids using plaintext configuration properties. +// +public interface PasswordCallback +{ + // + // Obtain the password for the key. If an alias was selected using + // the IceSSL.Alias property, the value of the property is provided. + // The return value must not be null. + // + char[] getPassword(String alias); + + // + // Obtain the password for validating the truststore. Return null + // to skip truststore validation. + // + char[] getTruststorePassword(); + + // + // Obtain the password for validating the keystore. Return null + // to skip keystore validation. + // + char[] getKeystorePassword(); +} diff --git a/java/src/IceSSL/Plugin.java b/java/src/IceSSL/Plugin.java new file mode 100644 index 00000000000..15aeb7692cc --- /dev/null +++ b/java/src/IceSSL/Plugin.java @@ -0,0 +1,54 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 +{ + // + // Establish the SSL context. This must be done before the + // plugin is initialized, therefore the application must define + // the property Ice.InitPlugins=0, set the context, and finally + // invoke initializePlugins on the PluginManager. + // + // When the application supplies its own SSL context, the + // plugin skips its normal property-based configuration. + // + void setContext(javax.net.ssl.SSLContext context); + + // + // Obtain the SSL context. Use caution when modifying this value. + // Changes made to this value have no effect on existing connections. + // + javax.net.ssl.SSLContext getContext(); + + // + // Establish the certificate verifier object. This should be + // done before any connections are established. + // + void setCertificateVerifier(CertificateVerifier verifier); + + // + // Obtain the certificate verifier object. Returns null if no + // verifier is set. + // + CertificateVerifier getCertificateVerifier(); + + // + // Establish the password callback object. This should be + // done before the plugin is initialized. + // + void setPasswordCallback(PasswordCallback callback); + + // + // Obtain the password callback object. Returns null if no + // callback is set. + // + PasswordCallback getPasswordCallback(); +} diff --git a/java/src/IceSSL/PluginFactory.java b/java/src/IceSSL/PluginFactory.java new file mode 100644 index 00000000000..70064d8a1b0 --- /dev/null +++ b/java/src/IceSSL/PluginFactory.java @@ -0,0 +1,19 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 class PluginFactory implements Ice.PluginFactory +{ + public Ice.Plugin + create(Ice.Communicator communicator, String name, String[] args) + { + return new PluginI(communicator); + } +} diff --git a/java/src/IceSSL/PluginI.java b/java/src/IceSSL/PluginI.java new file mode 100644 index 00000000000..2021b7ae9f2 --- /dev/null +++ b/java/src/IceSSL/PluginI.java @@ -0,0 +1,68 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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; + +class PluginI implements Plugin +{ + public + PluginI(Ice.Communicator communicator) + { + _instance = new Instance(communicator); + } + + public void + initialize() + { + _instance.initialize(); + } + + public void + destroy() + { + } + + public void + setContext(javax.net.ssl.SSLContext context) + { + _instance.context(context); + } + + public javax.net.ssl.SSLContext + getContext() + { + return _instance.context(); + } + + public void + setCertificateVerifier(CertificateVerifier verifier) + { + _instance.setCertificateVerifier(verifier); + } + + public CertificateVerifier + getCertificateVerifier() + { + return _instance.getCertificateVerifier(); + } + + public void + setPasswordCallback(PasswordCallback callback) + { + _instance.setPasswordCallback(callback); + } + + public PasswordCallback + getPasswordCallback() + { + return _instance.getPasswordCallback(); + } + + private Instance _instance; +} diff --git a/java/src/IceSSL/RFC2253.java b/java/src/IceSSL/RFC2253.java new file mode 100644 index 00000000000..601f8622aa4 --- /dev/null +++ b/java/src/IceSSL/RFC2253.java @@ -0,0 +1,417 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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; + +// +// See RFC 2253 and RFC 1779. +// +class RFC2253 +{ + static class ParseException extends Ice.LocalException + { + public ParseException() + { + } + + public ParseException(String reason) + { + this.reason = reason; + } + + public String + ice_name() + { + return "RFC2253::ParseException"; + } + + public String reason; + } + + static class RDNPair + { + String key; + String value; + } + + static private class ParseState + { + String data; + int pos; + } + + public static java.util.List + parse(String data) + throws ParseException + { + java.util.List results = new java.util.LinkedList(); + java.util.List current = new java.util.LinkedList(); + ParseState state = new ParseState(); + state.data = data; + state.pos = 0; + while(state.pos < state.data.length()) + { + current.add(parseNameComponent(state)); + eatWhite(state); + if(state.pos < state.data.length() && state.data.charAt(state.pos) == ',') + { + ++state.pos; + } + else if(state.pos < state.data.length() && state.data.charAt(state.pos) == ';') + { + ++state.pos; + results.add(current); + current = new java.util.LinkedList(); + } + else if(state.pos < state.data.length()) + { + throw new ParseException("expected ',' or ';' at `" + state.data.substring(state.pos) + "'"); + } + } + if(!current.isEmpty()) + { + results.add(current); + } + + return results; + } + + public static java.util.List + parseStrict(String data) + throws ParseException + { + java.util.List results = new java.util.LinkedList(); + ParseState state = new ParseState(); + state.data = data; + state.pos = 0; + while(state.pos < state.data.length()) + { + results.add(parseNameComponent(state)); + eatWhite(state); + if(state.pos < state.data.length() && + (state.data.charAt(state.pos) == ',' || state.data.charAt(state.pos) == ';')) + { + ++state.pos; + } + else if(state.pos < state.data.length()) + { + throw new ParseException("expected ',' or ';' at `" + state.data.substring(state.pos) + "'"); + } + } + return results; + } + + private static RDNPair + parseNameComponent(ParseState state) + throws ParseException + { + RDNPair result = parseAttributeTypeAndValue(state); + while(state.pos < state.data.length()) + { + eatWhite(state); + if(state.pos < state.data.length() && state.data.charAt(state.pos) == '+') + { + ++state.pos; + } + else + { + break; + } + RDNPair p = parseAttributeTypeAndValue(state); + result.value += "+"; + result.value += p.key; + result.value += '='; + result.value += p.value; + } + return result; + } + + private static RDNPair + parseAttributeTypeAndValue(ParseState state) + throws ParseException + { + RDNPair p = new RDNPair(); + p.key = parseAttributeType(state); + eatWhite(state); + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid attribute type/value pair (unexpected end of state.data)"); + } + if(state.data.charAt(state.pos) != '=') + { + throw new ParseException("invalid attribute type/value pair (missing =)"); + } + ++state.pos; + p.value = parseAttributeValue(state); + return p; + } + + private static String + parseAttributeType(ParseState state) + throws ParseException + { + eatWhite(state); + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid attribute type (expected end of state.data)"); + } + + String result = new String(); + + // + // RFC 1779. + // <key> ::= 1*( <keychar> ) | "OID." <oid> | "oid." <oid> + // <oid> ::= <digitString> | <digitstring> "." <oid> + // RFC 2253: + // attributeType = (ALPHA 1*keychar) | oid + // keychar = ALPHA | DIGIT | "-" + // oid = 1*DIGIT *("." 1*DIGIT) + // + // In section 4 of RFC 2253 the document says: + // Implementations MUST allow an oid in the attribute type to be + // prefixed by one of the character Strings "oid." or "OID.". + // + // Here we must also check for "oid." and "OID." before parsing + // according to the ALPHA KEYCHAR* rule. + // + // First the OID case. + // + if(Character.isDigit(state.data.charAt(state.pos)) || + (state.data.length() - state.pos >= 4 && (state.data.substring(state.pos, state.pos + 4) == "oid." || + state.data.substring(state.pos, state.pos + 4) == "OID."))) + { + if(!Character.isDigit(state.data.charAt(state.pos))) + { + result += state.data.substring(state.pos, state.pos + 4); + state.pos += 4; + } + + while(true) + { + // 1*DIGIT + while(state.pos < state.data.length() && Character.isDigit(state.data.charAt(state.pos))) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + // "." 1*DIGIT + if(state.pos < state.data.length() && state.data.charAt(state.pos) == '.') + { + result += state.data.charAt(state.pos); + ++state.pos; + // 1*DIGIT must follow "." + if(state.pos < state.data.length() && !Character.isDigit(state.data.charAt(state.pos))) + { + throw new ParseException("invalid attribute type (expected end of state.data)"); + } + } + else + { + break; + } + } + } + else if(Character.isUpperCase(state.data.charAt(state.pos)) || + Character.isLowerCase(state.data.charAt(state.pos))) + { + // + // The grammar is wrong in this case. It should be ALPHA + // KEYCHAR* otherwise it will not accept "O" as a valid + // attribute type. + // + result += state.data.charAt(state.pos); + ++state.pos; + // 1* KEYCHAR + while(state.pos < state.data.length() && + (Character.isDigit(state.data.charAt(state.pos)) || + Character.isUpperCase(state.data.charAt(state.pos)) || + Character.isLowerCase(state.data.charAt(state.pos)) || + state.data.charAt(state.pos) == '-')) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + } + else + { + throw new ParseException("invalid attribute type"); + } + return result; + } + + private static String + parseAttributeValue(ParseState state) + throws ParseException + { + eatWhite(state); + String result = new String(); + if(state.pos >= state.data.length()) + { + return result; + } + + // + // RFC 2253 + // # hexString + // + if(state.data.charAt(state.pos) == '#') + { + result += state.data.charAt(state.pos); + ++state.pos; + while(true) + { + String h = parseHexPair(state, true); + if(h.length() == 0) + { + break; + } + result += h; + } + } + // + // RFC 2253 + // QUOTATION *( quotechar | pair ) QUOTATION ; only from v2 + // quotechar = <any character except "\" or QUOTATION > + // + else if(state.data.charAt(state.pos) == '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + while(true) + { + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid attribute value (unexpected end of state.data)"); + } + // final terminating " + if(state.data.charAt(state.pos) == '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + break; + } + // any character except '\' + else if(state.data.charAt(state.pos) != '\\') + { + result += state.data.charAt(state.pos); + ++state.pos; + } + // pair '\' + else + { + result += parsePair(state); + } + } + } + // + // RFC 2253 + // * (Stringchar | pair) + // Stringchar = <any character except one of special, "\" or QUOTATION > + // + else + { + while(state.pos < state.data.length()) + { + if(state.data.charAt(state.pos) == '\\') + { + result += parsePair(state); + } + else if(special.indexOf(state.data.charAt(state.pos)) == -1 && state.data.charAt(state.pos) != '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + } + else + { + break; + } + } + } + return result; + } + + // + // RFC2253: + // pair = "\" ( special | "\" | QUOTATION | hexpair ) + // + private static String + parsePair(ParseState state) + throws ParseException + { + String result = new String(); + + assert(state.data.charAt(state.pos) == '\\'); + result += state.data.charAt(state.pos); + ++state.pos; + + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid escape format (unexpected end of state.data)"); + } + + if(special.indexOf(state.data.charAt(state.pos)) != -1 || state.data.charAt(state.pos) != '\\' || + state.data.charAt(state.pos) != '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + return result; + } + return parseHexPair(state, false); + } + + // + // RFC 2253 + // hexpair = hexchar hexchar + // + private static String + parseHexPair(ParseState state, boolean allowEmpty) + throws ParseException + { + String result = new String(); + if(state.pos < state.data.length() && hexvalid.indexOf(state.data.charAt(state.pos)) != -1) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + if(state.pos < state.data.length() && hexvalid.indexOf(state.data.charAt(state.pos)) != -1) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + if(result.length() != 2) + { + if(allowEmpty && result.length() == 0) + { + return result; + } + throw new ParseException("invalid hex format"); + } + return result; + } + + // + // RFC 2253: + // + // Implementations MUST allow for space (' ' ASCII 32) characters to be + // present between name-component and ',', between attributeTypeAndValue + // and '+', between attributeType and '=', and between '=' and + // attributeValue. These space characters are ignored when parsing. + // + private static void + eatWhite(ParseState state) + { + while(state.pos < state.data.length() && state.data.charAt(state.pos) == ' ') + { + ++state.pos; + } + } + + private final static String special = ",=+<>#;"; + private final static String hexvalid = "0123456789abcdefABCDEF"; +} diff --git a/java/src/IceSSL/TransceiverI.java b/java/src/IceSSL/TransceiverI.java new file mode 100644 index 00000000000..b43a340c703 --- /dev/null +++ b/java/src/IceSSL/TransceiverI.java @@ -0,0 +1,854 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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; + +import java.nio.*; +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.*; + +final class TransceiverI implements IceInternal.Transceiver +{ + public java.nio.channels.SelectableChannel + fd() + { + assert(_fd != null); + return _fd; + } + + public void + close() + { + if(_instance.networkTraceLevel() >= 1) + { + String s = "closing ssl connection\n" + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + assert(_fd != null); + + if(_readSelector != null) + { + try + { + _readSelector.close(); + } + catch(java.io.IOException ex) + { + // Ignore. + } + _readSelector = null; + } + + if(_writeSelector != null) + { + try + { + _writeSelector.close(); + } + catch(java.io.IOException ex) + { + // Ignore. + } + _writeSelector = null; + } + + try + { + _engine.closeInbound(); + } + catch(SSLException ex) + { + // + // SSLEngine always raises an exception with this message: + // + // Inbound closed before receiving peer's close_notify: possible truncation attack? + // + // We would probably need to wait for a response in shutdown() to avoid this. + // For now, we'll ignore this exception. + // + //_logger.error("IceSSL: error during close\n" + ex.getMessage()); + } + + try + { + _fd.close(); + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + finally + { + _fd = null; + } + } + + // + // All methods that can write to the socket are synchronized. + // + public synchronized void + shutdownWrite() + { + if(_instance.networkTraceLevel() >= 2) + { + String s = "shutting down ssl connection for writing\n" + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + shutdown(); + + assert(_fd != null); + java.net.Socket socket = _fd.socket(); + try + { + socket.shutdownOutput(); // Shutdown socket for writing. + } + catch(java.net.SocketException ex) + { + // + // Ignore. We can't reliably figure out if the socket + // exception is because the socket is not connected. + // + // if(!IceInternal.Network.notConnected(ex)) + // { + // Ice.SocketException se = new Ice.SocketException(); + // se.initCause(ex); + // throw se; + // } + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + + // + // All methods that can write to the socket are synchronized. + // + public synchronized void + shutdownReadWrite() + { + if(_instance.networkTraceLevel() >= 2) + { + String s = "shutting down ssl connection for reading and writing\n" + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + shutdown(); + + assert(_fd != null); + java.net.Socket socket = _fd.socket(); + try + { + socket.shutdownInput(); // Shutdown socket for reading + socket.shutdownOutput(); // Shutdown socket for writing + } + catch(java.net.SocketException ex) + { + // + // Ignore. We can't reliably figure out if the socket + // exception is because the socket is not connected. + // + // if(!IceInternal.Network.notConnected(ex)) + // { + // Ice.SocketException se = new Ice.SocketException(); + // se.initCause(ex); + // throw se; + // } + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + + // + // All methods that can write to the socket are synchronized. + // + public synchronized void + write(IceInternal.BasicStream stream, int timeout) + throws IceInternal.LocalExceptionWrapper + { + // + // Complete handshaking first if necessary. + // + if(!_handshakeComplete) + { + handshake(timeout); + } + + ByteBuffer buf = stream.prepareWrite(); + + // + // Write any pending data to the socket. + // + flush(timeout); + + try + { + while(buf.hasRemaining()) + { + final int rem = buf.remaining(); + + // + // Encrypt the buffer. + // + SSLEngineResult result = _engine.wrap(buf, _netOutput); + switch(result.getStatus()) + { + case BUFFER_OVERFLOW: + assert(false); + break; + case BUFFER_UNDERFLOW: + assert(false); + break; + case CLOSED: + throw new Ice.ConnectionLostException(); + case OK: + break; + } + + // + // Write the encrypted data to the socket. + // + if(result.bytesProduced() > 0) + { + flush(timeout); + + if(_instance.networkTraceLevel() >= 3) + { + String s = "sent " + result.bytesConsumed() + " of " + rem + " bytes via ssl\n" + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + if(_stats != null) + { + _stats.bytesSent(type(), result.bytesConsumed()); + } + } + } + } + catch(SSLException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: error while encoding message"; + e.initCause(ex); + throw e; + } + } + + public boolean + read(IceInternal.BasicStream stream, int timeout) + { + ByteBuffer buf = stream.prepareRead(); + + int rem = 0; + if(_instance.networkTraceLevel() >= 3) + { + rem = buf.remaining(); + } + + // + // Complete handshaking first if necessary. + // + if(!_handshakeComplete) + { + handshake(timeout); + } + + // + // Try to satisfy the request from data we've already decrypted. + // + int pos = buf.position(); + fill(buf); + + if(_instance.networkTraceLevel() >= 3 && buf.position() > pos) + { + String s = "received " + (buf.position() - pos) + " of " + rem + " bytes via ssl\n" + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + if(_stats != null && buf.position() > pos) + { + _stats.bytesReceived(type(), buf.position() - pos); + } + + // + // Read and decrypt more data if necessary. Note that we might read + // more data from the socket than is actually necessary to fill the + // caller's stream. + // + try + { + while(buf.hasRemaining()) + { + _netInput.flip(); + SSLEngineResult result = _engine.unwrap(_netInput, _appInput); + _netInput.compact(); + switch(result.getStatus()) + { + case BUFFER_OVERFLOW: + assert(false); + break; + case BUFFER_UNDERFLOW: + read(timeout); + continue; + case CLOSED: + throw new Ice.ConnectionLostException(); + case OK: + break; + } + + pos = buf.position(); + fill(buf); + + if(_instance.networkTraceLevel() >= 3 && buf.position() > pos) + { + String s = "received " + (buf.position() - pos) + " of " + rem + " bytes via ssl\n" + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + if(_stats != null && buf.position() > pos) + { + _stats.bytesReceived(type(), buf.position() - pos); + } + } + } + catch(SSLException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: error during read"; + e.initCause(ex); + throw e; + } + + // + // Return a boolean to indicate whether more data is available. + // + return _netInput.position() > 0; + } + + public String + type() + { + return "ssl"; + } + + public String + toString() + { + return _desc; + } + + public void + checkSendSize(IceInternal.BasicStream stream, int messageSizeMax) + { + if(stream.size() > messageSizeMax) + { + throw new Ice.MemoryLimitException(); + } + } + + ConnectionInfo + getConnectionInfo() + { + // + // This can only be called on an open transceiver. + // + assert(_fd != null); + return _info; + } + + // + // Only for use by ConnectorI, AcceptorI. + // + TransceiverI(Instance instance, javax.net.ssl.SSLEngine engine, java.nio.channels.SocketChannel fd, + String host, boolean incoming, String adapterName) + { + _instance = instance; + _engine = engine; + _fd = fd; + _host = host; + _adapterName = adapterName; + _incoming = incoming; + _logger = instance.communicator().getLogger(); + try + { + _stats = instance.communicator().getStats(); + } + catch(Ice.CommunicatorDestroyedException ex) + { + // Ignore. + } + _desc = IceInternal.Network.fdToString(_fd); + _maxPacketSize = 0; + if(System.getProperty("os.name").startsWith("Windows")) + { + // + // On Windows, limiting the buffer size is important to prevent + // poor throughput performances when transfering large amount of + // data. See Microsoft KB article KB823764. + // + _maxPacketSize = IceInternal.Network.getSendBufferSize(_fd) / 2; + if(_maxPacketSize < 512) + { + _maxPacketSize = 0; + } + } + + // TODO: Buffer cache? + _appInput = ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize() * 2); + _netInput = ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize() * 2); + _netOutput = ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize() * 2); + _handshakeComplete = false; + } + + protected void + finalize() + throws Throwable + { + IceUtil.Assert.FinalizerAssert(_fd == null); + + super.finalize(); + } + + private void + flush(int timeout) + { + _netOutput.flip(); + + int size = _netOutput.limit(); + int packetSize = 0; + if(_maxPacketSize > 0 && size > _maxPacketSize) + { + packetSize = _maxPacketSize; + _netOutput.limit(_netOutput.position() + packetSize); + } + + while(_netOutput.hasRemaining()) + { + try + { + assert(_fd != null); + int ret = _fd.write(_netOutput); + + if(ret == -1) + { + throw new Ice.ConnectionLostException(); + } + + if(ret == 0) + { + if(timeout == 0) + { + throw new Ice.TimeoutException(); + } + + if(_writeSelector == null) + { + _writeSelector = java.nio.channels.Selector.open(); + _fd.register(_writeSelector, java.nio.channels.SelectionKey.OP_WRITE, null); + } + + try + { + if(timeout > 0) + { + long start = System.currentTimeMillis(); + int n = _writeSelector.select(timeout); + if(n == 0 && System.currentTimeMillis() >= start + timeout) + { + throw new Ice.TimeoutException(); + } + } + else + { + _writeSelector.select(); + } + } + catch(java.io.InterruptedIOException ex) + { + // Ignore. + } + + continue; + } + } + catch(java.io.InterruptedIOException ex) + { + continue; + } + catch(java.io.IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + Ice.ConnectionLostException se = new Ice.ConnectionLostException(); + se.initCause(ex); + throw se; + } + + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + + if(packetSize > 0) + { + assert(_netOutput.position() == _netOutput.limit()); + int position = _netOutput.position(); + if(size - position > packetSize) + { + _netOutput.limit(position + packetSize); + } + else + { + packetSize = 0; + _netOutput.limit(size); + } + } + } + _netOutput.clear(); + } + + private void + read(int timeout) + { + while(true) + { + try + { + assert(_fd != null); + int ret = _fd.read(_netInput); + + if(ret == -1) + { + throw new Ice.ConnectionLostException(); + } + + if(ret == 0) + { + if(timeout == 0) + { + throw new Ice.TimeoutException(); + } + + if(_readSelector == null) + { + _readSelector = java.nio.channels.Selector.open(); + _fd.register(_readSelector, java.nio.channels.SelectionKey.OP_READ, null); + } + + try + { + if(timeout > 0) + { + long start = System.currentTimeMillis(); + int n = _readSelector.select(timeout); + if(n == 0 && System.currentTimeMillis() >= start + timeout) + { + throw new Ice.TimeoutException(); + } + } + else + { + _readSelector.select(); + } + } + catch(java.io.InterruptedIOException ex) + { + // Ignore. + } + + continue; + } + + break; + } + catch(java.io.InterruptedIOException ex) + { + continue; + } + catch(java.io.IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + Ice.ConnectionLostException se = new Ice.ConnectionLostException(); + se.initCause(ex); + throw se; + } + + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + } + + private void + handshake(int timeout) + { + try + { + HandshakeStatus status = _engine.getHandshakeStatus(); + while(!_handshakeComplete) + { + SSLEngineResult result = null; + switch(status) + { + case FINISHED: + handshakeCompleted(); + break; + case NEED_TASK: + { + // TODO: Use separate threads & join with timeout? + Runnable task; + while((task = _engine.getDelegatedTask()) != null) + { + task.run(); + } + status = _engine.getHandshakeStatus(); + break; + } + case NEED_UNWRAP: + { + // + // The engine needs more data. We might already have enough data in + // the _netInput buffer to satisfy the engine. If not, the engine + // responds with BUFFER_UNDERFLOW and we'll read from the socket. + // + _netInput.flip(); + result = _engine.unwrap(_netInput, _appInput); + _netInput.compact(); + // + // FINISHED is only returned from wrap or unwrap, not from engine.getHandshakeResult(). + // + status = result.getHandshakeStatus(); + switch(result.getStatus()) + { + case BUFFER_OVERFLOW: + assert(false); + break; + case BUFFER_UNDERFLOW: + assert(status == javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP); + read(timeout); + break; + case CLOSED: + throw new Ice.ConnectionLostException(); + case OK: + break; + } + break; + } + case NEED_WRAP: + { + // + // The engine needs to send a message. + // + result = _engine.wrap(_emptyBuffer, _netOutput); + if(result.bytesProduced() > 0) + { + flush(timeout); + } + // + // FINISHED is only returned from wrap or unwrap, not from engine.getHandshakeResult(). + // + status = result.getHandshakeStatus(); + break; + } + case NOT_HANDSHAKING: + assert(false); + break; + } + + if(result != null) + { + switch(result.getStatus()) + { + case BUFFER_OVERFLOW: + assert(false); + break; + case BUFFER_UNDERFLOW: + // Need to read again. + assert(status == HandshakeStatus.NEED_UNWRAP); + break; + case CLOSED: + throw new Ice.ConnectionLostException(); + case OK: + break; + } + } + } + } + catch(SSLException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: handshake error"; + e.initCause(ex); + throw e; + } + } + + private void + fill(ByteBuffer buf) + { + _appInput.flip(); + if(_appInput.hasRemaining()) + { + int bytesAvailable = _appInput.remaining(); + int bytesNeeded = buf.remaining(); + if(bytesAvailable > bytesNeeded) + { + bytesAvailable = bytesNeeded; + } + if(buf.hasArray()) + { + // + // Copy directly into the destination buffer's backing array. + // + byte[] arr = buf.array(); + int offset = buf.arrayOffset() + buf.position(); + _appInput.get(arr, offset, bytesAvailable); + buf.position(offset + bytesAvailable); + } + else if(_appInput.hasArray()) + { + // + // Copy directly from the source buffer's backing array. + // + byte[] arr = _appInput.array(); + int offset = _appInput.arrayOffset() + _appInput.position(); + buf.put(arr, offset, bytesAvailable); + _appInput.position(offset + bytesAvailable); + } + else + { + // + // Copy using a temporary array. + // + byte[] arr = new byte[bytesAvailable]; + _appInput.get(arr); + buf.put(arr); + } + } + _appInput.compact(); + } + + private void + shutdown() + { + // + // Send the close_notify message. + // + _engine.closeOutbound(); + try + { + _netOutput.clear(); + while(!_engine.isOutboundDone()) + { + _engine.wrap(_emptyBuffer, _netOutput); + try + { + flush(-1); + } + catch(Ice.ConnectionLostException ex) + { + // Ignore. + } + } + } + catch(SSLException ex) + { + Ice.SecurityException se = new Ice.SecurityException(); + se.reason = "IceSSL: SSL failure while shutting down socket"; + se.initCause(ex); + throw se; + } + } + + private void + handshakeCompleted() + { + _handshakeComplete = true; + + // + // IceSSL.VerifyPeer is translated into the proper SSLEngine configuration + // for a server, but we have to do it ourselves for a client. + // + if(!_incoming) + { + int verifyPeer = + _instance.communicator().getProperties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); + if(verifyPeer > 0) + { + try + { + _engine.getSession().getPeerCertificates(); + } + catch(javax.net.ssl.SSLPeerUnverifiedException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: server did not supply a certificate"; + e.initCause(ex); + throw e; + } + } + } + + // + // Additional verification. + // + _info = Util.populateConnectionInfo(_engine.getSession(), _fd.socket(), _adapterName, _incoming); + _instance.verifyPeer(_info, _fd, _host, _incoming); + + if(_instance.networkTraceLevel() >= 1) + { + String s; + if(_incoming) + { + s = "accepted ssl connection\n" + IceInternal.Network.fdToString(_fd); + } + else + { + s = "ssl connection established\n" + IceInternal.Network.fdToString(_fd); + } + _logger.trace(_instance.networkTraceCategory(), s); + } + + if(_instance.securityTraceLevel() >= 1) + { + _instance.traceConnection(_fd, _engine, _incoming); + } + } + + private Instance _instance; + private java.nio.channels.SocketChannel _fd; + private javax.net.ssl.SSLEngine _engine; + private String _host; + private String _adapterName; + private boolean _incoming; + private Ice.Logger _logger; + private Ice.Stats _stats; + private String _desc; + private int _maxPacketSize; + private ByteBuffer _appInput; // Holds clear-text data to be read by the application. + private ByteBuffer _netInput; // Holds encrypted data read from the socket. + private ByteBuffer _netOutput; // Holds encrypted data to be written to the socket. + private static ByteBuffer _emptyBuffer = ByteBuffer.allocate(0); // Used during handshaking. + private boolean _handshakeComplete; + private java.nio.channels.Selector _readSelector; + private java.nio.channels.Selector _writeSelector; + private ConnectionInfo _info; +} diff --git a/java/src/IceSSL/TrustManager.java b/java/src/IceSSL/TrustManager.java new file mode 100644 index 00000000000..8598c1ac7e6 --- /dev/null +++ b/java/src/IceSSL/TrustManager.java @@ -0,0 +1,295 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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; + +class TrustManager +{ + TrustManager(Ice.Communicator communicator) + { + assert communicator != null; + _communicator = communicator; + Ice.Properties properties = communicator.getProperties(); + _traceLevel = properties.getPropertyAsInt("IceSSL.Trace.Security"); + String key = null; + try + { + key = "IceSSL.TrustOnly"; + _all = parse(properties.getProperty(key)); + key = "IceSSL.TrustOnly.Client"; + _client = parse(properties.getProperty(key)); + key = "IceSSL.TrustOnly.Server"; + _allServer = parse(properties.getProperty(key)); + java.util.Map dict = properties.getPropertiesForPrefix("IceSSL.TrustOnly.Server."); + java.util.Iterator p = dict.entrySet().iterator(); + while(p.hasNext()) + { + java.util.Map.Entry entry = (java.util.Map.Entry)p.next(); + key = (String)entry.getKey(); + String name = key.substring("IceSSL.TrustOnly.Server.".length()); + _server.put(name, parse((String)entry.getValue())); + } + } + catch(RFC2253.ParseException e) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: invalid property " + key + ":\n" + e.reason; + throw ex; + } + } + + boolean + verify(ConnectionInfo info) + { + java.util.List trustset = new java.util.LinkedList(); + if(!_all.isEmpty()) + { + trustset.add(_all); + } + + if(info.incoming) + { + if(!_allServer.isEmpty()) + { + trustset.add(_allServer); + } + if(info.adapterName.length() > 0) + { + java.util.List p = (java.util.List)_server.get(info.adapterName); + if(p != null) + { + trustset.add(p); + } + } + } + else + { + if(!_client.isEmpty()) + { + trustset.add(_client); + } + } + + // + // If there is nothing to match against, then we accept the cert. + // + if(trustset.isEmpty()) + { + return true; + } + + // + // If there is no certificate then we match false. + // + if(info.certs != null && info.certs.length > 0) + { + javax.security.auth.x500.X500Principal subjectDN = (javax.security.auth.x500.X500Principal) + ((java.security.cert.X509Certificate)info.certs[0]).getSubjectX500Principal(); + String subjectName = subjectDN.getName(javax.security.auth.x500.X500Principal.RFC2253); + assert subjectName != null; + try + { + // + // Decompose the subject DN into the RDNs. + // + if(_traceLevel > 0) + { + if(info.incoming) + { + _communicator.getLogger().trace("Security", "trust manager evaluating client:\n" + + "subject = " + subjectName + "\n" + + "adapter = " + info.adapterName + "\n" + + "local addr = " + IceInternal.Network.addrToString(info.localAddr) + "\n" + + "remote addr = " + IceInternal.Network.addrToString(info.remoteAddr)); + } + else + { + _communicator.getLogger().trace("Security", "trust manager evaluating server:\n" + + "subject = " + subjectName + "\n" + + "local addr = " + IceInternal.Network.addrToString(info.localAddr) + "\n" + + "remote addr = " + IceInternal.Network.addrToString(info.remoteAddr)); + } + } + java.util.List dn = RFC2253.parseStrict(subjectName); + + // + // Try matching against everything in the trust set. + // + java.util.Iterator p = trustset.iterator(); + while(p.hasNext()) + { + java.util.List matchSet = (java.util.List)p.next(); + if(_traceLevel > 1) + { + String s = "trust manager matching PDNs:\n"; + java.util.Iterator q = matchSet.iterator(); + boolean addSemi = false; + while(q.hasNext()) + { + if(addSemi) + { + s += ';'; + } + addSemi = true; + java.util.List rdnSet = (java.util.List)q.next(); + java.util.Iterator r = rdnSet.iterator(); + boolean addComma = false; + while(r.hasNext()) + { + if(addComma) + { + s += ','; + } + addComma = true; + RFC2253.RDNPair rdn = (RFC2253.RDNPair)r.next(); + s += rdn.key; + s += '='; + s += rdn.value; + } + } + _communicator.getLogger().trace("Security", s); + } + if(match(matchSet, dn)) + { + return true; + } + } + } + catch(RFC2253.ParseException e) + { + _communicator.getLogger().warning( + "IceSSL: unable to parse certificate DN `" + subjectName + "'\nreason: " + e.reason); + } + } + + return false; + } + + private boolean + match(java.util.List matchSet, java.util.List subject) + { + java.util.Iterator r = matchSet.iterator(); + while(r.hasNext()) + { + if(matchRDNs((java.util.List)r.next(), subject)) + { + return true; + } + } + return false; + } + + private boolean + matchRDNs(java.util.List match, java.util.List subject) + { + java.util.Iterator p = match.iterator(); + while(p.hasNext()) + { + RFC2253.RDNPair matchRDN = (RFC2253.RDNPair)p.next(); + boolean found = false; + java.util.Iterator q = subject.iterator(); + while(q.hasNext()) + { + RFC2253.RDNPair subjectRDN = (RFC2253.RDNPair)q.next(); + if(matchRDN.key.equals(subjectRDN.key)) + { + found = true; + if(!matchRDN.value.equals(subjectRDN.value)) + { + return false; + } + } + } + if(!found) + { + return false; + } + } + return true; + } + + java.util.List + parse(String value) + throws RFC2253.ParseException + { + // + // Java X500Principal.getName says: + // + // If "RFC2253" is specified as the format, this method emits + // the attribute type keywords defined in RFC 2253 (CN, L, ST, + // O, OU, C, STREET, DC, UID). Any other attribute type is + // emitted as an OID. Under a strict reading, RFC 2253 only + // specifies a UTF-8 string representation. The String + // returned by this method is the Unicode string achieved by + // decoding this UTF-8 representation. + // + // This means that things like emailAddress and such will be turned into + // something like: + // + // 1.2.840.113549.1.9.1=#160e696e666f407a65726f632e636f6d + // + // The left hand side is the OID (see + // http://www.columbia.edu/~ariel/ssleay/asn1-oids.html) for a + // list. The right hand side is a BER encoding of the value. + // + // This means that the user input, unless it uses the + // unfriendly OID format, will not directly match the + // principal. + // + // Two possible solutions: + // + // Have the RFC2253 parser convert anything that is not CN, L, + // ST, O, OU, C, STREET, DC, UID into OID format, and have it + // convert the values into a BER encoding. + // + // Send the user data through X500Principal to string form and + // then through the RFC2253 encoder. This uses the + // X500Principal to do the encoding for us. + // + // The latter is much simpler, however, it means we need to + // send the data through the parser twice because we split the + // DNs on ';' which cannot be blindly split because of quotes, + // \ and such. + // + java.util.List l = RFC2253.parse(value); + java.util.List result = new java.util.LinkedList(); + java.util.Iterator p = l.iterator(); + while(p.hasNext()) + { + java.util.List dn = (java.util.List)p.next(); + String v = new String(); + boolean first = true; + java.util.Iterator q = dn.iterator(); + while(q.hasNext()) + { + if(!first) + { + v += ","; + } + first = false; + RFC2253.RDNPair pair = (RFC2253.RDNPair)q.next(); + v += pair.key; + v += "="; + v += pair.value; + } + javax.security.auth.x500.X500Principal princ = new javax.security.auth.x500.X500Principal(v); + String subjectName = princ.getName(javax.security.auth.x500.X500Principal.RFC2253); + result.add(RFC2253.parseStrict(subjectName)); + } + return result; + } + + private Ice.Communicator _communicator; + private int _traceLevel; + + private java.util.List _all; + private java.util.List _client; + private java.util.List _allServer; + private java.util.Map _server = new java.util.HashMap(); +} diff --git a/java/src/IceSSL/Util.java b/java/src/IceSSL/Util.java new file mode 100644 index 00000000000..fcde66a5db4 --- /dev/null +++ b/java/src/IceSSL/Util.java @@ -0,0 +1,127 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 final class Util +{ + public static ConnectionInfo + getConnectionInfo(Ice.Connection connection) + { + Ice.ConnectionI con = (Ice.ConnectionI)connection; + assert(con != null); + + // + // Lock the connection directly. This is done because the only + // thing that prevents the transceiver from being closed during + // the duration of the invocation is the connection. + // + synchronized(con) + { + IceInternal.Transceiver transceiver = con.getTransceiver(); + if(transceiver == null) + { + ConnectionInvalidException ex = new ConnectionInvalidException(); + ex.reason = "connection closed"; + throw ex; + } + + try + { + TransceiverI sslTransceiver = (TransceiverI)transceiver; + return sslTransceiver.getConnectionInfo(); + } + catch(ClassCastException ex) + { + ConnectionInvalidException e = new ConnectionInvalidException(); + e.reason = "not ssl connection"; + throw e; + } + } + } + + // + // Create a certificate from a PEM-encoded string. + // + public static java.security.cert.X509Certificate + createCertificate(String certPEM) + throws java.security.cert.CertificateException + { + final String header = "-----BEGIN CERTIFICATE-----"; + final String footer = "-----END CERTIFICATE-----"; + + // + // The generateCertificate method requires that its input begin + // with the PEM header. + // + int pos = certPEM.indexOf(header); + if(pos == -1) + { + certPEM = header + "\n" + certPEM; + } + else if(pos > 0) + { + certPEM = certPEM.substring(pos); + } + + // + // Add the footer if necessary. + // + if(certPEM.indexOf(footer) == -1) + { + certPEM = certPEM + footer; + } + + byte[] bytes = null; + try + { + bytes = certPEM.getBytes("UTF8"); + } + catch(java.io.UnsupportedEncodingException ex) + { + assert(false); + return null; + } + + java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(bytes); + java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X.509"); + return (java.security.cert.X509Certificate)cf.generateCertificate(in); + } + + static ConnectionInfo + populateConnectionInfo(javax.net.ssl.SSLSession session, java.net.Socket fd, String adapterName, boolean incoming) + { + ConnectionInfo info = new ConnectionInfo(); + try + { + info.certs = session.getPeerCertificates(); + } + catch(javax.net.ssl.SSLPeerUnverifiedException ex) + { + // No peer certificates. + } + info.cipher = session.getCipherSuite(); + info.localAddr = (java.net.InetSocketAddress)fd.getLocalSocketAddress(); + info.remoteAddr = (java.net.InetSocketAddress)fd.getRemoteSocketAddress(); + info.adapterName = adapterName; + info.incoming = incoming; + return info; + } + + public final static String jdkTarget = "1.5"; + + // + // Needed by the test scripts to determine the JDK target of the SSL plug-in. + // + public static void + main(String[] args) + { + System.out.println(jdkTarget); + } +} diff --git a/java/src/IceSSL/X509KeyManagerI.java b/java/src/IceSSL/X509KeyManagerI.java new file mode 100644 index 00000000000..8b209ee3978 --- /dev/null +++ b/java/src/IceSSL/X509KeyManagerI.java @@ -0,0 +1,70 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 X509KeyManagerI extends javax.net.ssl.X509ExtendedKeyManager +{ + X509KeyManagerI(javax.net.ssl.X509KeyManager del, String alias) + { + _delegate = del; + _alias = alias; + } + + public String + chooseClientAlias(String[] keyType, java.security.Principal[] issuers, java.net.Socket socket) + { + return _alias; + } + + public String + chooseEngineClientAlias(String[] keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) + { + return _alias; + } + + public String + chooseServerAlias(String keyType, java.security.Principal[] issuers, java.net.Socket socket) + { + return _alias; + } + + public String + chooseEngineServerAlias(String[] keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) + { + return _alias; + } + + 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; +} diff --git a/java/src/IceSSL/X509TrustManagerI.java b/java/src/IceSSL/X509TrustManagerI.java new file mode 100644 index 00000000000..325287daec1 --- /dev/null +++ b/java/src/IceSSL/X509TrustManagerI.java @@ -0,0 +1,50 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 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 X509TrustManagerI implements javax.net.ssl.X509TrustManager +{ + X509TrustManagerI(javax.net.ssl.X509TrustManager delegate) + { + _delegate = delegate; + } + + public void + checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) + throws java.security.cert.CertificateException + { + if(!authType.equals("DH_anon") && _delegate != null) + { + _delegate.checkClientTrusted(chain, authType); + } + } + + public void + checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) + throws java.security.cert.CertificateException + { + if(!authType.equals("DH_anon") && _delegate != null) + { + _delegate.checkServerTrusted(chain, authType); + } + } + + public java.security.cert.X509Certificate[] + getAcceptedIssuers() + { + if(_delegate != null) + { + return _delegate.getAcceptedIssuers(); + } + return null; + } + + private javax.net.ssl.X509TrustManager _delegate; +} |