Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Glorious hack to use Java 9 HttpClient with servers enforcing SNI
package us.monoid.resty.hack;
import javax.net.ssl.*;
import java.security.*;
import java.util.List;
/** Glorious hack to always create an SSL Engine with a hostname and port.
* engineGetServerSessionContext and engineGetClientSessionContext can't be implemented without violating class/module loading constraints,
* but for some reason, they are not being called.
* */
public class ForceHostnameVerificationSSLContext extends SSLContext {
String hostname;
public ForceHostnameVerificationSSLContext(String hostname, int port) {
super(new ForcedSSLContextSpi(hostname, port), null, "Default");
this.hostname = hostname;
}
public SSLParameters getParametersForSNI() {
SSLParameters params = null;
try {
params = SSLContext.getDefault().getDefaultSSLParameters();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
params.setServerNames(List.of(new SNIHostName(hostname)));
params.setEndpointIdentificationAlgorithm("HTTPS");
return params;
}
public static class ForcedSSLContextSpi extends SSLContextSpi {
private final String hostname;
private final int port;
public ForcedSSLContextSpi(String hostname, int port) {
this.hostname = hostname;
this.port = port;
}
@Override
protected void engineInit(KeyManager[] keyManagers, TrustManager[] trustManagers, SecureRandom secureRandom) throws KeyManagementException {
}
@Override
protected SSLSocketFactory engineGetSocketFactory() {
try {
return SSLContext.getDefault().getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
@Override
protected SSLServerSocketFactory engineGetServerSocketFactory() {
try {
return SSLContext.getDefault().getServerSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
@Override
protected SSLEngine engineCreateSSLEngine() {
try {
return SSLContext.getDefault().createSSLEngine(hostname, port);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
@Override
protected SSLEngine engineCreateSSLEngine(String host, int port) {
try {
return SSLContext.getDefault().createSSLEngine(host, port);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
@Override
protected SSLSessionContext engineGetServerSessionContext() {
return null;
}
@Override
protected SSLSessionContext engineGetClientSessionContext() {
return null;
}
}
}
@beders
Copy link
Author

beders commented Oct 16, 2017

Use this with the new HttpClient like this:

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

@beders
Copy link
Author

beders commented Oct 17, 2017

Why would you need this?
Turns out the new http client in jdk.incubator.http doesn't support SNI yet. That significantly reduces compatibility with many HTTPS servers. One example is https://jsonplaceholder.typicode.com/posts which works just fine with the regular HTTP client.

The new http client will need significant improvement before inclusion into JDK 10.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment