Senior Wurst Senior Wurst - 3 months ago 15
HTTP Question

Enabling old/unsafe Ciphers programmatically in Java

I am currently writing a wrapper for a REST API. The site has their SSL go through a cloudflare plan.
I am trying to make my calls to the api with OkHttp3, the problem here is that cloudflare apparently uses some old ciphers, that Java has disabled by default (in java.security).

When I try to make a POST request to the api I get this stacktrace:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:241)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:198)
at okhttp3.internal.connection.RealConnection.buildConnection(RealConnection.java:174)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:114)
at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:193)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:129)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:98)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:109)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:124)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:170)
at okhttp3.RealCall.execute(RealCall.java:60)
at de.dootdoot.dubtrack4k.requesttypes.AbstractRequest.request(AbstractRequest.kt:28)
at de.dootdoot.dubtrack4k.requesttypes.AbstractRequest.request(AbstractRequest.kt:23)
at de.dootdoot.dubtrack4k.HttpRequester.post(HttpRequester.kt:26)
at de.dootdoot.dubtrack4k.HttpRequester.request(HttpRequester.kt:14)
at de.dootdoot.dubtrack4k.HttpRequester.request$default(HttpRequester.kt:11)
at de.dootdoot.dubtrack4k.requests.LoginRequest.setURL(LoginRequest.kt:50)
at de.dootdoot.dubtrack4k.requests.LoginRequest.request(LoginRequest.kt:17)
at de.dootdoot.dubtrack4k.models.Account.login(Account.kt:10)
at de.dootdoot.dubtrack4k.Dubtrack.login(Dubtrack.kt:19)
at LoginTest.loginTest(LoginTest.kt:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
... 65 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:146)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 71 more


So I've been looking around a bit and found a way to enable SSL Debugging.
This resulted in this debug log (Hastebin because it won't go into a code block for some reason):

http://hastebin.com/palomijisa.dos

Close to the end of the log it says this:

%% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
main, SEND TLSv1.2 ALERT: fatal, description = certificate_unknown


Which made me google around some more. Apparently this is an old-ish ciphersuite that is disabled by default, which I could confirm, it is listed in the disabled algorithms section of java.security.
Now as I'm writing an API Wrapper it wouldn't make sense to go and edit this on my machine as it wouldn't allow anyone else to use it.
So now my question is, is there a way to that allow that certificate for just the session the wrapper is in?
I've looked around OkHttp3 a bit and indeed that ciphersuite is listed in their
CipherSuite
class, but I don't know how I would use that.
Does anyone have any experience with this kind of scenario?
Thanks in advance!

Answer

The issue is that your JVM doesn't trust StartCom as a root CA, so the certificate is rejected.

The cert is signed by

Subject: CN=StartCom Class 2 IV Server CA, OU=StartCom Certification Authority, O=StartCom Ltd., C=IL

This is the error in the debug log

main, SEND TLSv1.2 ALERT:  fatal, description = certificate_unknown

Download https://aia.startssl.com/certs/ca.crt once manually

You can either import it permanently into this JVM instance. It should be safe since chrome and OSX support it already.

https://www.ailis.de/~k/uploads/scripts/import-startssl

keytool -keystore C:\Program Files\Java\jdk1.8.0_73\jre\lib\security\cacerts -storepass changeit -import -trustcacerts -alias startcom -file ca.crt

Or alternatively package the certificate with your program and load it at runtime. This would be my suggested route.

https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java

https://github.com/yschimke/oksocial/blob/master/src/main/java/com/baulsupp/oksocial/security/CertificateUtils.java

Comments