Trust all hosts in WebAPI test with OkHttp and Java SSL

Problem description

This issue was first discovered with circleci but the SSLHandShakeException is not reproduce in dev environment with my Mac. The detail error page is here. This post provides a verified way to bypass all hostname verification (or you can add specified conditions inside) for non-security test case to apply on Web API testing.

Exception details

Above picture shows the exception details. Generally a RESTful WebAPI test could be constructed with OkHttpClient lib:

@Before
public void setUp() throws Exception {
client = new OkHttpClient();
}

@Test
public void httpFreegeoipJsonTest() throws Exception {
Request request = new Request.Builder()
.url(Constants.Freegeoio_Url_Json)
.build();
Response response = client.newCall(request).execute();
Assert.assertEquals(200, response.code());
Assert.assertEquals("application/json", response.body().contentType().toString());

JSONObject jsonResp = new JSONObject(response.body().string());
String ip = jsonResp.getString("ip");
Assert.assertTrue(checkIpv4(ip));
}

Solution

It is usually caused by a self-signed certificate or an unknown certificate signer. The official solution plan is to download the SSL certificate and install it to Java SSL keystore. However, for a quick fix to test environment only concerning about the function instead of security perspective here (suggest to keep security part in a seperate test set). If we would like to bypass all hostname verification, here is the solution with a customized hostverifier. With Java8 we can use lambda to replace anonymous class when the interface is a functional one.

@Before
public void setUp() throws Exception {
//Changed to customized SSL context and hostVerifier.
OkHttpClient.Builder clientBuilder = new OkHttpClient().newBuilder();
X509TrustManager trustManager = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}

@Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}

@Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}
};
final TrustManager[] trustAllCerts = new TrustManager[]{ trustManager };

SSLContext sslContext = SSLContext.getInstance("SSL");

sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);

clientBuilder.hostnameVerifier((String hostname, SSLSession session) -> {
ColorPrint.println_red("Trusts Host " + hostname + "in session " + session);
return true;
});
client = clientBuilder.build();
}

Full code implementation could be referenced from Github Cucumber-Java-Toy Link in JUnite suite org.maxwu.jrefresh.HttpApi.SourceIpApiTest.

//EOF