Skip to content

Instantly share code, notes, and snippets.

@gvisoc
Last active November 7, 2023 05:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gvisoc/b33fa85c52a945db4145a00473f5cdda to your computer and use it in GitHub Desktop.
Save gvisoc/b33fa85c52a945db4145a00473f5cdda to your computer and use it in GitHub Desktop.
2-Way SSL: HTTPs Client

What is this

This Gist is about using Client Certificates in Java when making HTTPs calls with plain Java code (java(x).net, no other frameworks) in a 2-way SSL context, that is: the Server trusts and uses a Client Public Key to establish the secure connection. The example is a Java runnable class for a Client that uses a Certificate to establish a Two Way SSL communication with a Server.

Disclaimer: this gist reflects my understanding, and is not necessarily 100% accurate. Feedback is appreciated.

Context

For this to work, the Client certificate has to be issued by a CA (Certification Authority) trusted by the Server, the same way that the Client has to trust the CA of the Server. In Java, "trust a CA" means having that CA stored in an encrypted file called "trust store" accessible from our code by the use of a password and the appropriate security SDK provided by the JDK.

How to store that trust stores' passwords securely in our enviroments is another story.

In most of the cases where both systems belong to the same corporation, both Server and Client certificates are issued by the same CA, but it is better to think about two CA (one for each), regardless if it was the same.

In ths example, only the Client side is shown.

Running the example from the command line

To run this example we need to pack the CA from Server in to a .jks (Java Key Store) trust store for the client. That is done by running the following command:

keytool -import -keystore ./client-truststore.jks -file /route/to/server/ca/ca.crt -alias serverca

To run, make those resources available to the code by setting the following javax.net.* properties in JVM arguments:

-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=client_0.p12
-Djavax.net.ssl.trustStore=client-truststore.jks
-Djavax.net.debug=ssl
-Djavax.net.ssl.keyStorePassword=changeme
-Djavax.net.ssl.trustStorePassword=changeme

The property -Djavax.net.debug=ssl makes the console output (System.out) extremely verbose, so omit if it necessary.

Two Way SSL from 50,000 ft above

  1. The client_0.p12 is the Client Certificate issued by its CA, but only the Public Key would be sent to the Server.
  2. The Server has to send the identificator of the CA that issued the Client Certificate to the Client. That is to say: in the initial handshakes, the Server sends the Client the set of CA's it trusts. If the Client cannot send a Public Key of a Certificate issued by any of those CA, the communication won't take place.
  3. The same way, the Client has to trust the CA of the Server certificate in order to accept the Server's Public Key. If the Client doesn't trust the Server CA, the communication cannot take place.
  4. The ca.crt of the example holds the CA of the Server, for this Client to trust the Server Certificate. The corresponding truststore of the Server (not shown in this gist) has to hold the CA from the client for it to send in the initial handshakes.
  5. When the handshakes are made (basically, if the Server trusts the Client CA and the client trusted the Server CA), the communication is secure and both can encrypt / decrypt traffic as each party has access to the Public Key of each other. Remember that in asyncronous keys scenarios, we encrypt with the public key of the receiver, who decrypts the messages with is own private key.
  6. You can change the alias from serverca to whatever you need.
package snippets.ssl;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
/**
* Application to hit a secure endpoint
* Read the documentation to find out where to get the jks's and certificates and etc.
*/
public class TwoWaySSLClient
{
public static void main( String[] args )
{
try{
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://localhost:8086/secure/endpoint");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String string = null;
while ((string = bufferedreader.readLine()) != null) {
System.out.println("Received " + string);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment