TLS

Java

Openssl certificates

The certificates used in this section created by openssl were discussed in the section TL: General . They are

Generating keys using keytool

Java has long had its own formats for certificates known as Java Key Stores (JKS). Since Java 9 this has using PKCS #12. The Java terminology is

keytool
This is the application used to manipulate JKS files
Keystore
The certificates for a server are stored in a keystore
Truststore
Certificates relating to a server that need to be imported into a client are stored in a truststore

If you are working in Java only and using keytool alone, the steps are

Converting PEM certificates to JKS

We have created a set of certificates using openssl already, which are used by all the languages we discuss. So we need to convert them into the format required by Java.

To create a Java keystore from PEM certificates, follow the process in Import private key and certificate into java keystore , you need to first convert the PEM files to PKCS #12 using openssl and then import into a keystore using keytool.

A server wanting to use a keystore with our own CA will need the keystore built from the certificate signed by the CA:


      openssl pkcs12 -export -in ../certs/CA-signed-cert.pem \
                     -inkey ../certs/key.pem -CAfile ../certs/CA-cert.pem \
                     -name "localhost" -out CA-signed-cert.p12
	  
      keytool -importkeystore -deststorepass abcdefg \
              -destkeystore CA-signed-cert-keystore.jks \
              -srckeystore CA-signed-cert.p12 -srcstoretype PKCS12
  
Give the keystore a password such as "abcdefg" and trust the resultant certificate.

To build a truststore, we just need the certificate, not the private key. The process is described at ADD A CERTIFICATE TO A TRUSTSTORE USING KEYTOOL .

A client trusting our CA needs a truststore made from the CA's certificate. This becomes here


      keytool -import -alias localhost -file ../certs/CA-cert.pem \
              -storetype JKS -keystore CA.truststore.jks
  

Client talking to server with CA signed certificate

HTTP servers offering HTTPS are giving HTTP over TLS. Most will have valid TLS certificates, properly signed by a recognised CA. The client just needs to validate the certificate and can then talk.

Validation is done by the Java SSLSocket, without any extra work by the client. The client, talking to an HTTPS server, will use the HTTP protocol. The simplest messages is to send an HTTP Head request, which returns information about the server. (HHTP will be discussed in more detail later.)

A simple client talking to www.verisign.com is TLSGetHead.java:


/**
 * From https://docs.oracle.com/javase/10/security/sample-code-illustrating-secure-socket-connection-client-and-server.htm

 * Copyright © 1993, 2018, Oracle and/or its affiliates. All rights reserved.
 */

import java.net.*;
import java.io.*;
import javax.net.ssl.*;

/*
 * This example demostrates how to use a SSLSocket as client to
 * send a HTTP request and get response from an HTTPS server.
 * It assumes that the client is not behind a firewall
 */

public class TLSGetHead {

