diff --git a/examples/provider/MultiThreadedSSLClient.java b/examples/provider/MultiThreadedSSLClient.java new file mode 100644 index 0000000..38b62de --- /dev/null +++ b/examples/provider/MultiThreadedSSLClient.java @@ -0,0 +1,235 @@ +/* MultiThreadedSSLClient.java + * + * Copyright (C) 2006-2021 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * Multi threaded SSLSocket example that connects a specified number of + * client threads to a server. Intended to test multi-threading with wolfJSSE. + * + * This example creates a specified number of client threads to a server + * located at 127.0.0.1:11118. This example is set up to use the SSLSocket + * class. It makes one connection (handshake), sends/receives data, and shuts + * down. + * + * A random amount of time is injected into each client thread before: + * 1) The SSL/TLS handshake + * 2) Doing I/O operations after the handshake + * + * The maximum amount of sleep time for each of those is "maxSleep", or + * 3 seconds by default. This is intended to add some randomness into the + * the client thread operations. + * + * Example usage: + * + * $ ant examples + * $ ./examples/provider/MultiThreadedSSLClient.java + * + * This example is designed to connect against the MultiThreadedSSLServer + * example: + * + * $ ./examples/provider/MultiThreadedSSLServer.java + * + * This example also prints out average SSL/TLS handshake time, which is + * measured in milliseconds on the "startHandshake()" API call. + */ + +import java.util.*; +import java.io.*; +import java.net.*; +import javax.net.ssl.*; +import java.security.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import com.wolfssl.provider.jsse.WolfSSLProvider; + +public class MultiThreadedSSLClient +{ + String tmfImpl = "SunX509"; /* TrustManagerFactory provider */ + String kmfImpl = "SunX509"; /* KeyManagerFactory provider */ + String ctxImpl = "wolfJSSE"; /* SSLContext provider */ + + String srvHost = "127.0.0.1"; /* server host */ + int srvPort = 11118; /* server port */ + + int numClientConnections = 10; /* number of client connection threads */ + int startedClientConnections = 0; /* active clients connected to server */ + int successClientConnections = 0; /* successful client connections */ + int failedClientConnections = 0; /* failed client connections */ + + long totalConnectionTimeMs = 0; /* total handshake time, across clients */ + final Object timeLock = new Object(); + + class ClientThread implements Runnable + { + private KeyManagerFactory km = null; + private TrustManagerFactory tm = null; + private CountDownLatch latch; + + public ClientThread(KeyManagerFactory km, TrustManagerFactory tm, + CountDownLatch latch) { + this.km = km; + this.tm = tm; + this.latch = latch; + } + + public void run() { + + byte[] back = new byte[80]; + String msg = "Too legit to quit"; + + /* max sleep is 3 seconds */ + int maxSleep = 3000; + + /* get random sleep value before calling connect() */ + int randConnectSleep = + ThreadLocalRandom.current().nextInt(0, maxSleep + 1); + + /* get random sleep value before doing I/O after handshake */ + int randIOSleep = + ThreadLocalRandom.current().nextInt(0, maxSleep +1); + + try { + SSLContext ctx = SSLContext.getInstance("TLS", ctxImpl); + ctx.init(km.getKeyManagers(), tm.getTrustManagers(), null); + + SSLSocket sock = (SSLSocket)ctx.getSocketFactory() + .createSocket(); + + Thread.sleep(randConnectSleep); + + sock.connect(new InetSocketAddress(srvHost, srvPort)); + + final long startTime = System.currentTimeMillis(); + sock.startHandshake(); + final long endTime = System.currentTimeMillis(); + + synchronized (timeLock) { + totalConnectionTimeMs += (endTime - startTime); + } + + Thread.sleep(randIOSleep); + + sock.getOutputStream().write(msg.getBytes()); + sock.getInputStream().read(back); + System.out.println("Server message : " + new String(back)); + + sock.close(); + successClientConnections++; + + } catch (Exception e) { + e.printStackTrace(); + failedClientConnections++; + } + + this.latch.countDown(); + } + } + + public MultiThreadedSSLClient(String[] args) { + + Security.addProvider(new WolfSSLProvider()); + + String clientKS = "./examples/provider/client.jks"; + String clientTS = "./examples/provider/client.jks"; + String jkspass = "wolfSSL test"; + char[] passArr = jkspass.toCharArray(); + + if (args.length != 2) { + printUsage(); + } + + /* pull in command line options from user */ + for (int i = 0; i < args.length; i++) + { + String arg = args[i]; + + if (arg.equals("-n")) { + if (args.length < i+2) + printUsage(); + numClientConnections = Integer.parseInt(args[++i]); + + } else { + printUsage(); + } + } + + try { + List clientList = new ArrayList(); + CountDownLatch latch = new CountDownLatch(numClientConnections); + + /* set up client KeyStore */ + KeyStore clientKeyStore = KeyStore.getInstance("JKS"); + clientKeyStore.load(new FileInputStream(clientKS), passArr); + + KeyManagerFactory clientKMF = + KeyManagerFactory.getInstance(kmfImpl); + clientKMF.init(clientKeyStore, passArr); + + /* set up CA TrustManagerFactory */ + KeyStore caKeyStore = KeyStore.getInstance("JKS"); + caKeyStore.load(new FileInputStream(clientTS), passArr); + + TrustManagerFactory tm = TrustManagerFactory.getInstance(tmfImpl); + tm.init(caKeyStore); + + for (int i = 0; i < numClientConnections; i++) { + clientList.add(new ClientThread(clientKMF, tm, latch)); + } + + ExecutorService executor = Executors.newFixedThreadPool( + clientList.size()); + + for (final ClientThread c : clientList) { + executor.execute(c); + } + + latch.await(); + executor.shutdown(); + + } catch (Exception e) { + e.printStackTrace(); + } + + Security.removeProvider("wolfJSSE"); + + System.out.println("================================================"); + System.out.println("All Client Connections Finished"); + System.out.println("Successful = " + successClientConnections); + System.out.println("Failed = " + failedClientConnections); + System.out.println("Avg handshake time = " + + totalConnectionTimeMs / successClientConnections + " ms"); + System.out.println("================================================"); + } + + + public static void main(String[] args) { + new MultiThreadedSSLClient(args); + } + + private void printUsage() { + System.out.println("Java wolfJSSE example threaded client usage:"); + System.out.println("-n \tNumber of client connections"); + System.exit(1); + } +} + diff --git a/examples/provider/MultiThreadedSSLClient.sh b/examples/provider/MultiThreadedSSLClient.sh new file mode 100755 index 0000000..3296d11 --- /dev/null +++ b/examples/provider/MultiThreadedSSLClient.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib/:/usr/local/lib +java -classpath ./lib/wolfssl.jar:./lib/wolfssl-jsse.jar:./examples/build -Dsun.boot.library.path=./lib/ -Dwolfjsse.debug=true MultiThreadedSSLClient $@ + diff --git a/examples/provider/MultiThreadedSSLServer.java b/examples/provider/MultiThreadedSSLServer.java index 7be3edd..e2070a5 100644 --- a/examples/provider/MultiThreadedSSLServer.java +++ b/examples/provider/MultiThreadedSSLServer.java @@ -24,14 +24,15 @@ * * This server waits in an infinite loop for client connections, and when * connected creates a new thread for each connection. This example is compiled - * when 'ant' is run in the package root. + * when 'ant examples' is run in the package root. * - * $ ant + * $ ant examples * $ ./examples/provider/MultiThreadedSSLServer.sh * - * This can be tested against the normal wolfSSL example client. But, wolfSSL - * will need to be compiled with WOLFSSL_ALT_TEST_STRINGS defined so that - * the client strings are null terminated. + * For multi threaded client testing, test against MultiThreadedSSLClient.sh. + * For example, to connect 10 client threads: + * + * ./examples/provider/MultiThreadedSSLClient.sh -n 10 * */ import java.util.*; @@ -44,7 +45,8 @@ import com.wolfssl.provider.jsse.WolfSSLProvider; public class MultiThreadedSSLServer { private char[] psw = "wolfSSL test".toCharArray(); - private String serverJKS = "./examples/provider/server.jks"; + private String serverKS = "./examples/provider/rsa.jks"; + private String serverTS = "./examples/provider/client.jks"; int serverPort = 11118; public MultiThreadedSSLServer() { @@ -52,16 +54,20 @@ public class MultiThreadedSSLServer Security.addProvider(new WolfSSLProvider()); - KeyStore pKey = KeyStore.getInstance("JKS"); - pKey.load(new FileInputStream(serverJKS), psw); - KeyStore cert = KeyStore.getInstance("JKS"); - cert.load(new FileInputStream(serverJKS), psw); + /* Set up KeyStore */ + KeyStore serverKeyStore = KeyStore.getInstance("JKS"); + serverKeyStore.load(new FileInputStream(serverKS), psw); + + KeyManagerFactory km = KeyManagerFactory.getInstance("SunX509"); + km.init(serverKeyStore, psw); + + /* Set up CA TrustManagerFactory */ + KeyStore caKeyStore = KeyStore.getInstance("JKS"); + caKeyStore.load(new FileInputStream(serverTS), psw); TrustManagerFactory tm = TrustManagerFactory.getInstance("SunX509"); - tm.init(cert); - - KeyManagerFactory km = KeyManagerFactory.getInstance("SunX509"); - km.init(pKey, psw); + tm.init(caKeyStore); + SSLContext ctx = SSLContext.getInstance("TLS", "wolfJSSE"); ctx.init(km.getKeyManagers(), tm.getTrustManagers(), null); @@ -91,26 +97,17 @@ public class MultiThreadedSSLServer public void run() { + byte[] response = new byte[80]; + String msg = "I hear you fa shizzle, from Java!"; + try { - OutputStream rawOut = sock.getOutputStream(); + sock.startHandshake(); - PrintWriter out = new PrintWriter( - new BufferedWriter( - new OutputStreamWriter(rawOut))); + sock.getInputStream().read(response); + System.out.println("Client message : " + new String(response)); + sock.getOutputStream().write(msg.getBytes()); - BufferedReader in = new BufferedReader( - new InputStreamReader(sock.getInputStream())); - - String line = in.readLine(); - System.out.println("client: " + line); - - out.print("I hear you C client!"); - out.flush(); - out.close(); - in.close(); - - in.close(); sock.close(); } catch (Exception e) {