A HTTPS client and HTTPS server demo in Java

  Pi Ke        2015-10-23 09:04:36       70,789        5    

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.

JAVA  SSL  DEMO  HTTPS 

       

  RELATED


  5 COMMENTS


shashi kumar [Reply]@ 2017-07-05 04:35:20

Thanks for the info, It helped me .

Anonymous [Reply]@ 2019-01-15 15:56:01

I get javax.net.ssl.SSLHandshakeException: no cipher suites in common with this.

I tried this code to generate SSL key file

        try{
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(null,null);

            keyStore.store(new FileOutputStream("test.jks"), "passphrase".toCharArray());
        }catch(Exception ex){
            ex.printStackTrace();
        }
Ke Pi [Reply]@ 2019-01-17 09:11:30

This error means no suitable cipher suite can be used to do handshake. This can be caused by many reasons.

Please check java.security file to ensure that the algorithms are not disabled for the keys/certificates generated in test.jks. Typical cases are that weak algorithms like(RC4) are used and are not allowed.

Anonymous [Reply]@ 2022-03-10 09:12:06

Getting below wxception:

java.io.FileNotFoundException: test.jks (The system cannot find the file specified)

Ke Pi [Reply]@ 2022-04-04 05:08:08

do u have the test.jks? this is just an example, you should create ur own keystore.



  RANDOM FUN

Hello world