    public static void main(String[] args) throws Exception {
        try {
            SSLSocketFactory factory =
                (SSLSocketFactory)SSLSocketFactory.getDefault();
            SSLSocket socket =
	              (SSLSocket)factory.createSocket("www.verisign.com", 443);

            /*
             * send http request
             *
             * Before any application data is sent or received, the
             * SSL socket will do SSL handshaking first to set up
             * the security attributes.
             *
             * SSL handshaking can be initiated by either flushing data
             * down the pipe, or by starting the handshaking by hand.
             *
             * Handshaking is started manually in this example because
             * PrintWriter catches all IOExceptions (including
             * SSLExceptions), sets an internal error flag, and then
             * returns without rethrowing the exception.
             *
             * Unfortunately, this means any error messages are lost,
             * which caused lots of confusion for others using this
             * code.  The only way to tell there was an error is to call
             * PrintWriter.checkError().
             */
            socket.startHandshake();

            PrintWriter out = new PrintWriter(
                                  new BufferedWriter(
                                  new OutputStreamWriter(
                                  socket.getOutputStream())));

            out.print("HEAD / HTTP/1.0\r\n\r\n");
             out.flush();

            /*
             * Make sure there were no surprises
             */
            if (out.checkError())
                System.out.println(
                    "SSLSocketClient:  java.io.PrintWriter error");

            /* read response */
            BufferedReader in = new BufferedReader(
                                    new InputStreamReader(
                                    socket.getInputStream()));

            String inputLine;
            while ((inputLine = in.readLine()) != null)
                System.out.println(inputLine);

            in.close();
            out.close();
            socket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

When compiled and run by


      javac TLSGetHead.java
      java TLSGetHead
  
it produces something like

HTTP/1.1 200 OK
Date: Mon, 15 Jun 2020 10:59:59 GMT
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Content-Language: en-US
Transfer-Encoding: chunked
Set-Cookie: locale-name=en_US; Expires=Sun, 13-Sep-2020 10:59:59 GMT; Path=/
Set-Cookie: locale-name=en_US; Expires=Sun, 13-Sep-2020 10:59:59 GMT; Path=/
Set-Cookie: JSESSIONID=BF08615501E1518E02727B6B1DABD54E.brn2; Path=/; Secure; HttpOnly
Strict-Transport-Security: max-age=15768000;
Connection: close
  

TLS echo client

The client uses an SSLSocketFactory to create an SSLSocket. From then on, the code is essentially the same as the earlier TCP clients.

The code is TLSEchoClient.java:



import java.io.*;
import java.net.*;
import javax.net.ssl.*;

public class TLSEchoClient{

    public static final int MYECHOPORT = 1200;
    
    public static void main(String[] args){

	if (args.length != 1) {
	    System.err.println("Usage: Client address");
	    System.exit(1);
	}

	InetAddress address = null;
	try {
	    address = InetAddress.getByName(args[0]);
	} catch(UnknownHostException e) {
	    e.printStackTrace();
	    System.exit(2);
	}

	Socket sock = null;
	try {
	    sock = new Socket(address, MYECHOPORT);
	} catch(IOException e) {
	    e.printStackTrace();
	    System.exit(3);
	}
	SSLSocketFactory factory =
	    (SSLSocketFactory) SSLSocketFactory.getDefault();
	
	SSLSocket sslSocket = null;
	try {
	    sslSocket =
	    (SSLSocket) factory.createSocket(sock, args[0], MYECHOPORT, true);
	} catch(IOException e) {
	    e.printStackTrace();
	    System.exit(3);
	}
	
	BufferedReader reader = null;
	PrintStream out = null;

	try {
	    reader = new BufferedReader(new InputStreamReader(
                                    sslSocket.getInputStream()));
	    out = new PrintStream(sslSocket.getOutputStream());
	} catch(IOException e) {
	    e.printStackTrace();
	    System.exit(6);
	}

	BufferedReader consoleReader =  
                   new BufferedReader(new InputStreamReader(System.in));
	String line = null;
	while (true) {
	    line = null;
	    try {
		System.out.print("Enter line:");
		line = consoleReader.readLine();
		System.out.println("Read '" + line + "'");
	    } catch(IOException e) {
		e.printStackTrace();
		System.exit(6);
	    }
	    
	    if (line.equals("BYE"))
		break;
	    try {
		// Just send a goodbye message, for testing
		out.println(line);
		line = reader.readLine();
	    } catch(IOException e) {
		e.printStackTrace();
		System.exit(6);
	    }

	    System.out.println(line);
	}
	System.exit(0);
    }
} // Client

The difference comes at runtime, where the client has to be told where to pick up the trust keys. Run the client using the truststore


      java -Djavax.net.ssl.trustStore=CA.truststore \
           -Djavax.net.ssl.trustStorePassword="abcdefg" \
           TLSEchoClient localhost
  

TLS echo server

The server similarly needs to create an SSLSocket, and then continues like the other TCP servers.

The code is TLSEchoServer.java:


import java.io.*;
import java.net.*;
import javax.net.ssl.*;

public class TLSEchoServer {
    
    public static int MYECHOPORT = 1200;

    public static void main(String argv[]) {
	try {
	    SSLServerSocketFactory factory =
		(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
	    
	    SSLServerSocket sslSocket =
		(SSLServerSocket) factory.createServerSocket(MYECHOPORT);

	    while (true) {
		Socket incoming = sslSocket.accept();
		new SocketHandler(incoming).start();
	    }  
	} catch(IOException e) {
	    e.printStackTrace();
	    System.exit(30);
	}
    }
}

class SocketHandler extends Thread {

    Socket incoming;

    SocketHandler(Socket incoming) {
	this.incoming = incoming;
    }

    public void run() {

	byte[] data = new byte[1024];
	InputStream in;
	OutputStream out;
	try {
	    in = incoming.getInputStream();
	    out = incoming.getOutputStream();
	}  catch(IOException e) {
	    System.err.println(e.toString());
	    return;
	}
	int numRead = 0;

	try {
	    while (true) {
		try {
		    numRead = in.read(data);
		} catch(IOException e) {
		    System.err.println(e.toString());
		    return;
		}
		if (numRead == -1) {
		    System.out.println("Disconnecting");
		    return;
		}
		try {
		    out.write(data, 0, numRead);
		} catch(IOException e) {
		    System.err.println(e.toString());
		    return;
		}
	    }		
	} finally {
	    System.out.println("Closing all");
	    try {
		in.close();
		out.close();
		incoming.close();
	    } catch(IOException e) {
		// nothing to do really
	    }
	}
    }
}

The server needs to pick up a certificate from the keystore. Run the server using the keystore


      java -Djavax.net.ssl.keyStore=CA-signed-cert-keystore.jks \
           -Djavax.net.ssl.keyStorePassword="abcdefg"  \
           TLSEchoServer
  

For more complex cases see Sample Code Illustrating a Secure Socket Connection Between a Client and a Server

Resources


Copyright © Jan Newmarch, jan@newmarch.name
Creative Commons License
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia" by Jan Newmarch is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
Based on a work at https://jan.newmarch.name/NetworkProgramming/ .

If you like this book, please contribute using PayPal