How to create a Server which accepts Client Certificate in Java


When you need to create a server which is requiring client certificate, you can use below code for that.

package testserver;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsExchange;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;

public class TestServer {

    /**
     * @param args the command line arguments
     */

    final static String Server_Password = "password";
    final static String keystore = "D://NetbeansProjects/TestServer/src/testserver/keys/server.jks";
    final static String truststore = "D://NetbeansProjects/TestServer/src/testserver/keys/servertrust.jks";
    public static HttpsServer server;

    public static void main(String[] args)throws Exception{
        // TODO code application logic here;
            HttpsServer server = makeNewServer();
            server.start();
            System.out.println("Server is running.\n"+server.getAddress().getHostName()); System.in.read();
            server.stop(0);
       
    }
       public static HttpsServer makeNewServer() throws Exception {
        server = HttpsServer.create(new InetSocketAddress(8000), 0);

        SSLContext sslCon = createSSLContext();
        ServerConfigurator conf = new ServerConfigurator(sslCon);
        server.setHttpsConfigurator(conf);

        server.createContext("/auth", new ServerHandler());
        return server;
    }
    private static SSLContext createSSLContext()  {
        SSLContext sslContext = null;
        KeyStore ks;
        KeyStore ts;

        try{
            sslContext = SSLContext.getInstance("TLSv1.2");

            ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream(keystore), Server_Password.toCharArray());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, Server_Password.toCharArray());

            ts = KeyStore.getInstance("JKS");
            ts.load(new FileInputStream(truststore), Server_Password.toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ts);

            sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

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

class ServerConfigurator extends HttpsConfigurator {
    public ServerConfigurator(SSLContext sslContext) {
        super(sslContext);  }

    @Override
    public  void configure(HttpsParameters params) {
        SSLContext sslContext = getSSLContext();
        SSLParameters  sslParams = sslContext.getDefaultSSLParameters();
        sslParams.setNeedClientAuth(true);
        params.setNeedClientAuth(true);
        params.setSSLParameters(sslParams);
    }
}

class ServerHandler implements HttpHandler {
    public void handle(HttpExchange t) throws IOException {
        HttpsExchange ts = (HttpsExchange) t;
        SSLSession sess = ts.getSSLSession();
        System.out.printf("Responding to host: %s\n",sess.getPeerHost());

        t.getResponseHeaders().set("Content-Type", "text/plain");
        t.sendResponseHeaders(200,0);
        String response = "Hello!  I am a trusted server!\n";
        OutputStream os = t.getResponseBody();
        os.write(response.getBytes());
        os.close();
    }

}

Then you need to create Keystore and Truststore for your server.

What is the Keystore?

Keystore is used to store private key and own certificate which should present to other party for verifying own identity.

What is Truststore?

Truststore is used to save trusted CA certificates and public certificates of other parties. In java, cacerts is the Truststore and it is located at $JAVA_HOME/lib/security/cacerts.

Here, we are going to create self signed certificate which is signed by itself rather than trusted CA.
We can use java keytool to do this.

Create Server java key store
------------------------------------
keytool -genkey -alias server -keyalg RSA -keystore server.jks -validity 365 -dname "cn=$LOCALNAME, ou=Cert, o=Cert, c=CA" -storepass $PASSEORD -keypass $PASSWORD

Create client java key store
----------------------------------
keytool -genkey -alias MyClient -keyalg RSA -keystore MyClient.jks -validity 365 -dname "cn=$LOCALNAME, ou=Cert, o=Cert, c=CA" -storepass $PASS -keypass $PASS

Export Server certificate
-------------------------------
keytool -export -file server.cert -keystore server.jks -storepass $PASSWORD -alias server

Export Client certificate
--------------------------------
keytool -export -file MyClient.cert -keystore MyClient.jks -storepass $PASSWORD -alias MyClient

Import Server certificate to Client trust store
----------------------------------------------------------
keytool -import -file server.cert -alias server -keystore clienttrust.jks -storepass $PASSWORD 

Import Client certificate to Server trust store
----------------------------------------------------------
keytool -import -file MyClient.cert -alias MyClient -keystore servertrust.jks -storepass $PASSWORD 

Important Points
  • When you are executing keytool commands in windows command prompt, command prompt should be opened as an administrator
  • You should create your keystore at $JAVA_HOME/bin.
  • It is good to use same password for keystore and keys to avoid unnecessary error
Exception Handling
  • javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching ssl.someUrl.de found -                                                                                     
Reason: When an HTTPS Client connects to a server, it verifies that the hostname in the certificate matches the hostname of the server. Hence, CN should be the hostname of the server that you are going to connect.
  • Exception while sending data Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target   Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target                                                        
Resolution:  Make sure you have imported the public certificate of the target instance into the Truststore 
  • Exception while sending data Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake Caused by: java.io.EOFException: SSL peer shut down incorrectly                                                                                                                
Reason:  Protocol version error.  Check TLS version EX: sslContext = SSLContext.getInstance("TLSv1.2"); or in my case I have set  sslParams.setNeedClientAuth(true);   params.setNeedClientAuth(true);  in my server code. But,  I have tested this without importing client certificate to server truststore. 

Comments

  1. Thank you for the great post.
    Prancer is a pre-deployment and post-deployment multi-cloud validation framework for your Infrastructure as Code (IaC) pipeline and continuous compliance in the cloud.
    https://www.prancer.io/

    ReplyDelete

Post a Comment

Popular posts from this blog

How to add standard and custom Metadata to PDF using iTextSharp

How to set and get Metadata to image using BitmapMetadata class in C#

How to generate direct line token to start a new conversation between your own client application and bot framework in Java