2
votes

I am confused as to where exactly I need to include client certificate. Now, my first issue is that I don't trust the server. I tried using default Java keystore file (cacerts) which has both Thawte and Digicert in it, and those are the root authorities of the server I'm trying to communicate with. I set that cacerts file as keystore using System.setProperty("javax.net.ssl.keyStore", "..."), it didn't work, I set it as truststore, it didn't work. I still got

unable to find valid certification path to requested target

So I sorted that out temporarily by using AlwaysTrustSSLConnectionFactory().

Now the issue is that the server doesn't trust me. I have a client certificate and I tried adding it to both keystore and truststore, but regardless of what I do, after ServerHelloDone I get

Warning: no suitable certificate found - continuing without client authentication

in Java's SSL debug messages and a handshake failure after secret and key messages. Here is the end of my log:

http-bio-8080-exec-3, WRITE: TLSv1.2 Handshake, length = 40
http-bio-8080-exec-3, READ: TLSv1.2 Alert, length = 2
http-bio-8080-exec-3, RECV TLSv1.2 ALERT:  fatal, handshake_failure
%% Invalidated:  [Session-7, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]
http-bio-8080-exec-3, called closeSocket()
http-bio-8080-exec-3, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.recvAlert(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(Unknown Source)

Here is my current code:

System.setProperty("javax.net.ssl.keyStore", "C:/Users/Lovro/Desktop/certs/api/keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "pass");

URL url = new URL(urlRequest);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(new AlwaysTrustSSLContextFactory());
conn.connect();

I recieved my client certificate from API developers in format .p12, so I converted it to .crt and added that to keystore with Keytool. Does anyone know what could be the issue and is my handshake failing because I have not included client certificate properly or if I didn't add it to keystore properly? As far as I understand, truststore needs to contain public keys of trusted root authorities and keystore should have my client certificate. Is this correct? How do I achieve this?

Any suggestion is welcome, I am new to TLS/HTTPS and have no idea what am I doing.

2
I forgot to say that I tried using client certificate in Postman and it works. The certificate is valid.lovrodoe
no suitable certificate found - continuing without client authentication means that the client could not found a matching certificate in the keystore for the CA certificate list sent by server. Then the server probably shutdown the connection because the client certificate has not been sent. Make sure that all certificates and the private key of the .p12 are actually imported into the JKS, or set the .p12 directly as the keystore in javax.net.ssl.keyStore. If p12 does not work either, get the CA list sent by the server and compare it to the issuer of the p12 certificatepedrofb
Thanks Pedro, the issue was that I imported .crt to my keystore created from the original .p12 client certificate, so I effectively only added the public key to my store. Rookie mistake.lovrodoe

2 Answers

3
votes

I recieved my client certificate from API developers in format .p12, so I converted it to .crt and added that to keystore with Keytool

This is where you went wrong. Converting it to .crt extracts the public certificate. What you need to do is convert the .p12 file to a java keystore. There are many examples on the net. See this SO answer for how.

To confirm that it's worked, run keytool -list -keystore <yourkeystore>.jks and check that you have a PrivateKeyEntry in there.

While you're checking things, add the -v flag to the keytool -list command and check that the Issuer field is CN=test, O=test because we can see from your log file that your server requires a client certificate to be issued by that authority.

Also check that your JDK is configured with the Unlimited Strength Jurisdiction Policy Files because the cipher you're being asked to use requires it.

-1
votes

From the log, it seems that TLS cipher TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 is invalidated for TLS client. You may need to check which cipher list client supports. If the cipher TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 is not included in the cipher list, you may need to add support for it.

http-bio-8080-exec-3, READ: TLSv1.2 Alert, length = 2
http-bio-8080-exec-3, RECV TLSv1.2 ALERT: fatal, handshake_failure
%% Invalidated: [Session-7, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]