Skip to content

Instantly share code, notes, and snippets.

@beders
Created October 16, 2017 23:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beders/51d3600d7fb57ad7d36a1745749ef641 to your computer and use it in GitHub Desktop.
Save beders/51d3600d7fb57ad7d36a1745749ef641 to your computer and use it in GitHub Desktop.
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 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