Receiving SSLHandshakeException: handshake_failure despite my client ignoring all certs - java

I have a Java program that connects to a webserver using SSL/TLS, and sends various HTTP requests over that connection. The server is localhost and is using a self-signed cert, but my code is using custom TrustManagers, and ignores invalid certificates. It has worked perfectly until now.
The only difference on the server is that it used to run jboss 6 and is now running jboss 7. I'm not sure if this is a configuration issue, or whether there is a problem with my code, but I get the same errors if I try to connect using other Java-based programs like WebScarab or ZAP.
In any case, is there anything I can do to my code to get around this problem? Here is the error in full:
Received fatal alert: handshake_failure
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at com.sun.net.ssl.internal.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 are the debug messages before the failure:
main, WRITE: TLSv1 Handshake, length = 75
main, WRITE: SSLv2 client hello message, length = 101
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT: fatal, handshake_failure

So I found the problem. There might be a bug in Java, but the client seems to initiate a TLSv1 Handshake, but then sends an SSLv2 client hello message, at which point the server rejects the connection.
This happens even if you create your SSLContext with an instance of TLS:
SSLContext sslContext = SSLContext.getInstance("TLS");
The solution is to set a system property before any connection attempts are made:
System.setProperty("https.protocols", "TLSv1");
There are probably other solutions to it, but this one worked for me.

The info you provide is very little as well as your stack trace.
I'll take a guess here.
What I suspect is that in the new server the protocol is TLSv1 while your clients try to connect with SSLv3 (or less) and as a result the handshake fails.
Change you clients to use higher version of TLS
or
Make your webserver support SSLv3 as well. I know how to do this in Tomcat but not in JBoss.
If this doesn't work update the post with more info (and a full stack trace).
You should enable ssl debug info -Djavax.net.debug=ssl

Was this ever resolved?
I had the exact same problem, essentially I was receiving a handshake exception immediately following the clientHello. So The chain of events was
I would present my certificate to the server
Server would imediately respond with a handshake failure. (I would not even get a Server Hello back).
Eventually I found that the server was requiring a stronger encryption/decryption algorithm than what I Was supplying in the initial handshake phase (Ie. Client and Server could not agree on a mutual encryption algorithm to use for the ssl communication).
I need to install the Unlimited Java JCE (Java Cryptography Extension Policy). There are export rules on using this, so if you ship your code overseas that may have implications..however this is what solved my problem.
This link explains how to install the updated policies
http://suhothayan.blogspot.com/2012/05/how-to-install-java-cryptography.html
This was also a great link that helped me understand exactly what was going on
https://support.f5.com/kb/en-us/solutions/public/15000/200/sol15292.html#id
This may or may not be the issue, but when the handshake fails immediately after the client Hello, it looks like the client and the server can not agree on something (in many cases its the encryption algorithms that they will mutually need to communicate).

You are seeing this error most probably because the keystore that your JBoss 6 had access to is not accessible to your JBoss 7 instance.
What I would recommend is the following.
Your self-signed server certificate must be imported into a truststore
keytool -import -alias gridserver -file server.crt -storepass $YOUR_PASSWORD_HERE -keystore server.keystore
Add the following properties to your run.conf
-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=clientcertificate.p12
-Djavax.net.ssl.trustStore=server.keystore
-Djavax.net.debug=ssl # very verbose debug. Turn this off after everything looks good.
-Djavax.net.ssl.keyStorePassword=$YOUR_PASSWORD_HERE
-Djavax.net.ssl.trustStorePassword=$YOUR_PASSWORD_HERE

The stack trace is from you client code and your client 'Received [a] fatal alert'. In other words, the SSL error happened in Jboss, not your client.
Your client side custom TrustManagers have therefore nothing to do with it. My wild guess is that your new Jboss 7 is configured to require client certificate and your client did not present any.
To debug your SSL connection, use openssl and try this:
openssl s_client -connect jboss.server.com:443
or is it is an SSLV3 server
openssl s_client -connect jboss.server.com:443 -ssl3
This should print a lot of interesting information.

I think this is related to a Java 7 bug. It is hard to be sure without more details.

For me solution was : System.setProperty("https.protocols", "TLSv1.1,TLSv1.2");

Related

SSL Handshake Error using Java

