This is an automated email from the ASF dual-hosted git repository.
elecharny pushed a commit to branch 2.1
in repository https://gitbox.apache.org/repos/asf/mina.git
The following commit(s) were added to refs/heads/2.1 by this push:
new 310e931 Added a test with 2 SSLEngine. It's useful to understand teh
way they are handling the various messages.
310e931 is described below
commit 310e9319f1ae2c62947ccf395fb3432ddafc1253
Author: emmanuel lecharny <[email protected]>
AuthorDate: Fri Apr 12 15:43:31 2019 +0200
Added a test with 2 SSLEngine. It's useful to understand teh way they
are handling the various messages.
---
.../org/apache/mina/filter/ssl/SslEngineTest.java | 324 +++++++++++++++++++++
1 file changed, 324 insertions(+)
diff --git
a/mina-core/src/test/java/org/apache/mina/filter/ssl/SslEngineTest.java
b/mina-core/src/test/java/org/apache/mina/filter/ssl/SslEngineTest.java
new file mode 100644
index 0000000..414dcfe
--- /dev/null
+++ b/mina-core/src/test/java/org/apache/mina/filter/ssl/SslEngineTest.java
@@ -0,0 +1,324 @@
+package org.apache.mina.filter.ssl;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.Security;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.junit.Test;
+
+public class SslEngineTest
+{
+ /** A JVM independant KEY_MANAGER_FACTORY algorithm */
+ private static final String KEY_MANAGER_FACTORY_ALGORITHM;
+
+ static {
+ String algorithm =
Security.getProperty("ssl.KeyManagerFactory.algorithm");
+ if (algorithm == null) {
+ algorithm = KeyManagerFactory.getDefaultAlgorithm();
+ }
+
+ KEY_MANAGER_FACTORY_ALGORITHM = algorithm;
+ }
+
+ /** App data buffer for the client SSLEngine*/
+ private IoBuffer inNetBufferClient;
+
+ /** Net data buffer for the client SSLEngine */
+ private IoBuffer outNetBufferClient;
+
+ /** App data buffer for the server SSLEngine */
+ private IoBuffer inNetBufferServer;
+
+ /** Net data buffer for the server SSLEngine */
+ private IoBuffer outNetBufferServer;
+
+ private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
+
+
+ private static SSLContext createSSLContext() throws IOException,
GeneralSecurityException {
+ char[] passphrase = "password".toCharArray();
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM);
+ TrustManagerFactory tmf =
TrustManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM);
+
+ KeyStore ks = KeyStore.getInstance("JKS");
+ KeyStore ts = KeyStore.getInstance("JKS");
+
+ ks.load(SslTest.class.getResourceAsStream("keystore.sslTest"),
passphrase);
+ ts.load(SslTest.class.getResourceAsStream("truststore.sslTest"),
passphrase);
+
+ kmf.init(ks, passphrase);
+ tmf.init(ts);
+ ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+ return ctx;
+ }
+
+
+ /**
+ * Decrypt the incoming buffer and move the decrypted data to an
+ * application buffer.
+ */
+ private SSLEngineResult unwrap(SSLEngine sslEngine, IoBuffer inBuffer,
IoBuffer outBuffer) throws SSLException {
+ // We first have to create the application buffer if it does not exist
+ if (outBuffer == null) {
+ outBuffer = IoBuffer.allocate(inBuffer.remaining());
+ } else {
+ // We already have one, just add the new data into it
+ outBuffer.expand(inBuffer.remaining());
+ }
+
+ SSLEngineResult res;
+ Status status;
+ HandshakeStatus localHandshakeStatus;
+
+ do {
+ // Decode the incoming data
+ res = sslEngine.unwrap(inBuffer.buf(), outBuffer.buf());
+ status = res.getStatus();
+
+ // We can be processing the Handshake
+ localHandshakeStatus = res.getHandshakeStatus();
+
+ if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ // We have to grow the target buffer, it's too small.
+ // Then we can call the unwrap method again
+ int newCapacity =
sslEngine.getSession().getApplicationBufferSize();
+
+ if (inBuffer.remaining() >= newCapacity) {
+ // The buffer is already larger than the max buffer size
suggested by the SSL engine.
+ // Raising it any more will not make sense and it will end
up in an endless loop. Throwing an error is safer
+ throw new SSLException("SSL buffer overflow");
+ }
+
+ inBuffer.expand(newCapacity);
+ continue;
+ }
+ } while (((status == SSLEngineResult.Status.OK) || (status ==
SSLEngineResult.Status.BUFFER_OVERFLOW))
+ && ((localHandshakeStatus ==
SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) ||
+ (localHandshakeStatus ==
SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
+
+ return res;
+ }
+
+
+ private SSLEngineResult.Status unwrapHandshake(SSLEngine sslEngine,
IoBuffer appBuffer, IoBuffer netBuffer) throws SSLException {
+ // Prepare the net data for reading.
+ if ((appBuffer == null) || !appBuffer.hasRemaining()) {
+ // Need more data.
+ return SSLEngineResult.Status.BUFFER_UNDERFLOW;
+ }
+
+ SSLEngineResult res = unwrap(sslEngine, appBuffer, netBuffer);
+ HandshakeStatus handshakeStatus = res.getHandshakeStatus();
+
+ //checkStatus(res);
+
+ // If handshake finished, no data was produced, and the status is still
+ // ok, try to unwrap more
+ if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
+ && (res.getStatus() == SSLEngineResult.Status.OK)
+ && appBuffer.hasRemaining()) {
+ res = unwrap(sslEngine, appBuffer, netBuffer);
+
+ // prepare to be written again
+ if (appBuffer.hasRemaining()) {
+ appBuffer.compact();
+ } else {
+ appBuffer.free();
+ appBuffer = null;
+ }
+ } else {
+ // prepare to be written again
+ if (appBuffer.hasRemaining()) {
+ appBuffer.compact();
+ } else {
+ appBuffer.free();
+ appBuffer = null;
+ }
+ }
+
+ return res.getStatus();
+ }
+
+
+ /* no qualifier */boolean isInboundDone(SSLEngine sslEngine) {
+ return sslEngine == null || sslEngine.isInboundDone();
+ }
+
+
+ /* no qualifier */boolean isOutboundDone(SSLEngine sslEngine) {
+ return sslEngine == null || sslEngine.isOutboundDone();
+ }
+
+
+ /**
+ * Perform any handshaking processing.
+ */
+ /* no qualifier */HandshakeStatus handshake(SSLEngine sslEngine, IoBuffer
appBuffer, IoBuffer netBuffer ) throws SSLException {
+ SSLEngineResult result;
+ HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
+
+ for (;;) {
+ switch (handshakeStatus) {
+ case FINISHED:
+ //handshakeComplete = true;
+ return handshakeStatus;
+
+ case NEED_TASK:
+ //handshakeStatus = doTasks();
+ break;
+
+ case NEED_UNWRAP:
+ // we need more data read
+ SSLEngineResult.Status status = unwrapHandshake(sslEngine,
appBuffer, netBuffer);
+ handshakeStatus = sslEngine.getHandshakeStatus();
+
+ return handshakeStatus;
+
+ case NEED_WRAP:
+ result = sslEngine.wrap(emptyBuffer.buf(), netBuffer.buf());
+
+ while ( result.getStatus() ==
SSLEngineResult.Status.BUFFER_OVERFLOW ) {
+ netBuffer.capacity(netBuffer.capacity() << 1);
+ netBuffer.limit(netBuffer.capacity());
+
+ result = sslEngine.wrap(emptyBuffer.buf(),
netBuffer.buf());
+ }
+
+ netBuffer.flip();
+ return result.getHandshakeStatus();
+
+ case NOT_HANDSHAKING:
+ result = sslEngine.wrap(emptyBuffer.buf(), netBuffer.buf());
+
+ while ( result.getStatus() ==
SSLEngineResult.Status.BUFFER_OVERFLOW ) {
+ netBuffer.capacity(netBuffer.capacity() << 1);
+ netBuffer.limit(netBuffer.capacity());
+
+ result = sslEngine.wrap(emptyBuffer.buf(),
netBuffer.buf());
+ }
+
+ netBuffer.flip();
+ handshakeStatus = result.getHandshakeStatus();
+ return handshakeStatus;
+
+ default:
+ throw new IllegalStateException("error");
+ }
+ }
+ }
+
+
+ /**
+ * Do all the outstanding handshake tasks in the current Thread.
+ */
+ private SSLEngineResult.HandshakeStatus doTasks(SSLEngine sslEngine) {
+ /*
+ * We could run this in a separate thread, but I don't see the need for
+ * this when used from SSLFilter. Use thread filters in MINA instead?
+ */
+ Runnable runnable;
+ while ((runnable = sslEngine.getDelegatedTask()) != null) {
+ //Thread thread = new Thread(runnable);
+ //thread.start();
+ runnable.run();
+ }
+ return sslEngine.getHandshakeStatus();
+ }
+
+
+ private HandshakeStatus handshake(SSLEngine sslEngine, HandshakeStatus
expected,
+ IoBuffer inBuffer, IoBuffer outBuffer) throws SSLException {
+ HandshakeStatus handshakeStatus = handshake(sslEngine, inBuffer,
outBuffer);
+
+ if ( handshakeStatus != expected) {
+ fail();
+ }
+
+ return handshakeStatus;
+ }
+
+
+ @Test
+ public void testSSL() throws Exception {
+ // Initialise the client SSLEngine
+ SSLContext sslContextClient = createSSLContext();
+ SSLEngine sslEngineClient = sslContextClient.createSSLEngine();
+ int packetBufferSize =
sslEngineClient.getSession().getPacketBufferSize();
+ inNetBufferClient =
IoBuffer.allocate(packetBufferSize).setAutoExpand(true);
+ outNetBufferClient =
IoBuffer.allocate(packetBufferSize).setAutoExpand(true);
+
+ sslEngineClient.setUseClientMode(true);
+
+ // Initialise the Server SSLEngine
+ SSLContext sslContextServer = createSSLContext();
+ SSLEngine sslEngineServer = sslContextServer.createSSLEngine();
+ packetBufferSize = sslEngineServer.getSession().getPacketBufferSize();
+ inNetBufferServer =
IoBuffer.allocate(packetBufferSize).setAutoExpand(true);
+ outNetBufferServer =
IoBuffer.allocate(packetBufferSize).setAutoExpand(true);
+
+ sslEngineServer.setUseClientMode(false);
+
+ HandshakeStatus handshakeStatusClient =
sslEngineClient.getHandshakeStatus();
+ HandshakeStatus handshakeStatusServer =
sslEngineServer.getHandshakeStatus();
+
+ // Start the server
+ handshakeStatusServer = handshake(sslEngineServer,
HandshakeStatus.NEED_UNWRAP, inNetBufferServer, outNetBufferServer);
+
+ // Now start the client
+ handshakeStatusClient = handshake(sslEngineClient,
HandshakeStatus.NEED_UNWRAP, inNetBufferClient, outNetBufferClient);
+
+ // 'Read' the CLIENT_HELLO to the server
+ handshakeStatusServer = handshake(sslEngineServer,
HandshakeStatus.NEED_TASK, outNetBufferClient, outNetBufferServer);
+
+ // Create the SERVER_HELLO message
+ handshakeStatusServer = doTasks(sslEngineServer);
+
+ // We should get back the message
+ if ( handshakeStatusServer != HandshakeStatus.NEED_WRAP) {
+ fail();
+ }
+
+ // 'Send' the SERVER_HELLO message to the client
+ outNetBufferServer.clear();
+ handshakeStatusServer = handshake(sslEngineServer,
HandshakeStatus.NEED_UNWRAP, null, outNetBufferServer);
+
+ // 'Read' the SERVER_HELLO message on the client
+ handshakeStatusClient = handshake(sslEngineClient,
HandshakeStatus.NEED_TASK, outNetBufferServer, inNetBufferClient);
+
+ // Create the message
+ handshakeStatusClient = doTasks(sslEngineClient);
+
+ // We should get back the message
+ if ( handshakeStatusClient != HandshakeStatus.NEED_WRAP) {
+ fail();
+ }
+
+ // 'Send' the SERVER_HELLO message to the client
+ outNetBufferClient.clear();
+ handshakeStatusClient = handshake(sslEngineClient,
HandshakeStatus.NEED_WRAP, null, outNetBufferClient);
+
+ // 'Send' the CLIENT_KEY_EXCHANGE message to the server
+ outNetBufferClient.clear();
+ handshakeStatusClient = handshake(sslEngineClient,
HandshakeStatus.NEED_WRAP, null, outNetBufferClient);
+
+ // 'Send' the ALERT message to the server
+ outNetBufferClient.clear();
+ handshakeStatusClient = handshake(sslEngineClient,
HandshakeStatus.NEED_UNWRAP, null, outNetBufferClient);
+ }
+}