Merge pull request #212 from cconlon/threadedJNIClient
JNI: add example threaded client/server applications (JNI only)pull/209/head
commit
4e70b6827d
|
@ -41,6 +41,33 @@ argument:
|
|||
$ ./examples/server.sh --help
|
||||
```
|
||||
|
||||
## wolfSSL JNI Example Simple Threaded Client and Server
|
||||
|
||||
Example client/server applications that use threads, which use
|
||||
wolfSSL JNI (not JSSE):
|
||||
|
||||
**SimpleThreadedClient.java** - Example wolfSSL JNI threaded client \
|
||||
**SimpleThreadedServer.java** - Example wolfSSL JNI threaded server
|
||||
|
||||
These examples can be run with the provided bash scripts:
|
||||
|
||||
```
|
||||
$ cd <wolfssljni_root>
|
||||
$ ./examples/SimpleThreadedServer.sh
|
||||
$ ./examples/SimpleThreadedClient.sh -n <num_connections>
|
||||
```
|
||||
|
||||
The `SimpleThreadedServer.java` starts at `localhost:11111` and waits for
|
||||
client connections. When a client connection is received, it is handled in a
|
||||
separate thread.
|
||||
|
||||
The `SimpleThreadedClient.java` makes concurrent client connections to a server
|
||||
located at `localhost:11111`. Default number of client threads is **5**, but
|
||||
can be changed using the `-n <num_connections>` command line argument. This
|
||||
example implements a simple application-wide Java client cache where native
|
||||
`WOLFSSL_SESSION` pointers are stored and used for session resumption where
|
||||
possible. See code comments for further explanation.
|
||||
|
||||
## X509v3 Certificate Generation Example
|
||||
|
||||
An example is included which will generate self-signed and CA-signed
|
||||
|
|
|
@ -0,0 +1,489 @@
|
|||
/* SimpleThreadedServer.java
|
||||
*
|
||||
* Copyright (C) 2006-2024 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-1335, USA
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
|
||||
import com.wolfssl.WolfSSL;
|
||||
import com.wolfssl.WolfSSLContext;
|
||||
import com.wolfssl.WolfSSLSession;
|
||||
import com.wolfssl.WolfSSLJNIException;
|
||||
|
||||
/**
|
||||
* This is a simple example of a launching multiple client threads
|
||||
* connecting to the same server, while attemping to do session resumption
|
||||
* where possible. Client threads are started and sleep a random
|
||||
* value between 0 and 1 second so they are not all running at exactly the same
|
||||
* time. This behavior more closely mimics real world application use.
|
||||
*
|
||||
* A application-wide static client session cache is implemented here as
|
||||
* a LinkedHashMap. This is used to store WOLFSSL_SESSION pointer (long)
|
||||
* values obtained from getSession(), and used again with setSession().
|
||||
*
|
||||
* This is meant to be a simple usage example, so there is not much
|
||||
* customization currently from the command line. Certs and keys are hard
|
||||
* coded to the values in class variables below.
|
||||
*
|
||||
* Client threads make SSL/TLS connections to localhost:11111, using the
|
||||
* SSLv23_ClientMethod() during creation of the WolfSSLContext.
|
||||
*
|
||||
* This example has been designed to connect against the SimpleThreadedServer
|
||||
* example in the same directory:
|
||||
*
|
||||
* cd wolfssljni
|
||||
* ./examples/SimpleThreadedServer.sh
|
||||
*
|
||||
* ./examples/SimpleThreadedClient -n 10
|
||||
*
|
||||
*/
|
||||
public class SimpleThreadedClient {
|
||||
|
||||
/* Cert and key info */
|
||||
private String clientCert = "../certs/client-cert.pem";
|
||||
private String clientKey = "../certs/client-key.pem";
|
||||
private String caCert = "../certs/ca-cert.pem";
|
||||
private String crlPemDir = "../certs/crl";
|
||||
|
||||
/* Server info */
|
||||
int serverPort = 11111;
|
||||
|
||||
/* Number of client threads to start connecting to server,
|
||||
* can be changed using the -n command line argument. */
|
||||
int numConnections = 5;
|
||||
|
||||
/* Keep track of connection count that is resumed vs full */
|
||||
final AtomicIntegerArray connectionsResumed = new AtomicIntegerArray(1);
|
||||
final AtomicIntegerArray connectionsFull = new AtomicIntegerArray(1);
|
||||
|
||||
/* Client session store:
|
||||
* Key: hash of host:port
|
||||
* Value: pointer (long) to WOLFSSL_SESSION
|
||||
* Default size: 33 sessions (defaultCacheSize)
|
||||
*
|
||||
* This is a Java session store to store WOLFSSL_SESSION pointers
|
||||
* to be used with setSession(). Pointers will have been obtained
|
||||
* with a call to getSession().
|
||||
*
|
||||
* Since the TLS 1.3 binder changes on each session connection, each entry
|
||||
* should only be re-used by one thread/connection resumption at a time.
|
||||
* We remove the session from the cache, try to resume it, and after
|
||||
* a new successful connection put it back in the cache. This does mean
|
||||
* that parallel concurrent client connections to the same host may not
|
||||
* have all connections resume. Only the first thread to grab the session
|
||||
* from the cache will potentially do a resumed session. Others will fall
|
||||
* back to a new full handshake.
|
||||
*
|
||||
* Access to the session store should be synchronized on storeLock.
|
||||
*/
|
||||
private int defaultCacheSize = 33;
|
||||
protected SessionStore<Integer, Long> store =
|
||||
new SessionStore<>(defaultCacheSize);
|
||||
private static final Object storeLock = new Object();
|
||||
|
||||
/**
|
||||
* Inner SessionStore class, used for client session store.
|
||||
*/
|
||||
private class SessionStore<K, V> extends LinkedHashMap<K, V> {
|
||||
/* User defined ID */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/* Max LinkedHashMap size, before purging entries */
|
||||
private final int maxSz;
|
||||
|
||||
/**
|
||||
* Create new SessionStore.
|
||||
* @param size max size of LinkedHashMap before oldest entry is
|
||||
* overwritten
|
||||
*/
|
||||
protected SessionStore(int size) {
|
||||
this.maxSz = size;
|
||||
}
|
||||
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> oldest) {
|
||||
return size() > maxSz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a saved session (WOLFSSL_SESSION) from the client
|
||||
* session cache. Sessions are keyed off the hash code of
|
||||
* host+port. In production applications consder also keying off
|
||||
* cipher suite and protocol version.
|
||||
*
|
||||
* @param port port number of peer being connected to
|
||||
* @param host host of the peer being connected to
|
||||
* @return an existing pointer (long) to a WOLFSSL_SESSION structure,
|
||||
* removing it from the client cache so other threads do not try
|
||||
* to resume it at the same time. Or, 0 if no session is found in
|
||||
* cache.
|
||||
*/
|
||||
private synchronized long getSession(String host, int port) {
|
||||
|
||||
long sesPtr = 0;
|
||||
String toHash = null;
|
||||
|
||||
if (host == null || host.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
synchronized (storeLock) {
|
||||
toHash = host.concat(Integer.toString(port));
|
||||
|
||||
System.out.println("Entered getSession()\n" +
|
||||
"| host = " + host + ", port = " + port + "\n" +
|
||||
"| toHash = " + toHash + ", hashCode = " +
|
||||
toHash.hashCode() + "\n" +
|
||||
"| store = " + store);
|
||||
|
||||
if (toHash != null && store != null) {
|
||||
Integer storeKey = new Integer(toHash.hashCode());
|
||||
Long storeVal = this.store.get(storeKey);
|
||||
if (storeVal != null) {
|
||||
sesPtr = storeVal.longValue();
|
||||
}
|
||||
|
||||
/* Delete entry from cache */
|
||||
this.store.remove(storeKey);
|
||||
}
|
||||
|
||||
if (sesPtr == 0) {
|
||||
System.out.println("|-- SESSION NOT FOUND IN CACHE: 0");
|
||||
} else {
|
||||
System.out.println("|-- FOUND SESSION IN CACHE: " + sesPtr);
|
||||
}
|
||||
|
||||
return sesPtr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store WOLFSSL_SESSION pointer (long) into client session cache, keying
|
||||
* off hash code of host:port.
|
||||
*
|
||||
* Keep in mind that the WOLFSSL_SESSION pointer will need to be freed
|
||||
* at some point with freeSession().
|
||||
*
|
||||
* @param port port number of peer connected to for this session
|
||||
* @param host hots of peer connected to
|
||||
*/
|
||||
private synchronized void storeSession(String host, int port, long sesPtr) {
|
||||
|
||||
int hashCode = 0;
|
||||
String toHash = null;
|
||||
|
||||
if (host == null || host.isEmpty() || sesPtr == 0) {
|
||||
/* Invalid args, don't store */
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (storeLock) {
|
||||
toHash = host.concat(Integer.toString(port));
|
||||
hashCode = toHash.hashCode();
|
||||
if (hashCode != 0) {
|
||||
store.put(hashCode, sesPtr);
|
||||
System.out.println("Entered storeSession()\n" +
|
||||
"| stored session: " + sesPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClientThread implements Runnable
|
||||
{
|
||||
private WolfSSLContext sslCtx = null;
|
||||
private CountDownLatch closedLatch = null;
|
||||
|
||||
public ClientThread(WolfSSLContext sslCtx, CountDownLatch latch) {
|
||||
this.sslCtx = sslCtx;
|
||||
this.closedLatch = latch;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
int ret = 0;
|
||||
int err = 0;
|
||||
int input = 0;
|
||||
String host = "localhost";
|
||||
WolfSSLSession ssl = null;
|
||||
Socket sock = null;
|
||||
byte[] back = new byte[80];
|
||||
String msg = "hello from jni";
|
||||
|
||||
/* Pointer to native WOLFSSL_SESSION */
|
||||
long session = 0;
|
||||
|
||||
try {
|
||||
|
||||
/* Introduce a random sleep per thread, so all client
|
||||
* threads are started concurrently. Otherwise, one will get
|
||||
* the cache entry and all others will not resume. Adding
|
||||
* a random sleep makes this more realistic to what a real
|
||||
* application would be encountering with staggered sessions
|
||||
* across threads.
|
||||
*
|
||||
* Sleep here is random between zero and 1 second.
|
||||
*/
|
||||
Thread.sleep((long)(Math.random() * 1000));
|
||||
|
||||
/* Create new WolfSSLSession */
|
||||
ssl = new WolfSSLSession(sslCtx);
|
||||
|
||||
/* Connect TCP Socket */
|
||||
sock = new Socket(host, serverPort);
|
||||
System.out.println("Connected to " +
|
||||
sock.getInetAddress().getHostAddress() +
|
||||
" on port " + sock.getPort() + "\n");
|
||||
|
||||
/* Pass Socket descriptor to wolfSSL */
|
||||
ret = ssl.setFd(sock);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
throw new RuntimeException("Failed to set file descriptor");
|
||||
}
|
||||
|
||||
/* Try to get session from session cache. We may not have a
|
||||
* session in the cache yet if no connection has been made to
|
||||
* this server, or another connection/thread already
|
||||
* grabbed the client out of the cache. We need to remove the
|
||||
* client session from the cache each time since the TLS 1.3
|
||||
* binder changes between resumptions. */
|
||||
session = getSession(host, serverPort);
|
||||
if (session != 0) {
|
||||
/* Restore saved WOLFSSL_SESSION, clear pointer after use */
|
||||
ssl.setSession(session);
|
||||
|
||||
/* Free native WOLFSSL_SESSION memory */
|
||||
ssl.freeSession(session);
|
||||
session = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = ssl.connect();
|
||||
err = ssl.getError(ret);
|
||||
} while (ret != WolfSSL.SSL_SUCCESS &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
err = ssl.getError(ret);
|
||||
String errString = WolfSSL.getErrorString(err);
|
||||
throw new RuntimeException(
|
||||
"wolfSSL_connect failed. err = " + err +
|
||||
", " + errString);
|
||||
}
|
||||
|
||||
do {
|
||||
ret = ssl.write(msg.getBytes(), msg.length());
|
||||
err = ssl.getError(0);
|
||||
} while (ret < 0 &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
|
||||
/* Read response */
|
||||
do {
|
||||
input = ssl.read(back, back.length);
|
||||
err = ssl.getError(0);
|
||||
} while (input < 0 &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
|
||||
if (input > 0) {
|
||||
System.out.println("got back: " + new String(back));
|
||||
} else {
|
||||
throw new RuntimeException("read failed");
|
||||
}
|
||||
|
||||
/* Did we resume a connection from the cache? */
|
||||
if (ssl.sessionReused() == 1) {
|
||||
System.out.println("Session resumed");
|
||||
connectionsResumed.incrementAndGet(0);
|
||||
} else {
|
||||
System.out.println("New session was made, not resumed");
|
||||
connectionsFull.incrementAndGet(0);
|
||||
}
|
||||
|
||||
/* Get native WOLFSSL_SESSION and store into our local
|
||||
* client cache for resumption attempts later */
|
||||
session = ssl.getSession();
|
||||
if (session == 0) {
|
||||
System.out.println("Failed to get native WOLFSSL_SESSION");
|
||||
} else {
|
||||
storeSession(host, serverPort, session);
|
||||
session = 0;
|
||||
System.out.println("Saved native WOLFSSL_SESSION");
|
||||
}
|
||||
|
||||
ssl.shutdownSSL();
|
||||
ssl.freeSSL();
|
||||
ssl = null;
|
||||
sock.close();
|
||||
sock = null;
|
||||
|
||||
closedLatch.countDown();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
} finally {
|
||||
if (sock != null) {
|
||||
try {
|
||||
sock.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
sock = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LaunchClientThreads()
|
||||
throws Exception {
|
||||
|
||||
int ret = 0;
|
||||
WolfSSLContext sslCtx = null;
|
||||
CountDownLatch closedLatch = null;
|
||||
List<ClientThread> clientList = null;
|
||||
ExecutorService executor = null;
|
||||
|
||||
closedLatch = new CountDownLatch(numConnections);
|
||||
clientList = new ArrayList<ClientThread>();
|
||||
|
||||
/* Create one WolfSSLContext to share accross all client threads */
|
||||
sslCtx = new WolfSSLContext(WolfSSL.SSLv23_ClientMethod());
|
||||
|
||||
/* Load client certificate */
|
||||
ret = sslCtx.useCertificateFile(clientCert,
|
||||
WolfSSL.SSL_FILETYPE_PEM);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
throw new RuntimeException(
|
||||
"failed to load client certificate!");
|
||||
}
|
||||
|
||||
/* Load client private key */
|
||||
ret = sslCtx.usePrivateKeyFile(clientKey,
|
||||
WolfSSL.SSL_FILETYPE_PEM);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
throw new RuntimeException(
|
||||
"failed to load client private key!");
|
||||
}
|
||||
|
||||
/* Load CA certificate to verify server */
|
||||
ret = sslCtx.loadVerifyLocations(caCert, null);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
throw new RuntimeException("failed to load CA certificates!");
|
||||
}
|
||||
|
||||
/* Start client threads */
|
||||
for (int i = 0; i < numConnections; i++) {
|
||||
clientList.add(new ClientThread(sslCtx, closedLatch));
|
||||
}
|
||||
|
||||
executor = Executors.newFixedThreadPool(clientList.size());
|
||||
for (final ClientThread c : clientList) {
|
||||
executor.execute(c);
|
||||
}
|
||||
|
||||
/* Wait for client connections to finish */
|
||||
closedLatch.await();
|
||||
executor.shutdown();
|
||||
|
||||
/* Go through Java client session cache and free memory for
|
||||
* native WOLFSSL_SESSION pointers */
|
||||
synchronized (storeLock) {
|
||||
Iterator<Integer> iterator = store.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Integer key = iterator.next();
|
||||
Long ptr = store.get(key);
|
||||
|
||||
if (ptr != 0) {
|
||||
WolfSSLSession.freeSession(ptr);
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("\nCompleted " + numConnections +
|
||||
" client connections\n");
|
||||
System.out.println("\tconnections resumed: " +
|
||||
connectionsResumed.get(0));
|
||||
System.out.println("\tconnections full handshake: " +
|
||||
connectionsFull.get(0));
|
||||
}
|
||||
|
||||
public void run(String[] args) throws Exception {
|
||||
|
||||
/* Read and process command line options from user */
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
|
||||
if (arg.equals("-help")) {
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
else if (arg.equals("-n")) {
|
||||
if (args.length < i+2) {
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
numConnections = Integer.parseInt(args[++i]);
|
||||
}
|
||||
}
|
||||
|
||||
connectionsResumed.set(0, 0);
|
||||
connectionsFull.set(0, 0);
|
||||
|
||||
/* Setup and start client threads */
|
||||
LaunchClientThreads();
|
||||
}
|
||||
|
||||
private void printUsage() {
|
||||
System.out.println("Threaded Client Example:");
|
||||
System.out.println("-help\t\tHelp, print this usage");
|
||||
System.out.println("-n <num>\tNumber of threads/connections");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
try {
|
||||
/* Load library */
|
||||
WolfSSL.loadLibrary();
|
||||
|
||||
SimpleThreadedClient test = new SimpleThreadedClient();
|
||||
test.run(args);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd ./examples/build
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../lib/:/usr/local/lib
|
||||
java -classpath ../../lib/wolfssl.jar:./ -Dsun.boot.library.path=../../lib/ SimpleThreadedClient $@
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
/* SimpleThreadedServer.java
|
||||
*
|
||||
* Copyright (C) 2006-2024 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-1335, USA
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
|
||||
import com.wolfssl.WolfSSL;
|
||||
import com.wolfssl.WolfSSLSession;
|
||||
import com.wolfssl.WolfSSLContext;
|
||||
import com.wolfssl.WolfSSLCertificate;
|
||||
import com.wolfssl.WolfSSLException;
|
||||
import com.wolfssl.WolfSSLJNIException;
|
||||
|
||||
/**
|
||||
* Simple SSL/TLS server that uses wolfSSL JNI (not JSSE).
|
||||
* The server listens for client connections at localhost:11111 and
|
||||
* handles each client in a separate thread as they come in.
|
||||
*
|
||||
* This is meant to be a very simple example and does not currently offer
|
||||
* much customization. It uses the hard-coded certs and keys found below,
|
||||
* and creates the WolfSSLContext using SSLv23_ServerMethod().
|
||||
*/
|
||||
public class SimpleThreadedServer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SimpleThreadedServer().run(args);
|
||||
}
|
||||
|
||||
public void run(String[] args) {
|
||||
|
||||
int ret = 0;
|
||||
int serverPort = 11111;
|
||||
Socket clientSocket = null;
|
||||
ServerSocket serverSocket = null;
|
||||
|
||||
/* Cert and Key info */
|
||||
String serverCert = "../certs/server-cert.pem";
|
||||
String serverKey = "../certs/server-key.pem";
|
||||
String caCert = "../certs/client-cert.pem";
|
||||
String crlPemDir = "../certs/crl";
|
||||
String dhParam = "../certs/dh2048.pem";
|
||||
|
||||
try {
|
||||
/* Load JNI library */
|
||||
WolfSSL.loadLibrary();
|
||||
|
||||
/* Init library */
|
||||
WolfSSL sslLib = new WolfSSL();
|
||||
|
||||
/* Create context */
|
||||
WolfSSLContext sslCtx = new WolfSSLContext(
|
||||
WolfSSL.SSLv23_ServerMethod());
|
||||
|
||||
/* Load certificate/key files */
|
||||
ret = sslCtx.useCertificateChainFile(serverCert);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
System.out.println("failed to load server certificate!");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
ret = sslCtx.usePrivateKeyFile(serverKey,
|
||||
WolfSSL.SSL_FILETYPE_PEM);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
System.out.println("failed to load server private key!");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
/* Set verify callback */
|
||||
ret = sslCtx.loadVerifyLocations(caCert, null);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
System.out.println("failed to load CA certificates!");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
/* Create server socket */
|
||||
serverSocket = new ServerSocket(serverPort);
|
||||
|
||||
System.out.println("Started server at " +
|
||||
InetAddress.getLocalHost() + ", port " + serverPort);
|
||||
|
||||
/* Wait for new client connections, process each in new thread */
|
||||
while (true) {
|
||||
|
||||
clientSocket = serverSocket.accept();
|
||||
System.out.println("client connection received from " +
|
||||
clientSocket.getInetAddress().getHostAddress() +
|
||||
" at port " + clientSocket.getLocalPort() + "\n");
|
||||
|
||||
ClientHandler client = new ClientHandler(clientSocket, sslCtx);
|
||||
client.start();
|
||||
}
|
||||
|
||||
} catch (UnsatisfiedLinkError | WolfSSLException | IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (serverSocket != null) {
|
||||
serverSocket.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
} /* end run() */
|
||||
|
||||
class ClientHandler extends Thread
|
||||
{
|
||||
int ret = 0;
|
||||
int insz = 0;
|
||||
int err = 0;
|
||||
Socket clientSocket;
|
||||
WolfSSLContext sslCtx;
|
||||
String msg = "I hear you fa shizzle, from Java!";
|
||||
byte[] input = new byte[80];
|
||||
|
||||
public ClientHandler(Socket s, WolfSSLContext ctx) {
|
||||
clientSocket = s;
|
||||
sslCtx = ctx;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
WolfSSLSession ssl = null;
|
||||
DataOutputStream outstream = null;
|
||||
DataInputStream instream = null;
|
||||
|
||||
try {
|
||||
/* Get input and output streams */
|
||||
outstream = new DataOutputStream(
|
||||
clientSocket.getOutputStream());
|
||||
instream = new DataInputStream(
|
||||
clientSocket.getInputStream());
|
||||
|
||||
/* Create SSL object */
|
||||
ssl = new WolfSSLSession(sslCtx);
|
||||
|
||||
/* Pass socket fd to wolfSSL */
|
||||
ret = ssl.setFd(clientSocket);
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
throw new RuntimeException("Failed to set file descriptor");
|
||||
}
|
||||
|
||||
do {
|
||||
ret = ssl.accept();
|
||||
err = ssl.getError(ret);
|
||||
|
||||
} while (ret != WolfSSL.SSL_SUCCESS &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
|
||||
if (ret != WolfSSL.SSL_SUCCESS) {
|
||||
err = ssl.getError(ret);
|
||||
String errString = WolfSSL.getErrorString(err);
|
||||
throw new RuntimeException(
|
||||
"wolfSSL_accept failed. err = " + err +
|
||||
", " + errString);
|
||||
}
|
||||
|
||||
/* Show peer info */
|
||||
showPeer(ssl);
|
||||
|
||||
/* Read client response, and echo */
|
||||
do {
|
||||
insz = ssl.read(input, input.length);
|
||||
err = ssl.getError(0);
|
||||
} while (insz < 0 &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
|
||||
if (input.length > 0) {
|
||||
String cliMsg = new String(input, 0, insz);
|
||||
System.out.println("client says: " + cliMsg);
|
||||
} else {
|
||||
throw new RuntimeException("read failed");
|
||||
}
|
||||
|
||||
do {
|
||||
ret = ssl.write(msg.getBytes(), msg.length());
|
||||
err = ssl.getError(0);
|
||||
} while (ret < 0 &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
|
||||
if (ret != msg.length()) {
|
||||
throw new RuntimeException("ssl.write() failed");
|
||||
}
|
||||
|
||||
ssl.shutdownSSL();
|
||||
|
||||
} catch (WolfSSLException | IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
} finally {
|
||||
if (ssl != null) {
|
||||
try {
|
||||
ssl.freeSSL();
|
||||
} catch (WolfSSLJNIException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showPeer(WolfSSLSession ssl) {
|
||||
|
||||
String altname;
|
||||
long peerCrtPtr = 0;
|
||||
|
||||
try {
|
||||
|
||||
System.out.println("TLS version is " + ssl.getVersion());
|
||||
System.out.println("TLS cipher suite is " + ssl.cipherGetName());
|
||||
|
||||
peerCrtPtr = ssl.getPeerCertificate();
|
||||
if (peerCrtPtr != 0) {
|
||||
System.out.println(
|
||||
"issuer : " + ssl.getPeerX509Issuer(peerCrtPtr));
|
||||
System.out.println(
|
||||
"subject : " + ssl.getPeerX509Subject(peerCrtPtr));
|
||||
|
||||
while((altname = ssl.getPeerX509AltName(peerCrtPtr)) != null) {
|
||||
System.out.println("altname = " + altname);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (WolfSSLJNIException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
} finally {
|
||||
if (WolfSSL.getLibVersionHex() >= 0x05003000) {
|
||||
if (peerCrtPtr != 0) {
|
||||
WolfSSLCertificate.freeX509(peerCrtPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end Server */
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd ./examples/build
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../lib/:/usr/local/lib
|
||||
java -classpath ../../lib/wolfssl.jar:./ -Dsun.boot.library.path=../../lib/ SimpleThreadedServer $@
|
||||
|
Loading…
Reference in New Issue