summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaspar De Groot2011-04-07 00:22:30 (EDT)
committerCaspar De Groot2011-04-07 00:22:30 (EDT)
commit889079ab082a434927b72a5a176931a4a633c0de (patch)
tree6dd5ac1044821fdb30dc0d193c916cdeabfedbc0
parentd41475ec4a284047a3d3f160a8084b5c0934f8bc (diff)
downloadcdo-889079ab082a434927b72a5a176931a4a633c0de.zip
cdo-889079ab082a434927b72a5a176931a4a633c0de.tar.gz
cdo-889079ab082a434927b72a5a176931a4a633c0de.tar.bz2
[Bug 340108] SSL transport implementation for Net4J
https://bugs.eclipse.org/bugs/show_bug.cgi?id=340108
-rw-r--r--plugins/org.eclipse.emf.cdo.examples/sslKey/testKeysbin0 -> 1323 bytes
-rw-r--r--plugins/org.eclipse.emf.cdo.examples/sslKey/testTrustbin0 -> 621 bytes
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/sslKey/testKeysbin0 -> 1323 bytes
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/sslKey/testTrustbin0 -> 621 bytes
-rw-r--r--plugins/org.eclipse.net4j.examples/sslKey/testKeysbin0 -> 1323 bytes
-rw-r--r--plugins/org.eclipse.net4j.examples/sslKey/testTrustbin0 -> 621 bytes
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptor.java37
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptorFactory.java36
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBuffer.java205
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBufferFactory.java44
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLClientConnector.java97
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnector.java231
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnectorFactory.java36
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLEngineManager.java490
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLProperties.java139
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLServerConnector.java100
-rw-r--r--plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/tcp/ssl/SSLUtil.java301
-rw-r--r--plugins/org.eclipse.net4j.tests/sslKey/testKeysbin0 -> 1323 bytes
-rw-r--r--plugins/org.eclipse.net4j.tests/sslKey/testTrustbin0 -> 621 bytes
19 files changed, 1716 insertions, 0 deletions
diff --git a/plugins/org.eclipse.emf.cdo.examples/sslKey/testKeys b/plugins/org.eclipse.emf.cdo.examples/sslKey/testKeys
new file mode 100644
index 0000000..ecbf5f2
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.examples/sslKey/testKeys
Binary files differ
diff --git a/plugins/org.eclipse.emf.cdo.examples/sslKey/testTrust b/plugins/org.eclipse.emf.cdo.examples/sslKey/testTrust
new file mode 100644
index 0000000..9a14541
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.examples/sslKey/testTrust
Binary files differ
diff --git a/plugins/org.eclipse.emf.cdo.tests/sslKey/testKeys b/plugins/org.eclipse.emf.cdo.tests/sslKey/testKeys
new file mode 100644
index 0000000..ecbf5f2
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/sslKey/testKeys
Binary files differ
diff --git a/plugins/org.eclipse.emf.cdo.tests/sslKey/testTrust b/plugins/org.eclipse.emf.cdo.tests/sslKey/testTrust
new file mode 100644
index 0000000..9a14541
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/sslKey/testTrust
Binary files differ
diff --git a/plugins/org.eclipse.net4j.examples/sslKey/testKeys b/plugins/org.eclipse.net4j.examples/sslKey/testKeys
new file mode 100644
index 0000000..ecbf5f2
--- /dev/null
+++ b/plugins/org.eclipse.net4j.examples/sslKey/testKeys
Binary files differ
diff --git a/plugins/org.eclipse.net4j.examples/sslKey/testTrust b/plugins/org.eclipse.net4j.examples/sslKey/testTrust
new file mode 100644
index 0000000..9a14541
--- /dev/null
+++ b/plugins/org.eclipse.net4j.examples/sslKey/testTrust
Binary files differ
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptor.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptor.java
new file mode 100644
index 0000000..c2a9fb2
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptor.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.TCPAcceptor;
+import org.eclipse.net4j.internal.tcp.TCPConnector;
+
+import java.text.MessageFormat;
+
+/**
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLAcceptor extends TCPAcceptor
+{
+ @Override
+ public String toString()
+ {
+ return MessageFormat.format("SSLAcceptor[{0}:{1}]", getAddress(), getPort()); //$NON-NLS-1$
+ }
+
+ @Override
+ protected TCPConnector createConnector()
+ {
+ return new SSLServerConnector(this);
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptorFactory.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptorFactory.java
new file mode 100644
index 0000000..48116e6
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLAcceptorFactory.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.TCPAcceptor;
+import org.eclipse.net4j.internal.tcp.TCPAcceptorFactory;
+
+/**
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLAcceptorFactory extends TCPAcceptorFactory
+{
+ public static final String TYPE = "ssl"; //$NON-NLS-1$
+
+ public SSLAcceptorFactory()
+ {
+ super(TYPE);
+ }
+
+ @Override
+ protected TCPAcceptor createAcceptor()
+ {
+ return new SSLAcceptor();
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBuffer.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBuffer.java
new file mode 100644
index 0000000..9d0c32b
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBuffer.java
@@ -0,0 +1,205 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.buffer.BufferState;
+import org.eclipse.net4j.buffer.IBuffer;
+import org.eclipse.net4j.buffer.IBufferProvider;
+import org.eclipse.net4j.internal.tcp.bundle.OM;
+import org.eclipse.net4j.util.StringUtil;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.internal.net4j.buffer.Buffer;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SocketChannel;
+
+/**
+ * All source code same as org.eclipse.internal.net4j.buffer.Buffer except adding SSLEngineManager to constructor and
+ * overriding startGetting and write method in order to attach the SSL functional.
+ *
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLBuffer extends Buffer
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SSLBUFFER, SSLBuffer.class);
+
+ private SSLEngineManager sslEngineManager;
+
+ public SSLBuffer(IBufferProvider provider, short capacity, SSLEngineManager sslEngineManager)
+ {
+ super(provider, capacity);
+ this.sslEngineManager = sslEngineManager;
+ }
+
+ @Override
+ public ByteBuffer startGetting(SocketChannel socketChannel) throws IOException
+ {
+ BufferState state = getState();
+ if (state != BufferState.INITIAL && state != BufferState.READING_HEADER && state != BufferState.READING_BODY)
+ {
+ throw new IllegalStateException(toString());
+ }
+
+ int readSize = 0;
+
+ if (sslEngineManager.getAppRecvBuf().position() > 0)
+ {
+ readSize = sslEngineManager.getAppRecvBuf().position();
+ }
+ else
+ {
+ readSize = sslEngineManager.read(socketChannel);
+ }
+
+ if (readSize > 0)
+ {
+ ByteBuffer buf = sslEngineManager.getAppRecvBuf();
+ buf.flip();
+
+ int limit = buf.limit();
+ ByteBuffer byteBuffer = getByteBuffer();
+
+ int capacity = byteBuffer.capacity();
+ limit = limit > capacity ? capacity : limit;
+
+ byteBuffer.put(buf.array(), 0, limit);
+ buf.position(limit);
+ buf.compact();
+ byteBuffer.flip();
+
+ setChannelID(byteBuffer.getShort());
+ short payloadSize = byteBuffer.getShort();
+
+ if (payloadSize < 0)
+ {
+ setEOS(true);
+ payloadSize = (short)-payloadSize;
+ }
+
+ payloadSize -= EOS_OFFSET;
+
+ byteBuffer.position(IBuffer.HEADER_SIZE);
+ setState(BufferState.READING_HEADER);
+
+ byteBuffer.compact();
+ byteBuffer.limit(payloadSize);
+ setState(BufferState.READING_BODY);
+
+ byteBuffer.flip();
+ setState(BufferState.GETTING);
+
+ return byteBuffer;
+ }
+ else if (readSize < 0)
+ {
+ throw new ClosedChannelException();
+ }
+
+ return null;
+ }
+
+ /**
+ * @return <code>true</code> if the buffer has been completely written, <code>false</code> otherwise.
+ */
+ @Override
+ public boolean write(SocketChannel socketChannel) throws IOException
+ {
+ try
+ {
+
+ if (sslEngineManager.getPacketSendBuf().position() > 0)
+ {
+ sslEngineManager.handleWrite(socketChannel);
+
+ if (sslEngineManager.getPacketSendBuf().position() > 0)
+ {
+ clear();
+ return false;
+ }
+ clear();
+ return true;
+ }
+
+ BufferState state = getState();
+ if (state != BufferState.PUTTING && state != BufferState.WRITING)
+ {
+ throw new IllegalStateException(toString());
+ }
+
+ ByteBuffer byteBuffer = getByteBuffer();
+ if (state == BufferState.PUTTING)
+ {
+ if (getChannelID() == NO_CHANNEL)
+ {
+ throw new IllegalStateException("channelID == NO_CHANNEL"); //$NON-NLS-1$
+ }
+
+ int payloadSize = byteBuffer.position() - IBuffer.HEADER_SIZE + EOS_OFFSET;
+ if (isEOS())
+ {
+ payloadSize = -payloadSize;
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Writing " + (Math.abs(payloadSize) - 1) + " bytes" //$NON-NLS-1$ //$NON-NLS-2$
+ + (isEOS() ? " (EOS)" : "") + StringUtil.NL + formatContent(false)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ byteBuffer.flip();
+ byteBuffer.putShort(getChannelID());
+ byteBuffer.putShort((short)payloadSize);
+ byteBuffer.position(0);
+ setState(BufferState.WRITING);
+ }
+
+ sslEngineManager.getAppSendBuf().put(byteBuffer);
+ sslEngineManager.write(socketChannel);
+
+ if (sslEngineManager.getPacketSendBuf().position() > 0)
+ {
+ clear();
+ return false;
+ }
+
+ clear();
+ return true;
+ }
+ catch (IOException ex)
+ {
+ handleError(ex);
+ throw ex;
+ }
+ catch (RuntimeException ex)
+ {
+ handleError(ex);
+ throw ex;
+ }
+ catch (Error ex)
+ {
+ handleError(ex);
+ throw ex;
+ }
+ }
+
+ @Override
+ public void dispose()
+ {
+ sslEngineManager = null;
+ super.dispose();
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBufferFactory.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBufferFactory.java
new file mode 100644
index 0000000..f76f280
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLBufferFactory.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.Net4jUtil;
+import org.eclipse.net4j.buffer.IBuffer;
+
+import org.eclipse.internal.net4j.buffer.BufferFactory;
+
+/**
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLBufferFactory extends BufferFactory
+{
+ private SSLEngineManager sslEngineManager;
+
+ public SSLBufferFactory(short bufferCapacity)
+ {
+ super(bufferCapacity);
+ }
+
+ public SSLBufferFactory(SSLEngineManager sslEngineManager)
+ {
+ this(Net4jUtil.DEFAULT_BUFFER_CAPACITY);
+ this.sslEngineManager = sslEngineManager;
+ }
+
+ @Override
+ protected IBuffer doProvideBuffer()
+ {
+ return new SSLBuffer(this, getBufferCapacity(), sslEngineManager);
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLClientConnector.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLClientConnector.java
new file mode 100644
index 0000000..d1614cc
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLClientConnector.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.bundle.OM;
+import org.eclipse.net4j.tcp.ITCPSelector;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.channels.SocketChannel;
+import java.text.MessageFormat;
+
+/**
+ * SSLClientConnector responses to be connector on client side and it handles about connecting to the server connector.
+ *
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLClientConnector extends SSLConnector
+{
+ public SSLClientConnector()
+ {
+ try
+ {
+ SocketChannel socketChannel = SocketChannel.open();
+ socketChannel.socket().setReuseAddress(true);
+ // socketChannel.socket().setKeepAlive(true);
+ socketChannel.configureBlocking(false);
+ setSocketChannel(socketChannel);
+ }
+ catch (IOException ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+
+ @Override
+ public Location getLocation()
+ {
+ return Location.CLIENT;
+ }
+
+ @Override
+ public String toString()
+ {
+ if (getUserID() == null)
+ {
+ return MessageFormat.format("SSLClientConnector[{0}:{1}]", getHost(), getPort()); //$NON-NLS-1$
+ }
+
+ return MessageFormat.format("SSLClientConnector[{2}@{0}:{1}]", getHost(), getPort(), getUserID()); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void doBeforeActivate() throws Exception
+ {
+ super.doBeforeActivate();
+ if (getHost() == null)
+ {
+ throw new IllegalStateException("host == null"); //$NON-NLS-1$
+ }
+
+ if (getPort() == 0)
+ {
+ throw new IllegalStateException("port == 0"); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public void handleRegistration(ITCPSelector selector, SocketChannel socketChannel)
+ {
+ super.handleRegistration(selector, socketChannel);
+
+ try
+ {
+ InetAddress addr = InetAddress.getByName(getHost());
+ InetSocketAddress sAddr = new InetSocketAddress(addr, getPort());
+ getSocketChannel().connect(sAddr);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ deactivateAsync();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnector.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnector.java
new file mode 100644
index 0000000..431258a
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnector.java
@@ -0,0 +1,231 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.TCPConnector;
+import org.eclipse.net4j.internal.tcp.bundle.OM;
+import org.eclipse.net4j.tcp.ITCPSelector;
+import org.eclipse.net4j.tcp.ssl.SSLUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+
+/**
+ * SSLConnector responses to perform tasks same as TCPConnector but it attached the SSL functionality into read and
+ * write method.
+ *
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLConnector extends TCPConnector
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, SSLConnector.class);
+
+ private SSLEngineManager sslEngineManager;
+
+ @Override
+ public String getProtocolString()
+ {
+ return "ssl://";
+ }
+
+ @Override
+ public void handleConnect(ITCPSelector selector, SocketChannel channel)
+ {
+ super.handleConnect(selector, channel);
+
+ if (!sslEngineManager.isHandshakeComplete() && isClient())
+ {
+ getConfig().getReceiveExecutor().execute(createHandShakeTask(channel));
+ }
+ }
+
+ @Override
+ public void handleRegistration(ITCPSelector selector, SocketChannel socketChannel)
+ {
+ super.handleRegistration(selector, socketChannel);
+
+ if (!sslEngineManager.isHandshakeComplete() && isServer())
+ {
+ getConfig().getReceiveExecutor().execute(createHandShakeTask(socketChannel));
+ }
+ }
+
+ @Override
+ public void handleRead(ITCPSelector selector, SocketChannel socketChannel)
+ {
+ waitForHandShakeFinish();
+ super.handleRead(selector, socketChannel);
+ checkRehandShake(socketChannel);
+
+ // handle the left data from reading multiple data at once time.
+ while (sslEngineManager.getPacketRecvBuf().position() > 0)
+ {
+ super.handleRead(selector, socketChannel);
+ checkRehandShake(socketChannel);
+ }
+ }
+
+ @Override
+ public void handleWrite(ITCPSelector selector, SocketChannel socketChannel)
+ {
+ waitForHandShakeFinish();
+ super.handleWrite(selector, socketChannel);
+ checkRehandShake(socketChannel);
+ }
+
+ @Override
+ protected void doActivate() throws Exception
+ {
+ try
+ {
+ boolean isClient = isClient();
+
+ String host = getHost();
+ int port = getPort();
+ sslEngineManager = new SSLEngineManager(isClient, host, port, getConfig().getReceiveExecutor());
+
+ // Set the buffer provider of the config instance in order to replace
+ // BufferFactory instance with SSLBufferFactory instance.
+ getConfig().setBufferProvider(new SSLBufferFactory(sslEngineManager));
+ }
+ catch (Exception ex)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Cannot activate the ssl engine.", ex); //$NON-NLS-1$
+ }
+
+ throw ex;
+ }
+
+ super.doActivate();
+ }
+
+ @Override
+ protected void doDeactivate() throws Exception
+ {
+ try
+ {
+ sslEngineManager.close();
+ }
+ catch (Exception ex)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Cannot deactivate the ssl engine.", ex); //$NON-NLS-1$
+ }
+ }
+ finally
+ {
+ super.doDeactivate();
+ }
+ }
+
+ /**
+ * Toggles between OP_READ and OP_WRITE
+ * <p>
+ * (Having both OP_READ and OP_WRITE interests on a socketChannel is not a good idea when the channel is used for
+ * TLS/SSL communications.)
+ */
+ @Override
+ protected void doOrderWriteInterest(boolean on)
+ {
+ ITCPSelector selector = getSelector();
+ SelectionKey selectionKey = getSelectionKey();
+
+ if (on)
+ {
+ selector.orderReadInterest(selectionKey, isClient(), false);
+ selector.orderWriteInterest(selectionKey, isClient(), true);
+ }
+ else
+ {
+ // Note: order is different from above!
+ selector.orderWriteInterest(selectionKey, isClient(), false);
+ selector.orderReadInterest(selectionKey, isClient(), true);
+ }
+ }
+
+ private void checkRehandShake(SocketChannel socketChannel)
+ {
+ if (!isClosed())
+ {
+ try
+ {
+ sslEngineManager.checkRehandShake(socketChannel);
+ }
+ catch (Exception ex)
+ {
+ deactivateAsync();
+ }
+ }
+ }
+
+ private void waitForHandShakeFinish()
+ {
+ // wait until handshake finished. if handshake finish, it will not enter this loop.
+ while (!sslEngineManager.isHandshakeComplete())
+ {
+ if (isNegotiating())
+ {
+ try
+ {
+ Thread.sleep(SSLUtil.getHandShakeWaitTime());
+ }
+ catch (InterruptedException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+ }
+ else
+ {
+ Thread.yield();
+ }
+
+ // prevent sleeping forever.
+ if (!isActive())
+ {
+ break;
+ }
+ }
+ }
+
+ private Runnable createHandShakeTask(SocketChannel channel)
+ {
+ final SocketChannel socket = channel;
+ Runnable task = new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ sslEngineManager.checkInitialHandshake(socket);
+ }
+ catch (Exception ex)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("ssl cannot handshake.", ex); //$NON-NLS-1$
+ }
+
+ deferredActivate(false);
+ }
+ }
+ };
+
+ return task;
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnectorFactory.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnectorFactory.java
new file mode 100644
index 0000000..886df84
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLConnectorFactory.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.TCPConnector;
+import org.eclipse.net4j.internal.tcp.TCPConnectorFactory;
+
+/**
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLConnectorFactory extends TCPConnectorFactory
+{
+ public static final String TYPE = "ssl"; //$NON-NLS-1$
+
+ public SSLConnectorFactory()
+ {
+ super(TYPE);
+ }
+
+ @Override
+ protected TCPConnector createConnector()
+ {
+ return new SSLClientConnector();
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLEngineManager.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLEngineManager.java
new file mode 100644
index 0000000..0d86fb2
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLEngineManager.java
@@ -0,0 +1,490 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.bundle.OM;
+import org.eclipse.net4j.tcp.ssl.SSLUtil;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SocketChannel;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.concurrent.Executor;
+
+/**
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLEngineManager
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, SSLEngineManager.class);
+
+ /**
+ * An optional executor to be used this class.
+ * <p>
+ */
+ private Executor executor;
+
+ private SSLEngine sslEngine;
+
+ private boolean handshakeComplete;
+
+ private boolean needRehandShake;
+
+ private ByteBuffer appSendBuf;
+
+ private ByteBuffer appRecvBuf;
+
+ private ByteBuffer packetSendBuf;
+
+ private ByteBuffer packetRecvBuf;
+
+ private SSLEngineResult engineResult;
+
+ private SSLEngineResult.HandshakeStatus handshakeStatus;
+
+ private Object readLock = new ReadLock();
+
+ private Object writeLock = new WriteLock();
+
+ public SSLEngineManager(boolean client, String host, int port, Executor executor) throws NoSuchAlgorithmException,
+ KeyStoreException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException,
+ KeyManagementException
+ {
+ this.executor = executor;
+
+ sslEngine = SSLUtil.createSSLEngine(client, host, port);
+ sslEngine.beginHandshake();
+
+ SSLSession session = sslEngine.getSession();
+
+ int applicationBufferSize = session.getApplicationBufferSize();
+ appSendBuf = ByteBuffer.allocate(applicationBufferSize);
+ appRecvBuf = ByteBuffer.allocate(applicationBufferSize);
+
+ int packetBufferSize = session.getPacketBufferSize();
+ packetSendBuf = ByteBuffer.allocate(packetBufferSize);
+ packetRecvBuf = ByteBuffer.allocate(packetBufferSize);
+ }
+
+ public Executor getExecutor()
+ {
+ return executor;
+ }
+
+ public ByteBuffer getAppSendBuf()
+ {
+ return appSendBuf;
+ }
+
+ public void setAppSendBuf(ByteBuffer appSendBuf)
+ {
+ this.appSendBuf = appSendBuf;
+ }
+
+ public ByteBuffer getAppRecvBuf()
+ {
+ return appRecvBuf;
+ }
+
+ public void setAppRecvBuf(ByteBuffer appRecvBuf)
+ {
+ this.appRecvBuf = appRecvBuf;
+ }
+
+ public ByteBuffer getPacketSendBuf()
+ {
+ return packetSendBuf;
+ }
+
+ public void setPacketSendBuf(ByteBuffer packetSendBuf)
+ {
+ this.packetSendBuf = packetSendBuf;
+ }
+
+ public ByteBuffer getPacketRecvBuf()
+ {
+ return packetRecvBuf;
+ }
+
+ public void setPacketRecvBuf(ByteBuffer packetRecvBuf)
+ {
+ this.packetRecvBuf = packetRecvBuf;
+ }
+
+ public synchronized void checkInitialHandshake(SocketChannel socketChannel) throws Exception
+ {
+ if (!handshakeComplete)
+ {
+ try
+ {
+ int counter = 0;
+ while (!isHandshakeFinished() && counter <= SSLUtil.getHandShakeTimeOut())
+ {
+ performHandshake(socketChannel, needRehandShake);
+ counter = handshakeStatus == HandshakeStatus.NEED_UNWRAP ? counter++ : 0;
+ }
+
+ if (!isHandshakeFinished() && counter == SSLUtil.getHandShakeTimeOut())
+ {
+ throw new SSLException("SSL handshake timeout");
+ }
+ }
+ catch (Exception ex)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("SSL handshake incomplete.", ex); //$NON-NLS-1$
+ }
+
+ try
+ {
+ // Clean the SSLEngine.
+ close();
+ }
+ catch (IOException ioex)
+ {
+ OM.LOG.warn(ioex);
+ }
+
+ throw ex;
+ }
+
+ handshakeComplete = true;
+ }
+ }
+
+ public int read(SocketChannel socketChannel) throws IOException
+ {
+ if (!handshakeComplete)
+ {
+ throw new SSLException("Handshake still not completed");
+ }
+
+ synchronized (readLock)
+ {
+ int readTextCount = appRecvBuf.position();
+
+ do
+ {
+ if (sslEngine.isInboundDone())
+ {
+ return readTextCount > 0 ? readTextCount : -1;
+ }
+
+ int count = handleRead(socketChannel);
+ if (count <= 0 && packetRecvBuf.position() == 0)
+ {
+ return count;
+ }
+
+ packetRecvBuf.flip();
+ engineResult = sslEngine.unwrap(packetRecvBuf, appRecvBuf);
+ packetRecvBuf.compact();
+
+ switch (engineResult.getStatus())
+ {
+ case BUFFER_UNDERFLOW:
+ continue;
+
+ case BUFFER_OVERFLOW:
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Buffer overflow on read method."); //$NON-NLS-1$
+ }
+
+ return 0;
+
+ case CLOSED:
+ throw new ClosedChannelException();
+
+ default:
+ // OK
+ }
+
+ readTextCount = appRecvBuf.position();
+ } while (engineResult.getStatus() != Status.OK);
+
+ if (sslEngine.isInboundDone())
+ {
+ return -1;
+ }
+
+ return readTextCount;
+ }
+ }
+
+ public int write(SocketChannel socketChannel) throws IOException
+ {
+ if (!handshakeComplete)
+ {
+ throw new SSLException("Handshake still not completed");
+ }
+
+ synchronized (writeLock)
+ {
+ int writeCount = 0;
+ int count = 0;
+ int appSendBufSize = appSendBuf.position();
+
+ do
+ {
+ appSendBuf.flip();
+ engineResult = sslEngine.wrap(appSendBuf, packetSendBuf);
+ appSendBuf.compact();
+ switch (engineResult.getStatus())
+ {
+ case BUFFER_UNDERFLOW:
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Buffer Underflow happen on write method"); //$NON-NLS-1$
+ }
+
+ return -1;
+
+ case BUFFER_OVERFLOW:
+ count = handleWrite(socketChannel);
+ writeCount += count;
+ continue;
+
+ case CLOSED:
+ throw new ClosedChannelException();
+
+ case OK:
+ int bytesComsumed = engineResult.bytesConsumed();
+ appSendBufSize = appSendBufSize - bytesComsumed;
+ count = handleWrite(socketChannel);
+ writeCount += count;
+ break;
+ }
+ } while (engineResult.getStatus() != Status.OK);
+
+ return writeCount;
+ }
+ }
+
+ /**
+ * Write the contents of {@link #packetSendBuf} to the given {@link SocketChannel}
+ *
+ * @return the number of bytes actually written
+ */
+ public int handleWrite(SocketChannel socketChannel) throws IOException
+ {
+ try
+ {
+ packetSendBuf.flip();
+ int count = socketChannel.write(packetSendBuf);
+ packetSendBuf.compact();
+
+ if (count == -1)
+ {
+ throw new ClosedChannelException();
+ }
+
+ return count;
+ }
+ catch (ClosedChannelException ex)
+ {
+ throw ex;
+ }
+ catch (IOException ex)
+ {
+ throw new ClosedChannelException();
+ }
+ }
+
+ /**
+ * Read the contents from the given {@link SocketChannel} to {@link #packetSendBuf}
+ *
+ * @return the number of bytes actually read
+ */
+ public int handleRead(SocketChannel socketChannel) throws IOException
+ {
+ try
+ {
+ int count = socketChannel.read(packetRecvBuf);
+ if (count == -1)
+ {
+ throw new ClosedChannelException();
+ }
+
+ return count;
+ }
+ catch (ClosedChannelException ex)
+ {
+ throw ex;
+ }
+ catch (IOException ex)
+ {
+ throw new ClosedChannelException();
+ }
+ }
+
+ public void close() throws IOException
+ {
+ if (sslEngine != null)
+ {
+ sslEngine.closeOutbound();
+ }
+ }
+
+ public void checkRehandShake(SocketChannel socket) throws Exception
+ {
+ handshakeStatus = engineResult.getHandshakeStatus();
+ needRehandShake = handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
+ || handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP
+ || handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP;
+
+ if (needRehandShake)
+ {
+ handshakeComplete = false;
+ }
+
+ checkInitialHandshake(socket);
+ return;
+ }
+
+ public boolean isHandshakeComplete()
+ {
+ return handshakeComplete;
+ }
+
+ private boolean isHandshakeFinished()
+ {
+ return handshakeStatus != null && handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED;
+ }
+
+ private boolean performHandshake(SocketChannel socketChannel, boolean rehandShake) throws IOException
+ {
+ int nBytes = 0;
+ if (!rehandShake)
+ {
+ handshakeStatus = sslEngine.getHandshakeStatus();
+ }
+
+ switch (handshakeStatus)
+ {
+ case NEED_WRAP:
+ appSendBuf.flip();
+ engineResult = sslEngine.wrap(appSendBuf, packetSendBuf);
+ handshakeStatus = engineResult.getHandshakeStatus();
+ appSendBuf.compact();
+
+ switch (engineResult.getStatus())
+ {
+ case BUFFER_OVERFLOW:
+ nBytes = handleWrite(socketChannel);
+ break;
+
+ case OK:
+ while (packetSendBuf.position() > 0)
+ {
+ nBytes = handleWrite(socketChannel);
+ if (nBytes == 0)
+ {
+ // Prevent spinning if the channel refused the write
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Need Wrap Operation: cannot handle ssl result status [" + engineResult.getStatus() + "]"); //$NON-NLS-1$
+ }
+ }
+
+ return true;
+
+ case NEED_UNWRAP:
+ nBytes = handleRead(socketChannel);
+
+ packetRecvBuf.flip();
+ engineResult = sslEngine.unwrap(packetRecvBuf, appRecvBuf);
+ handshakeStatus = engineResult.getHandshakeStatus();
+ packetRecvBuf.compact();
+
+ if (engineResult.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW && sslEngine.isInboundDone())
+ {
+ return false;
+ }
+
+ return engineResult.getStatus() != SSLEngineResult.Status.BUFFER_OVERFLOW;
+
+ case NEED_TASK:
+ executeTasks();
+ return true;
+
+ case NOT_HANDSHAKING:
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Not handshaking status occurs."); //$NON-NLS-1$
+ }
+
+ throw new ClosedChannelException();
+
+ case FINISHED:
+ return false;
+ }
+
+ return true;
+ }
+
+ private void executeTasks()
+ {
+ Runnable task;
+ while ((task = sslEngine.getDelegatedTask()) != null)
+ {
+ executor.execute(task);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Scheduled task: " + task); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class ReadLock
+ {
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class WriteLock
+ {
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLProperties.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLProperties.java
new file mode 100644
index 0000000..3005da5
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLProperties.java
@@ -0,0 +1,139 @@
+/**
+ * Copyright (c) 2004 - 2010 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.bundle.OM;
+import org.eclipse.net4j.util.om.OMPlatform;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
+
+/**
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ */
+public class SSLProperties
+{
+ public static final String KEY_PATH = "org.eclipse.net4j.tcp.ssl.key";
+
+ public static final String TRUST_PATH = "org.eclipse.net4j.tcp.ssl.trust";
+
+ public static final String PASS_PHRASE = "org.eclipse.net4j.tcp.ssl.passphrase";
+
+ public static final String HANDSHAKE_TIMEOUT = "org.eclipse.net4j.tcp.ssl.handshake.timeout";
+
+ public static final String HANDSHAKE_WAITTIME = "org.eclipse.net4j.tcp.ssl.handshake.waittime";
+
+ private Properties localProperties;
+
+ public SSLProperties()
+ {
+ }
+
+ public void load(String localConfigPath) throws IOException
+ {
+ // Loading SSL config from local property file is optional.
+ localProperties = new Properties();
+ File localConfigFile = null;
+
+ try
+ {
+ localConfigFile = new File(new URL(localConfigPath).toURI());
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.info(ex.getMessage() + ",so try to load with the normal path", ex);
+
+ localConfigFile = new File(localConfigPath);
+ }
+
+ if (localConfigFile.exists())
+ {
+ FileInputStream localPropInputStream = null;
+ try
+ {
+ localPropInputStream = new FileInputStream(localConfigFile);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.info("SSL config file cannot be loaded.");
+ }
+ finally
+ {
+ if (localPropInputStream != null)
+ {
+ localProperties.load(localPropInputStream);
+ localPropInputStream.close();
+ }
+ }
+ }
+ }
+
+ public String getKeyPath()
+ {
+ String keyPath = OMPlatform.INSTANCE.getProperty(KEY_PATH);
+ if (keyPath == null && localProperties != null)
+ {
+ keyPath = localProperties.getProperty(KEY_PATH);
+ }
+
+ return keyPath;
+
+ }
+
+ public String getTrustPath()
+ {
+ String trustPath = OMPlatform.INSTANCE.getProperty(TRUST_PATH);
+ if (trustPath == null && localProperties != null)
+ {
+ trustPath = localProperties.getProperty(TRUST_PATH);
+ }
+
+ return trustPath;
+ }
+
+ public String getPassPhrase()
+ {
+ String passPhrase = OMPlatform.INSTANCE.getProperty(PASS_PHRASE);
+ if (passPhrase == null && localProperties != null)
+ {
+ passPhrase = localProperties.getProperty(PASS_PHRASE);
+ }
+
+ return passPhrase;
+ }
+
+ public String getHandShakeTimeOut()
+ {
+ String hsTimeOut = OMPlatform.INSTANCE.getProperty(HANDSHAKE_TIMEOUT);
+ if (hsTimeOut == null && localProperties != null)
+ {
+ hsTimeOut = localProperties.getProperty(HANDSHAKE_TIMEOUT);
+ }
+
+ return hsTimeOut;
+ }
+
+ public String getHandShakeWaitTime()
+ {
+ String waitTime = OMPlatform.INSTANCE.getProperty(HANDSHAKE_WAITTIME);
+ if (waitTime == null && localProperties != null)
+ {
+ waitTime = localProperties.getProperty(HANDSHAKE_WAITTIME);
+ }
+
+ return waitTime;
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLServerConnector.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLServerConnector.java
new file mode 100644
index 0000000..8cb7c91
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/internal/tcp/ssl/SSLServerConnector.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.internal.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.bundle.OM;
+import org.eclipse.net4j.tcp.ITCPSelector;
+
+import java.nio.channels.SocketChannel;
+import java.text.MessageFormat;
+
+/**
+ * SSLServerConnector responses to be connector on server side and it handles about registration from the
+ * SSLClientConnector.
+ *
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLServerConnector extends SSLConnector
+{
+ private SSLAcceptor acceptor;
+
+ public SSLServerConnector(SSLAcceptor acceptor)
+ {
+ this.acceptor = acceptor;
+ }
+
+ public SSLAcceptor getAcceptor()
+ {
+ return acceptor;
+ }
+
+ @Override
+ public Location getLocation()
+ {
+ return Location.SERVER;
+ }
+
+ @Override
+ public String getHost()
+ {
+ try
+ {
+ return getSocketChannel().socket().getInetAddress().getHostAddress();
+ }
+ catch (RuntimeException ex)
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public int getPort()
+ {
+ try
+ {
+ return getSocketChannel().socket().getPort();
+ }
+ catch (RuntimeException ex)
+ {
+ return 0;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ if (getUserID() == null)
+ {
+ return MessageFormat.format("SSLServerConnector[{0}:{1}]", getHost(), getPort()); //$NON-NLS-1$
+ }
+
+ return MessageFormat.format("SSLServerConnector[{2}@{0}:{1}]", getHost(), getPort(), getUserID()); //$NON-NLS-1$
+ }
+
+ @Override
+ public void handleRegistration(ITCPSelector selector, SocketChannel socketChannel)
+ {
+ super.handleRegistration(selector, socketChannel);
+
+ try
+ {
+ acceptor.addConnector(this);
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ deactivateAsync();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/tcp/ssl/SSLUtil.java b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/tcp/ssl/SSLUtil.java
new file mode 100644
index 0000000..24c2ad8
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tcp/src/org/eclipse/net4j/tcp/ssl/SSLUtil.java
@@ -0,0 +1,301 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Teerawat Chaiyakijpichet (No Magic Asia Ltd.) - initial API and implementation
+ * Caspar De Groot (No Magic Asia Ltd.) - initial API and implementation
+ */
+package org.eclipse.net4j.tcp.ssl;
+
+import org.eclipse.net4j.internal.tcp.TCPAcceptorFactory;
+import org.eclipse.net4j.internal.tcp.TCPConnectorFactory;
+import org.eclipse.net4j.internal.tcp.bundle.OM;
+import org.eclipse.net4j.internal.tcp.ssl.SSLAcceptorFactory;
+import org.eclipse.net4j.internal.tcp.ssl.SSLConnectorFactory;
+import org.eclipse.net4j.internal.tcp.ssl.SSLProperties;
+import org.eclipse.net4j.tcp.ITCPAcceptor;
+import org.eclipse.net4j.tcp.ITCPConnector;
+import org.eclipse.net4j.tcp.TCPUtil;
+import org.eclipse.net4j.util.container.IManagedContainer;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+/**
+ * @author Teerawat Chaiyakijpichet (No Magic Asia Ltd.)
+ * @author Caspar De Groot (No Magic Asia Ltd.)
+ * @since 4.0
+ */
+public class SSLUtil
+{
+ // The variable for SSL Engine
+ private static final String protocol = "TLS";
+
+ private static String configFile;
+
+ private static String keyPathVar;
+
+ private static String trustPathVar;
+
+ private static String passPhraseVar;
+
+ // default value of handshake timeout is 12 times.
+ private static int handShakeTimeOutVar = 12;
+
+ // default value of handshake wait time is 60 milliseconds.
+ private static int handShakeWaitTimeVar = 60;
+
+ public static synchronized void setDefaultSSLConfiguration(String keyPath, String trustPath, String passPhrase)
+ {
+ keyPathVar = keyPath;
+ trustPathVar = trustPath;
+ passPhraseVar = passPhrase;
+ }
+
+ public static synchronized void setDefaultSSLConfiguration(String keyPath, String trustPath, String passPhrase,
+ int handShakeTimeOut, int handShakeWaitTime)
+ {
+ setDefaultSSLConfiguration(keyPath, trustPath, passPhrase);
+
+ handShakeTimeOutVar = handShakeTimeOut;
+ handShakeWaitTimeVar = handShakeWaitTime;
+ }
+
+ public static synchronized void setSSLConfigurationFile(String file)
+ {
+ configFile = file;
+ }
+
+ public static synchronized void prepareContainer(IManagedContainer container)
+ {
+ TCPUtil.prepareContainer(container);
+
+ // prepare SSL
+ container.registerFactory(new SSLAcceptorFactory());
+ container.registerFactory(new SSLConnectorFactory());
+ }
+
+ public static synchronized ITCPAcceptor getAcceptor(IManagedContainer container, String description)
+ {
+ return (ITCPAcceptor)container.getElement(TCPAcceptorFactory.PRODUCT_GROUP, SSLAcceptorFactory.TYPE, description);
+ }
+
+ public static synchronized ITCPConnector getConnector(IManagedContainer container, String description)
+ {
+ return (ITCPConnector)container
+ .getElement(TCPConnectorFactory.PRODUCT_GROUP, SSLConnectorFactory.TYPE, description);
+ }
+
+ public static synchronized SSLEngine createSSLEngine(boolean client, String host, int port)
+ throws NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException,
+ UnrecoverableKeyException, KeyManagementException
+ {
+ // get values from the system properties.
+ SSLProperties sslProperties = new SSLProperties();
+ String keyPath = sslProperties.getKeyPath();
+ String trustPath = sslProperties.getTrustPath();
+ String passPhrase = sslProperties.getPassPhrase();
+
+ if ((keyPath == null || trustPath == null || passPhrase == null) && configFile != null)
+ {
+ sslProperties.load(configFile);
+ }
+
+ // in case, the system properties does not have the key path property. it will load from local config file.
+ if (keyPath == null)
+ {
+ keyPath = sslProperties.getKeyPath();
+ if (keyPath == null)
+ {
+ keyPath = keyPathVar;
+ }
+ }
+
+ // in case, the system properties does not have the trust path property. it will load from local config file.
+ if (trustPath == null)
+ {
+ trustPath = sslProperties.getTrustPath();
+ if (trustPath == null)
+ {
+ trustPath = trustPathVar;
+ }
+ }
+
+ // in case, the system properties does not have the passphrase property. it will load from local config file.
+ if (passPhrase == null)
+ {
+ passPhrase = sslProperties.getPassPhrase();
+ if (passPhrase == null)
+ {
+ passPhrase = passPhraseVar;
+ }
+ }
+
+ // handle assign the value of handshake timeout and handshake timewait from local properties or system properties by
+ // giving the value form system properties is high priority.
+ String value = sslProperties.getHandShakeTimeOut();
+ if (value != null)
+ {
+ handShakeTimeOutVar = Integer.parseInt(value);
+ }
+
+ value = sslProperties.getHandShakeWaitTime();
+ if (value != null)
+ {
+ handShakeWaitTimeVar = Integer.parseInt(value);
+ }
+
+ if (keyPath == null && !client || trustPath == null && client || passPhrase == null)
+ {
+ if (client)
+ {
+ throw new KeyStoreException("Trust Store[" + (trustPath != null) + "] or Pass Phrase[" + (passPhrase != null)
+ + "] is not provided. [false] means it does not exist.");
+ }
+
+ throw new KeyStoreException("Key Store[" + (keyPath != null) + "] or Pass Phrase[" + (passPhrase != null)
+ + "] is not provided. [false] means it does not exist.");
+ }
+
+ char[] pass = passPhrase.toCharArray();
+
+ KeyManager[] keyManagers = null;
+ TrustManager[] trustManagers = null;
+
+ if (client)
+ {
+ // initial key material(private key) for the client.
+ KeyStore ksTrust = KeyStore.getInstance(KeyStore.getDefaultType());
+
+ File ksTrustFile = null;
+ FileInputStream ksTrustInputStream = null;
+
+ try
+ {
+ ksTrustFile = new File(new URL(trustPath).toURI());
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.warn(ex.getMessage() + ",so try to load with the normal path", ex);
+ ksTrustFile = new File(trustPath);
+ }
+
+ if (ksTrustFile.exists())
+ {
+ try
+ {
+ ksTrustInputStream = new FileInputStream(ksTrustFile);
+ }
+ catch (FileNotFoundException ex)
+ {
+ throw ex;
+ }
+ finally
+ {
+ if (ksTrustInputStream != null)
+ {
+ ksTrust.load(ksTrustInputStream, pass);
+ ksTrustInputStream.close();
+ }
+ }
+
+ // initial the trust manager factory
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(ksTrust);
+
+ trustManagers = tmf.getTrustManagers();
+ }
+ else
+ {
+ throw new KeyStoreException("Trust Store cannot be loaded.");
+ }
+ }
+ else
+ {
+ // initial key material(private key) for the server.
+ KeyStore ksKeys = KeyStore.getInstance(KeyStore.getDefaultType());
+
+ File ksKeysFile = null;
+ FileInputStream ksKeysInputStream = null;
+
+ try
+ {
+ ksKeysFile = new File(new URL(keyPath).toURI());
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.warn(ex.getMessage() + ",so try to load with the normal path", ex);
+ ksKeysFile = new File(keyPath);
+ }
+
+ if (ksKeysFile.exists())
+ {
+ try
+ {
+ ksKeysInputStream = new FileInputStream(ksKeysFile);
+ }
+ catch (FileNotFoundException ex)
+ {
+ throw ex;
+ }
+ finally
+ {
+ if (ksKeysInputStream != null)
+ {
+ ksKeys.load(ksKeysInputStream, pass);
+ ksKeysInputStream.close();
+ }
+ }
+
+ }
+ else
+ {
+ throw new KeyStoreException("Key Store cannot be loaded.");
+ }
+
+ // initial the key manager factory.
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(ksKeys, pass);
+
+ keyManagers = kmf.getKeyManagers();
+ }
+
+ SSLContext sslContext = SSLContext.getInstance(protocol);
+ sslContext.init(keyManagers, trustManagers, null);
+
+ SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
+ sslEngine.setUseClientMode(client);
+ return sslEngine;
+ }
+
+ public static int getHandShakeTimeOut()
+ {
+ return handShakeTimeOutVar;
+ }
+
+ public static int getHandShakeWaitTime()
+ {
+ return handShakeWaitTimeVar;
+ }
+
+}
diff --git a/plugins/org.eclipse.net4j.tests/sslKey/testKeys b/plugins/org.eclipse.net4j.tests/sslKey/testKeys
new file mode 100644
index 0000000..ecbf5f2
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tests/sslKey/testKeys
Binary files differ
diff --git a/plugins/org.eclipse.net4j.tests/sslKey/testTrust b/plugins/org.eclipse.net4j.tests/sslKey/testTrust
new file mode 100644
index 0000000..9a14541
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tests/sslKey/testTrust
Binary files differ