javasslcertificateserversocketsslsocketfactory

getLocalCertificates() return null


I'm trying to write a simple ssl server socket and client socket program. first i get some real certificate and then i used the first code to generate keystore for it. then i wrote server and client code so my server use that keystore as server certificate but no certificate sent during connection and session.getLocalCertificates() only return null my keystore generating code:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.security.KeyStore;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

public class MainClass {
    public static void main(String args[]) throws Exception {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        List mylist = new ArrayList();

            FileInputStream in = new FileInputStream("C:\\Users\\nima\\Downloads\\72315359_example.com.cert");
            Certificate cr = cf.generateCertificate(in);
            mylist.add(cr);

        CertPath cp = cf.generateCertPath(mylist);
        System.out.println(cp);

        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(null, null);
        List cplist = cp.getCertificates();
        Object[] o = cplist.toArray();
        for (int i = 0; i < o.length; i++) {
            X509Certificate c = (X509Certificate) o[i];
            ks.setCertificateEntry("my" + i, c);
        }
        FileOutputStream output = new FileOutputStream("C:\\Users\\nima\\Downloads\\test.dat");
        ks.store(output, "mypass".toCharArray());
        output.close();

    }
}

my server code:

import java.io.PrintStream;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

public class MainClass {
    public static void main(String args[]) throws Exception {
        System.setProperty("javax.net.ssl.keyStore", "C:\\Users\\nima\\Downloads\\test.dat");
        System.setProperty("javax.net.ssl.keyStorePassword", "mypass");

        SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
        ServerSocket ss = ssf.createServerSocket(5432);
        while (true) {
            Socket s = ss.accept();
            SSLSession session = ((SSLSocket) s).getSession();
            Certificate[] cchain2 = session.getLocalCertificates();
            for (int i = 0; i < cchain2.length; i++) {
                System.out.println(((X509Certificate) cchain2[i]).getSubjectDN());
            }
            System.out.println("Peer host is " + session.getPeerHost());
            System.out.println("Cipher is " + session.getCipherSuite());
            System.out.println("Protocol is " + session.getProtocol());
            System.out.println("ID is " + new BigInteger(session.getId()));
            System.out.println("Session created in " + session.getCreationTime());
            System.out.println("Session accessed in " + session.getLastAccessedTime());

            PrintStream out = new PrintStream(s.getOutputStream());
            out.println("Hi");
            out.close();
            s.close();
        }

    }
}

my client code:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.Socket;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class MainClass {
    public static void main(String args[]) throws Exception {
        System.setProperty("javax.net.ssl.trustStore", "C:\\Users\\nima\\Downloads\\test.dat");

        SSLSocketFactory ssf = (SSLSocketFactory) SSLSocketFactory.getDefault();
        Socket s = ssf.createSocket("127.0.0.1", 5432);

        SSLSession session = ((SSLSocket) s).getSession();
        Certificate[] cchain = session.getPeerCertificates();
        System.out.println("The Certificates used by peer");
        for (int i = 0; i < cchain.length; i++) {
            System.out.println(((X509Certificate) cchain[i]).getSubjectDN());
        }
        System.out.println("Peer host is " + session.getPeerHost());
        System.out.println("Cipher is " + session.getCipherSuite());
        System.out.println("Protocol is " + session.getProtocol());
        System.out.println("ID is " + new BigInteger(session.getId()));
        System.out.println("Session created in " + session.getCreationTime());
        System.out.println("Session accessed in " + session.getLastAccessedTime());

        BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String x = in.readLine();
        System.out.println(x);
        in.close();

    }
}

my server code is from here and in both server and client side i get null from getLocalCertificates. It's also possible that all of my code would be wrong.


Solution

  • (From comments, expanded slightly)

    If SSLSocket.getSession() is called before the handshake is done, it tries to do it, but if the handshake fails getSession() swallows the exception and just returns a 'nullSession' object which doesn't have the data a real session object would. Either (1) set sysprop javax.net.debug=ssl (at startup, usually on commandline with -D) and look at what it logs to see what is wrong with your handshake or (2) explicitly call ((SSLSocket)s).startHandshake() (which in spite of the name actually runs the handshake to completion, successful or not) and see what it throws.

    In particular if you use the keystore file created by the first code shown, that contains only a certificate (trustedCertEntry) NOT a privateKeyEntry (with both privatekey and certificate/chain) as is necessary and required for an SSL/TLS server to use any of the common and default-enabled suites; this error will usually manifest, somewhat confusingly, as a handshake failure with 'no cipher[suite] in common' or 'no cipher[suite] overlap'.

    That second part (SSL/TLS server without a privatekey doesn't work) has been asked and answered many times, but I don't recall any dupes that also include the 'getSession masks the error' part.