0

I have a java test app that makes a Restful SSL connection/call to a remote server but same code run from servlet under Tomcat returns Bad_Certificate error.

Test app version successfully connects to remote after referencing my keyStore and trustStore that I specify through System.setProperty("javax.net.ssl....? statements for file locations and passwords. This test program is a stand-alone simple class with a main method that successfully establishes connection and gets a response. However, if I move this same code to my servlet that runs on Tomcat I get a "SSLHandshakeException" Bad_Certificate error. And ssl:all:verbose debug trace doesn't provide any help because it appears that it's not getting to the handshake stage. I can recreate this error on my test app only if I don't set the System Properties pointing to the certificates. It appears that setting System.setProperty is being overridden when running from Tomcat. I also tried putting locations in web.xml but that didn't have any affect either.

My code is not readily available because it is exists behind a firewall that won't allow me to copy anything. But I'll fat-finger anything in that you want to verify.

OS: Redhat 7.4 Tomcat: 7.0.76 Java: JDK 10.0.2

javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
java.base/sun.security.ssl.Alerts.getSSLException(Alerts.java 198)
java.base/sun.security.ssl.Alerts.getSSLException(Alerts.java 159)
java.base/sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2046)
java.base/sun.security.ssl.SSLSocketImpl.processInputRecord(SSLSocketImpl.java:1207)
java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1074)
java.base/sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
java.base/sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1402)
java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1429)
java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)   
java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1581)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527)
java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:329)

Since adding debug to JAVA_OPTS in Tomcat configuration file, I'm getting more detail. Prior to the stacktrace that I provided yesterday I'm seeing the handshake now. And the last part of the handshake looks as follows:

update handshake state: finished
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished [20]
ajp-bio-8009-exec-1, WRITE TLSv1.2 Handshake, length = 32
ajp-bio-8009-exec-1, READ TLSv1.2 Alert, length = 2
ajp-bio-8009-exec-1, RECV TLSv1.2 ALERT: fatal, bad certificate
%%invalidated: [Session-3 TLS_RSA_WITH_AES_128_CBC_SHA]
ajp-bio-8009-exec-1, call closeSocket()

But I'm not sure what to do about this.

bbboomer54
  • 35
  • 1
  • 10
  • https://stackoverflow.com/questions/21833732/configure-truststore-in-tomcat – Ihor Herasymenko Oct 15 '19 at 18:45
  • 1
    Where in the servlet do you do the setting? The javax.net.ssl sysprops are read only when the JSSE classes are first loaded, which will happen at Tomcat startup if there is an HTTPS connector using JSSE (i.e. not APR, or in 8.5+ OpenSSL-provider), or if any other code in the same server uses SSL. Whereas your standalone program doesn't do anything else before running your code. If you can't get your code to run early enough, try -Dprop-value in CATALINA_OPTS or maybe JSSE_OPTS. Or instead of using the default context, create an explicit context and give its factory to HttpsURLConnection. ... – dave_thompson_085 Oct 16 '19 at 00:42
  • ... @IhorHerasymenko: this is not a truststore problem. The truststore is fine, because if our end had not validated the remote's cert the handshake would have been aborted before the remote had a chance to object to our cert (or more likely its absence). – dave_thompson_085 Oct 16 '19 at 00:44
  • In the servlet I put it in at the startup and initialization of servlet. Since submitting this request, I put it in Tomcat config file "tomcat.conf" and commented out all settings in servlet. In the config file I included them in JAVA_OPTS. It is clearly picking up both stores but still getting error. – bbboomer54 Oct 16 '19 at 12:05
  • See added stack trace details above. – bbboomer54 Oct 16 '19 at 12:45
  • Since you've got a javax.net.debug log, look a few screenfuls before the part you showed and you should see `*** CertificateRequest` (from the server) and some details then `*** ServerHelloDone` followed by a line `matching alias: ` then `*** Certificate chain` and your cert chain. Are those all correct? If not, look a good bit earlier and you should see a section with `init keystore` and `found key for : ` followed by your cert chain; are _those_ correct? – dave_thompson_085 Oct 16 '19 at 17:03
  • It would be best if you configured your TLS client appropriately by setting the trust store and key store only for that connection, instead of playing games with the `javax.net.ssl.[foo]` system properties. You can set an `SSLSocketFactory` on an `HttpsURLConnection`, and you can create your own `SSLSocketFactory` with arbitray configuration using code like I have written in my [SSLUtils class](https://github.com/ChristopherSchultz/ssltest/blob/master/src/java/net/christopherschultz/ssltest/SSLUtils.java). – Christopher Schultz Oct 18 '19 at 19:43

0 Answers0