1

How can I enable SNI extension in new Java 9 client? (Package jdk.incubator.http)

HttpClient client = HttpClient.newHttpClient();
client.send(
       HttpRequest
           .newBuilder(uri)
           .GET()
           .build(),
       HttpResponse.BodyHandler.asString());

But this request failed because of missing TLS SNI extension

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at java.base/sun.security.ssl.Alerts.getSSLException(Alerts.java:198)
at java.base/sun.security.ssl.Alerts.getSSLException(Alerts.java:159)
at java.base/sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1905)
at java.base/sun.security.ssl.SSLEngineImpl.processInputRecord(SSLEngineImpl.java:1140)
at java.base/sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:1020)
at java.base/sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:902)
at java.base/sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:680)
at java.base/javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:626)
at jdk.incubator.httpclient/jdk.incubator.http.AsyncSSLDelegate.unwrapBuffer(AsyncSSLDelegate.java:476)
at jdk.incubator.httpclient/jdk.incubator.http.AsyncSSLDelegate.handshakeReceiveAndUnWrap(AsyncSSLDelegate.java:395)
at jdk.incubator.httpclient/jdk.incubator.http.AsyncSSLDelegate.doHandshakeImpl(AsyncSSLDelegate.java:294)
at jdk.incubator.httpclient/jdk.incubator.http.AsyncSSLDelegate.doHandshakeNow(AsyncSSLDelegate.java:262)
at jdk.incubator.httpclient/jdk.incubator.http.AsyncSSLDelegate.connect(AsyncSSLDelegate.java:233)
at jdk.incubator.httpclient/jdk.incubator.http.AsyncSSLConnection.connect(AsyncSSLConnection.java:78)
at jdk.incubator.httpclient/jdk.incubator.http.Http2Connection.<init>(Http2Connection.java:263)
at jdk.incubator.httpclient/jdk.incubator.http.Http2ClientImpl.getConnectionFor(Http2ClientImpl.java:108)
at jdk.incubator.httpclient/jdk.incubator.http.ExchangeImpl.get(ExchangeImpl.java:86)
at jdk.incubator.httpclient/jdk.incubator.http.Exchange.establishExchange(Exchange.java:272)
at jdk.incubator.httpclient/jdk.incubator.http.Exchange.responseImpl0(Exchange.java:283)
at jdk.incubator.httpclient/jdk.incubator.http.Exchange.responseImpl(Exchange.java:260)
at jdk.incubator.httpclient/jdk.incubator.http.Exchange.response(Exchange.java:136)
at jdk.incubator.httpclient/jdk.incubator.http.MultiExchange.response(MultiExchange.java:154)
at jdk.incubator.httpclient/jdk.incubator.http.HttpClientImpl.send(HttpClientImpl.java:234)
at CheckProj/Main.Main.lambda$main$0(Main.java:42)
at java.base/java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:714)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
at jdk.incubator.httpclient/jdk.incubator.http.ResponseProcessors$ByteArrayProcessor.onComplete(ResponseProcessors.java:219)
at jdk.incubator.httpclient/jdk.incubator.http.BlockingPushPublisher.acceptData(BlockingPushPublisher.java:65)
at jdk.incubator.httpclient/jdk.incubator.http.AbstractPushPublisher.consume(AbstractPushPublisher.java:51)
at jdk.incubator.httpclient/jdk.incubator.http.ResponseContent.pushBodyChunked(ResponseContent.java:238)
at jdk.incubator.httpclient/jdk.incubator.http.ResponseContent.pushBody(ResponseContent.java:110)
at jdk.incubator.httpclient/jdk.incubator.http.Http1Response.lambda$readBody$2(Http1Response.java:157)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1161)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:844)

How can I fix it?

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
Nick Nick
  • 177
  • 3
  • 18
  • 2
    How do you know it failed for that reason? – user207421 Jun 11 '17 at 02:14
  • Debugged, tested, compared packets with wireshark. (HttpUrlConnection and this newHttpClient). The only difference is SNI. Server response: handshake_failure(40) – Nick Nick Jun 11 '17 at 08:13
  • Server requires SNI! – Nick Nick Jun 11 '17 at 08:22
  • See [Which Cipher Suites to enable for SSL Socket?](https://stackoverflow.com/a/23365536/608639) It shows you how to enable protocols and cipher suites. SNI is a TLS extension, so be sure to use TLS 1.0 or above. Java used to sneak in SSLv3; be sure its not enabled by accident. – jww Jun 11 '17 at 13:31
  • I am sure that i'm using TLS >= 1.0. But how i can enable SNI extension in new HttpClient? – Nick Nick Jun 11 '17 at 18:37
  • Up.. .. Any news? – Nick Nick Jun 18 '17 at 23:49
  • Can confirm: new HttpClient fails with SSL servers requiring server_name extension – Jochen Bedersdorfer Oct 16 '17 at 19:41
  • traced this down to the current SSL connection handler in the new httpclient not even calling `SSLEngine createSSLEngine(String peerHost, int peerPort)` (which would trigger SNI hostname detection. Sigh) – Jochen Bedersdorfer Oct 16 '17 at 20:11
  • I was able to add Extension server_name by setting `params.setServerNames(List.of(new SNIHostName(uri.getHost()))` still no cigar in my case. Must be something else – Jochen Bedersdorfer Oct 16 '17 at 20:38

1 Answers1

0

Here's a work-around that should not work, but does so far. I've implemented a subclass of SSLContext which always creates an SSLEngine with the hostname/port set (as well as SSL Parameters to check the host name).

https://gist.github.com/beders/51d3600d7fb57ad7d36a1745749ef641

Use it like this

HttpClient client = HttpClient.newBuilder().sslContext(ctx).sslParameters(ctx.getParametersForSNI()).followRedirects(HttpClient.Redirect.ALWAYS).build();

Let me know if it works

Jochen Bedersdorfer
  • 4,093
  • 24
  • 26