I am trying to connect a java client with a JMX server using SSL certificate.
But unfortunately this connection gives a SSL Handshake error. When I tried to debug this, using -Djavax.net.debug=all flag, I get the following error message on the java client.
2016-07-15T13:29:50.02-0700 [APP/0] OUT RMI RenewClean-[10.200.0.27:44445,javax.rmi.ssl.SslRMIClientSocketFactory#305a99f7], READ: TLSv1.2 Alert, length = 2
2016-07-15T13:29:50.02-0700 [APP/0] OUT RMI RenewClean-[10.200.0.27:44445,javax.rmi.ssl.SslRMIClientSocketFactory#305a99f7], RECV TLSv1 ALERT: fatal, handshake_failure
2016-07-15T13:29:50.02-0700 [APP/0] OUT RMI RenewClean-[10.200.0.27:44445,javax.rmi.ssl.SslRMIClientSocketFactory#305a99f7], called closeSocket()
2016-07-15T13:29:50.02-0700 [APP/0] OUT RMI RenewClean-[10.200.0.27:44445,javax.rmi.ssl.SslRMIClientSocketFactory#305a99f7], handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
When I change the client to something else, like a JConsole, the connection works fine using the same SSL certificate and the truststore, which indicates that there is nothing wrong with the SSL certificate or the domain its connecting to.
It seems that the handshake was failing due to incorrect TLS version types.
My client was running on Java7 which was using TLS version 1, while the server was running Java8 which was using TLS version 1.2.
Check this oracle blog, which mentions that Java8 will default use TLS version 1.2
So, when I upgraded my client to use Java8, the issue got resolved.
Hope, this helps someone else as well.

sslv2 and sslv3 compatability or a cert issue?

I have a unix script running in server "CCC" from which am calling a servlet running in another server "GGG".(The server GGG is a secure server, having extra firewalls compared to normal server)
Also, the CCC is a ETL server, which is having a JRE. The script is calling the servlet using http, since I rule is applied, so redirection is happening to https, and the port no is also changing.
And am getting a bad certificate error, when i try to invoke the servlet.
The servlet URL when given in normal IE or chrome is giving me a valid response, am able to hit the server GGG.
The error am getting is :
javax.net.ssl.SSLHandshakeException: bad certificate
at com.ibm.jsse.bg.a(Unknown Source)
at com.ibm.jsse.b.a(Unknown Source)
at com.ibm.jsse.b.write(Unknown Source)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:139)
at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:827)
at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:1975)
at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:993)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:397)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:170)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324)
at com.tgt.task.client.TaskClient.doGet(TaskClient.java:153)
at com.tgt.task.client.TaskClient.runClient(TaskClient.java:91)
at com.tgt.task.client.TaskClient.completeTask(TaskClient.java:68)
at com.tgt.task.client.TaskClient.main(TaskClient.java:53)
So, initially i checked the certs in cacerts for CCC server, it is having all the root certs, except the cert issued to the URL.
We found the issue started after the BIG IP cert renewal for the URL, i have followed up with my network team to have a look into the issue, they have confirmed that traffic from server CCC is going through fine to GGG passing the firewalls.
So captured the traffic when we tried to invoke the URL, we got the below:
client server SSLV2 282 Client Hello
server client SSLV3 1631 Server Hello
server client SSLv3 433 Certificate
client server SSLV3 190 Alert(Level: Fatal, Description: Bad Certificate)
Can you let me know is the issue because of the SSL versions used by the client and the server ?
or some other thing that i missed out in my analysis.
I even tried to import the cert for the URL in to cacerts in server CCC, but i got the errors:
keytool error: java.lang.Exception: Input not an X.509 certificate
So , i took the PKCS7 cert for the URl and tried to convert it into .cer and tried to import, but got the same error.
If it works with the browser, but not with a script, it has often to do with Server Name Indication (SNI). With SNI you can have multiple hosts with different certificates behind the same IP address. While all current browser support SNI, other implementations might not or might not enable SNI by default. If the client fails to use SNI on a site with multiple certificates behind the same IP it just gets the default certificate, which is often the wrong one.
I don't which Java version you are running, but I suggest you determine your version and then google of how to use SNI with your version. From my understanding SNI was enabled only in Java version 7.
I have checked the captured network packet and found that the URL is providing the correct certificate to the server CCC as per the SSL configuartions at BIG IP end, the client CCC is not able to validate it. And returning a bad certificate message.
here i tried openssl and tried the command:
s_client -connect host:443
verify error:num=20:unable to get local issuer certificate
verify error:num=27:certificate not trusted
verify error:num=21:unable to verify the first certificate
`No client certificate CA names sent`
SSL handshake has read 1745 bytes and written 304 bytes
New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 1024 bit
Compression: NONE
Expansion: NONE
SSL-Session:
Start Time: 1402019174
Timeout : 300 (sec)
Verify return code: 21 (unable to verify the first certificate)
I guess my server is not sending the complete trust chains list, as a result, client is not able to validate it.
Am not sure whether my analysis is correct or not, also i referred the below post:
OpenSSL: unable to verify the first certificate for Experian URL
If i need to send my server to send the complete trusted chain list, where i need to change the settings ? at BIG Ip end, or at server end ?

Axis2 client is unable to initiate a second https call on Tomcat 7.0.42

