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.
Advertisement
Answer
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.