1

I'm working on a server in a distributed application that has browser clients and also participates in server-to-server communication with a 3rd party. My server has a CA-signed certificate to let my clients connect using TLS (SSL) communication using HTTP/S and XMPP(secure). That's all working fine.

Now I need to securely connect to a 3rd party server. In this communication, my server acts as client and I've a client certificate signed by the 3rd party.

The issue is when I tried adding the client certificates that I got from the 3rd party server and using the standard system configuration (-Djavax.net.ssl.keyStore=xyz). I get a "No X.509 certificate for client authentication" error in the debug log for SSL.

javax.net.ssl|FINE|01|main|2021-09-15 02:07:40.538 IST|CertificateMessage.java:297|No X.509 certificate for client authentication, use empty Certificate message instead
javax.net.ssl|FINE|01|main|2021-09-15 02:07:40.538 IST|CertificateMessage.java:328|Produced client Certificate handshake message (
"Certificates": <empty list>
)

But when I use a custom SSL context with the same keystore, it works fine. Following is the code for the same.

                String keystorePath = "keystorePath";
                String keystorePassword = "changeit";

                // Make a KeyStore from the JKS file
                KeyStore ks = null;
                try {
                    ks = KeyStore.getInstance("JKS");
                } catch (KeyStoreException e) {
                    e.printStackTrace();
                }
                try (
                    FileInputStream fis = new FileInputStream(keystorePath)) {
                    ks.load(fis, keystorePassword.toCharArray());
                }

                TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                tmf.init(ks);

                // Make a KeyManagerFactory from the KeyStore
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                kmf.init(ks, keystorePassword.toCharArray());

                // Get hold of the default trust manager
                X509TrustManager x509Tm = null;
                for (TrustManager tm : tmf.getTrustManagers()) {
                    if (tm instanceof X509TrustManager) {
                        x509Tm = (X509TrustManager) tm;
                        break;
                    }
                }

                // Now make an SSL Context with our Key Manager and the default Trust Manager
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(kmf.getKeyManagers(), null, null);
                return okHttpClient.newBuilder()
                        .sslSocketFactory(sslContext.getSocketFactory(), x509Tm)
                        .connectTimeout(10000, TimeUnit.MILLISECONDS)
                        .readTimeout(10000, TimeUnit.MILLISECONDS)
                        .build();

I want to use only the standard system configuration for keystore and not the custom SSL context.

Anuj Kumar
  • 11
  • 1
  • 4

1 Answers1

1

I had the same issue when the server's truststore was incorrect. Prior to the above SSL client logs were 'No X.509 cert selected for RSA' and 'No available authentication scheme'. Prior to that was the biggest clue - "certificate authorities" in the CertificateRequest contained only the CN for the server's cert - not the server's CN for its CA.

See also why doesn't java send the client certificate during SSL handshake?