In this post, I will create a HTTPS server and HTTPS client demo which can establish HTTPS communication between a server and a client using Java. This should be very useful when we want to test our understanding about SSL communication. We will use both a detailed SSL client and a simple HttpsURLConnection as the HTTPS client.
Before creating the actual HTTPS server and HTTPS client, we first need to generate the keystore and truststore to be used by the server and client. To generate the keystore, we can follow instructions at Different types of keystore in Java -- JKS. In this demo, we will use the generated keystore as both the keystore and truststore. In real applications, keystore and truststore should be separate files.
HTTPS server
Now we create the HTTPS server. Below is the code snippet.
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.security.KeyStore; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; public class HTTPSServer { private int port = 9999; private boolean isServerDone = false; public static void main(String[] args){ HTTPSServer server = new HTTPSServer(); server.run(); } HTTPSServer(){ } HTTPSServer(int port){ this.port = port; } // Create the and initialize the SSLContext private SSLContext createSSLContext(){ try{ KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream("test.jks"),"passphrase".toCharArray()); // Create key manager KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, "passphrase".toCharArray()); KeyManager[] km = keyManagerFactory.getKeyManagers(); // Create trust manager TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); trustManagerFactory.init(keyStore); TrustManager[] tm = trustManagerFactory.getTrustManagers(); // Initialize SSLContext SSLContext sslContext = SSLContext.getInstance("TLSv1"); sslContext.init(km, tm, null); return sslContext; } catch (Exception ex){ ex.printStackTrace(); } return null; } // Start to run the server public void run(){ SSLContext sslContext = this.createSSLContext(); try{ // Create server socket factory SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); // Create server socket SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(this.port); System.out.println("SSL server started"); while(!isServerDone){ SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); // Start the server thread new ServerThread(sslSocket).start(); } } catch (Exception ex){ ex.printStackTrace(); } } // Thread handling the socket from client static class ServerThread extends Thread { private SSLSocket sslSocket = null; ServerThread(SSLSocket sslSocket){ this.sslSocket = sslSocket; } public void run(){ sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); try{ // Start handshake sslSocket.startHandshake(); // Get session after the connection is established SSLSession sslSession = sslSocket.getSession(); System.out.println("SSLSession :"); System.out.println("\tProtocol : "+sslSession.getProtocol()); System.out.println("\tCipher suite : "+sslSession.getCipherSuite()); // Start handling application content InputStream inputStream = sslSocket.getInputStream(); OutputStream outputStream = sslSocket.getOutputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream)); String line = null; while((line = bufferedReader.readLine()) != null){ System.out.println("Inut : "+line); if(line.trim().isEmpty()){ break; } } // Write data printWriter.print("HTTP/1.1 200\r\n"); printWriter.flush(); sslSocket.close(); } catch (Exception ex) { ex.printStackTrace(); } } } }
It will first setup the SSL server with the correct keystore and truststore configured. then it will start to listen to client connection. Once a client connection is received, a new ServerThread is spawned to handle the client connection.
In the ServerThread, it will first start to perform the handshake with the client and once the handshake is completed successfully, application data can be exchanged between server and client. When the server receives the application data from client, it will send a response code of 200(OK) to the client.
HTTPS client
The HTTPS client is similar to the HTTPS server created above,
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.security.KeyStore; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; public class HTTPSClient { private String host = "127.0.0.1"; private int port = 9999; public static void main(String[] args){ HTTPSClient client = new HTTPSClient(); client.run(); } HTTPSClient(){ } HTTPSClient(String host, int port){ this.host = host; this.port = port; } // Create the and initialize the SSLContext private SSLContext createSSLContext(){ try{ KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream("test.jks"),"passphrase".toCharArray()); // Create key manager KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, "passphrase".toCharArray()); KeyManager[] km = keyManagerFactory.getKeyManagers(); // Create trust manager TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); trustManagerFactory.init(keyStore); TrustManager[] tm = trustManagerFactory.getTrustManagers(); // Initialize SSLContext SSLContext sslContext = SSLContext.getInstance("TLSv1"); sslContext.init(km, tm, null); return sslContext; } catch (Exception ex){ ex.printStackTrace(); } return null; } // Start to run the server public void run(){ SSLContext sslContext = this.createSSLContext(); try{ // Create socket factory SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); // Create socket SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(this.host, this.port); System.out.println("SSL client started"); new ClientThread(sslSocket).start(); } catch (Exception ex){ ex.printStackTrace(); } } // Thread handling the socket to server static class ClientThread extends Thread { private SSLSocket sslSocket = null; ClientThread(SSLSocket sslSocket){ this.sslSocket = sslSocket; } public void run(){ sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); try{ // Start handshake sslSocket.startHandshake(); // Get session after the connection is established SSLSession sslSession = sslSocket.getSession(); System.out.println("SSLSession :"); System.out.println("\tProtocol : "+sslSession.getProtocol()); System.out.println("\tCipher suite : "+sslSession.getCipherSuite()); // Start handling application content InputStream inputStream = sslSocket.getInputStream(); OutputStream outputStream = sslSocket.getOutputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream)); // Write data printWriter.println("Hello server"); printWriter.println(); printWriter.flush(); String line = null; while((line = bufferedReader.readLine()) != null){ System.out.println("Inut : "+line); if(line.trim().equals("HTTP/1.1 200\r\n")){ break; } } sslSocket.close(); } catch (Exception ex) { ex.printStackTrace(); } } } }
It will first setup the SSL client with the correct keystore and truststore. Then it will connect to the server and a new ClientThread will be spawned to handle the SSL connection.
In the ClientThread, the first thing is to enable the cipher suites you want to use. And then the client starts to start the handshake and send the application data once the handshake completes.
Another HTTPS client
Here is another HTTPS client which just uses a simple HttpsURLConnection to encapsulate all the details such as handshake.
import java.net.URL; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; public class HTTPSClient { // Disable the hostname verification for demo purpose static { HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); } public static void main(String[] args){ // Initialize configuration System.setProperty("javax.net.ssl.trustStore", "test.jks"); System.setProperty("javax.net.ssl.trustStoreType", "jks"); try{ URL url = new URL("https://127.0.0.1:9999"); HttpsURLConnection client = (HttpsURLConnection) url.openConnection(); System.out.println("RETURN : "+client.getResponseCode()); } catch (Exception ex){ ex.printStackTrace(); } } }
Here for demonstration purpose, we manually disabled the hostname verification. In real application, this should be handled with caution as this brings potential security breach.
Also, we use the system properties to configure the truststore used by the client to verify the server certificate.
The above demonstrate a simple but complete HTTPS server and client application. You can get more knowledge of the SSL connection model in Java from Oracle.
Thanks for the info, It helped me .