I am trying to migrate an Java Axis2 client from Websphere6/JRE1.5.x to Tomcat 7.0.42/JRE1.6.x on Solaris. In the process, I have come across a issue where the client can communicate with a webservice through https the first time, but would fail from the second attempt onwards due to ssl errors.
Extracted from the log of the failed attempts showed -
%% Client cached [Session-1, SSL_RSA_WITH_RC4_128_MD5]
%% Try resuming [Session-1, SSL_RSA_WITH_RC4_128_MD5] from port 34092
** ClientHello, TLSv1
...
Thread-606, WRITE: TLSv1 Handshake, length = 179
Thread-606, READ: TLSv1 Alert, > length = 2
Thread-606, RECV TLSv1 ALERT: fatal, unexpected_message
and the actual exception was (I suspect it was due to no open socket)-
Caused by: javax.net.ssl.SSLException: Received fatal alert:
unexpected_message at
com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown
Source) at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown
Source) at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown
Source) at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.AppOutputStream.write(Unknown Source)
at java.io.BufferedOutputStream.flushBuffer(Unknown Source) at
java.io.BufferedOutputStream.write(Unknown Source) at
java.io.FilterOutputStream.write(Unknown Source) at
org.apache.commons.httpclient.WireLogOutputStream.write(WireLogOutputStream.java:86)
at
org.apache.axis2.transport.http.AxisRequestEntity.writeRequest(AxisRequestEntity.java:89)
Comparing this to the logs on the Websphere environment, I have found a couple of differences and hence my questions -
Is SSL configuration on the Tomcat going to affect the behaviour of a Axis2 client calling a different server? Or is my issue more at the OS level?
on the old environment, I have noticed more than one ssl sessions were cached. When resume failed on one session, it simply tried using a different session and continued with the call. On the new server, however, I have ever only seen one session created and was cached. When resuming, it kept trying to resume the same sessionn a different port but kept failing. As it was the only session, if couldn't try a different session nor creating a new session. My question is why is there only one session created and is it a configuration issue to enable caching of more than one sessions?
Any help/pointers are greatly appreciated.
Finally tracked down the fault. Thought I would share it here for anyone that may come across a similar issue.
As it turned out, it has to do with the Security Provider on the JRE itself. Here is a link on how to configure it -
http://pic.dhe.ibm.com/infocenter/ssp/v3r4/index.jsp?topic=%2Fcom.ibm.help.sspreleasenotes.doc%2FSSP_RN_RemovePKCS11SecurityProvider.html
It seemed the default security provider on Solaris was causing the weird behaviour I saw. Following the instruction in the link, and by using the Java default provider, resolved the ssl problem I saw.

error while mailing from Java (SSLHandShake Exception)

I am getting following exception while sending mails from Java.
31/03 14:06:19:571 INFO [ ] IBSUtils sendMailfromsmtp() MessagingException in Sending Mail :javax.mail.MessagingException: Can't send command to SMTP host;
nested exception is:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: timestamp check failed
It's sending mails in stand alone java programs but while running from JBoss server its throwing this exception. Code has been working from six months but suddenly its starts throwing this exception.
Seems some certificate Expired.
I've ever get this kind of error when my application(IBM Java 1.6) handle connection with webserver(Oracle Java 1.6). There might be Handshake exception while negotiating SSH protocol.
You'd better set your Java protocol manually.
// Using IBM jre there will be a handshake failure as IBM java 1.6 will
// negotiate to server SSLv3 protocol while it SHOULD be TLSv1
System.setProperty("https.protocols", "TLSv1");
Seems some certificate Expired.
Correct. The server certificate has expired. Get it renewed, or complain to them if you can't get it done yourself.
Or else it hasn't come into its validity period yet.
Answering your request on how to get get information about the probably expired certificate:
Find out which server your mail is delivered to. Where this is configured depends on you utility class for sending mail, JEE style is to get it from a mail session configured in jboss.
Assuming you deliver your mail to somehost on port 25 you could use openssl s_client to perform a TLS handshake to get the certificate:
openssl s_client -starttls smtp -crlf -connect somehost:25
This gives you the certificate itself (the base64 encoded part between the "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE----- " delimiters and some basic information abobout it. Store the certificate including the delimiting lines in a file, say cert.pem.
You can then decode it using
openssl x509 -text -noout <cert.pem
This gives you all the information in the certificate as readable text.

Import expired SSL certificate into Java keystore

I need to be able to point JMeter at a test server which has an expired SSL certificate (it will be some time before we are able to renew it). JMeter is quite rightly throwing an exception when it tries to connect:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
at sun.security.ssl.SSLSessionImpl.getPeerCertificates(Unknown Source)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:284)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:62)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1075)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1064)
at org.apache.jmeter.threads.JMeterThread.process_sampler(JMeterThread.java:426)
at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:255)
at java.lang.Thread.run(Unknown Source)
Does anybody know of any way that I can import said certificate into the keystore and in the process change the expiry date (and would this even help or would the fact that the server certificate has expired still cause this exception to be thrown)?
I've tried to set the validity as part of the import but this is ignored:
keytool -import ... -validity 100
P.S - I know I could implement my own TrustManager which ignores these checks but my fingers won't allow me to write such evil code and I'd much rather get to a solution that I can install on the server without having to modify JMeter!
Thanks for your time.
JMeter does not validate certificate so this is not the cause of your issue.
Which implementation do you use, java, HC3 or HC4 ?
Your issue could come from error in Socket version negotation.
Try setting this in user.properties:
https.socket.protocols=SSLv2Hello SSLv3 TLSv1
You may have to play with them depending on your server configuration, for example only set this:
https.socket.protocols=SSLv3

Categories

Resources