I have written the code to call REST-API over SSL using OkHttp3. There are many samples out there that connect without validating the certificate, but this code does the following validation:
Obtain the server certificate from the place where the API is published. This time I got it from Recruit in the browser (Chrome). You can find various ways to get it by searching "How to get the server certificate browser". The format of the export file is "Base 64 encoded X.509".
It is the one above the certificate in the "Certificate Pass". Get it in the same way as above.
Open the above two certificates with a text editor and copy and paste one of them to combine them. I brought the parents down as a "proof pass".
Deploy the merged files to a location in your classpath.
API(SSL)Execution class
public class RestSecureApiExecutor extends RestApiExecutor {
private CertificateManager certMgr;
/**
*constructor.
*
* @param apiAttr Secure API Attribute
*/
public RestSecureApiExecutor(SecureApiAttribute apiAttr) {
super(apiAttr);
this.certMgr = apiAttr.getCertMgr();
}
/**
* {@inheritDoc}
*/
@Override
public Response get(Map<String, String> header, Map<String, String> param, boolean isValidCache) throws IOException {
//URL assembly
HttpUrl.Builder urlBuilder = createUrlBuilder(ProtocolType.Secure, param);
//HTTP header assembly
Request.Builder requestBuilder = createRequestBuilder(header);
//Whether cache is used
if (!isValidCache) {
requestBuilder.cacheControl(new CacheControl.Builder().noCache().noStore().maxAge(0, TimeUnit.SECONDS).build());
}
//Assembling the request
Request request = requestBuilder.url(urlBuilder.build()).build();
//Whether cache is used
OkHttpClient.Builder clientBuilder = createClientBuilder(isValidCache);
addCertificatePinner(clientBuilder, certMgr.getCertificates());
addSslSocketFactory(clientBuilder, certMgr.getTrustManager());
//API execution
return clientBuilder.build().newCall(request).execute();
}
/**
*Add CertificatePinner to the builder.
*
* @param builder OkHttpClient.Builder object
* @param certificates Certificate information
*/
private void addCertificatePinner(OkHttpClient.Builder builder, Certificate[] certificates) {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(this.hostname, CertificatePinner.pin(certificates[0]))
.build();
builder.certificatePinner(certificatePinner);
}
/**
*Add SslSocketFactory to the builder.
*
* @param builder OkHttpClient.Builder object
* @param trustManager TrustManager
*/
private void addSslSocketFactory(OkHttpClient.Builder builder, X509TrustManager trustManager) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new RuntimeException("____ failed to create ssl socket factory.", e);
}
}
Parent class
public class RestApiExecutor {
/**
*Protocol type.
*/
public enum ProtocolType {
/**Normal. */
Normal("http"),
/** SSL. */
Secure("https"),;
private String type;
private ProtocolType(String type) {
this.type = type;
}
public String getValue() {
return this.type;
}
}
/**hostname. */
protected String hostname;
/**segment. */
protected String segment;
/**
*constructor.
*
* @param apiAttr API attribute
*/
public RestApiExecutor(ApiAttribute apiAttr) {
//Hostname, segment
this.hostname = apiAttr.getHostname();
this.segment = apiAttr.getSegment();
}
/**
* REST API (get)To run.
*
* @param header HTTP header parameters
* @param param query parameters
* @param isValidCache cache used (true: use, false: not use)
* @return response
* @throws IOException If processing fails
*/
public Response get(Map<String, String> header, Map<String, String> param, boolean isValidCache) throws IOException {
//URL assembly
HttpUrl.Builder urlBuilder = createUrlBuilder(ProtocolType.Normal, param);
//HTTP header assembly
Request.Builder requestBuilder = createRequestBuilder(header);
//Whether cache is used
if (!isValidCache) {
requestBuilder.cacheControl(new CacheControl.Builder().noCache().noStore().maxAge(0, TimeUnit.SECONDS).build());
}
//Assembling the request
Request request = requestBuilder.url(urlBuilder.build()).build();
//Whether cache is used
OkHttpClient.Builder clientBuilder = createClientBuilder(isValidCache);
//API execution
return clientBuilder.build().newCall(request).execute();
}
/**
*Assemble the URL.
*
* @param type ProtocolType
* @param param parameter
* @return HttpUrl.Builder object
*/
protected HttpUrl.Builder createUrlBuilder(ProtocolType type, Map<String, String> param) {
//URL builder
HttpUrl.Builder builder = new HttpUrl.Builder();
//Schema(http or https)
builder.scheme(type.getValue());
//hostname
builder.host(this.hostname);
//segment
builder.addPathSegments(this.segment);
if (param != null) {
//Query parameters
param.forEach(builder::addQueryParameter);
}
return builder;
}
/**
*Assemble HTTP headers.
*
* @param param parameter
* @return Request.Builder object
*/
protected Request.Builder createRequestBuilder(Map<String, String> param) {
Request.Builder builder = new Request.Builder();
builder.addHeader("Content-Type", "application/json");
if (param != null) {
param.forEach(builder::addHeader);
}
return builder;
}
/**
*Assemble the HTTP client.
*
* @param isValidCache cache used or not
* @return OkHttpClient.Builder object
*/
protected OkHttpClient.Builder createClientBuilder(boolean isValidCache) {
//Whether cache is used
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (!isValidCache) {
builder.cache(null);
}
return builder;
}
Certificate reading
public class CertificateLoader {
/**
*Read the certificate.
*
* @param path Certificate path
* @return X509Certificate array
*/
public X509Certificate[] load(String path) {
List<X509Certificate> certificateList = new ArrayList<>();
try {
Collection<? extends Certificate> certificates = getCertificates(path);
for (Certificate certificate : certificates) {
X509Certificate x509certificate = (X509Certificate) certificate;
x509certificate.checkValidity(); //Determine if the certificate is currently valid
certificateList.add(x509certificate);
}
} catch (CertificateExpiredException | CertificateNotYetValidException e) {
throw new RuntimeException("____ failed to check valid cer file.", e);
}
return certificateList.toArray(new X509Certificate[certificateList.size()]);
}
/**
*Get certificate information.
*
* @param path Certificate path
* @return Certificate information
*/
private Collection<? extends Certificate> getCertificates(String path) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
InputStream in = this.getClass().getClassLoader().getResourceAsStream(path);
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
return certificates;
} catch (CertificateException e) {
throw new RuntimeException("____ failed to get certificates.");
}
}
Certificate management
public class CertificateManager {
private CertificateLoader loader;
private X509Certificate[] certificates;
private X509TrustManager trustManager;
/**
*constructor.
*
* @param path Certificate path
*/
public CertificateManager(String path) {
this.loader = new CertificateLoader();
this.certificates = loader.load(path);
//Create a certificate manager and verify that it is a trusted server certificate
this.trustManager = createTrustManager(certificates);
checkServerTrusted(trustManager, certificates);
}
/**
*Get certificate information.
*
* @return Certificate information
*/
public X509Certificate[] getCertificates() {
return this.certificates;
}
/**
*Get TrustManager.
*
* @return TrustManager
*/
public X509TrustManager getTrustManager() {
return this.trustManager;
}
/**
*Generate TrustManager.
*
* @param certificates Certificate information
* @return TrustManager
*/
private X509TrustManager createTrustManager(X509Certificate[] certificates) {
TrustManager[] trustManagers = null;
try {
char[] password = "password".toCharArray();
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (X509Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers : " + Arrays.toString(trustManagers));
}
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
throw new RuntimeException("____ failed to create trust manager.", e);
}
return (X509TrustManager) trustManagers[0];
}
/**
*Create an empty KeyStore object.
*
* @param password password
* @return Empty KeyStore object
*/
private KeyStore newEmptyKeyStore(char[] password) {
KeyStore keyStore = getDefaultKeyStoreInstance();
try {
InputStream in = null;
keyStore.load(in, password);
} catch (NoSuchAlgorithmException | CertificateException | IOException e) {
throw new RuntimeException("____ failed to load from KeyStore.", e);
}
return keyStore;
}
/**
*Gets the default KeyStore object.
*
* @return Default KeyStore object
*/
private KeyStore getDefaultKeyStoreInstance() {
try {
return KeyStore.getInstance(KeyStore.getDefaultType());
} catch (KeyStoreException e) {
throw new RuntimeException("____ failed to get KeyStore default instance.", e);
}
}
/**
*Check the certificate.
*
* @param trustManager TrustManager
* @param certificates certificate
*/
private void checkServerTrusted(X509TrustManager trustManager, X509Certificate[] certificates) {
try {
trustManager.checkServerTrusted(certificates, "SHA256withRSA");
} catch (CertificateException e) {
throw new RuntimeException("____ failed to check server trust.", e);
}
}
Hostname validation probably does, as it doesn't implement (override) the HostnameVerifier verify method.
that's all