Add some SSL options Java okhttp-gson client

* Add the `verifyingSsl` option to allow skipping verifying SSL
  certificate and host name (default to verify)
* Add `sslCaCert` to allow customizing the CA certificates
This commit is contained in:
xhh
2015-09-30 19:45:45 +08:00
parent 053af7a749
commit f483f934d8
3 changed files with 239 additions and 3 deletions

View File

@@ -30,13 +30,31 @@ import java.net.URLEncoder;
import java.net.URLConnection;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okio.BufferedSink;
import okio.Okio;
@@ -64,12 +82,17 @@ public class ApiClient {
private String datetimeFormat;
private DateFormat datetimeFormatter;
private InputStream sslCaCert;
private boolean verifyingSsl;
private OkHttpClient httpClient;
private JSON json;
public ApiClient() {
httpClient = new OkHttpClient();
verifyingSsl = true;
json = new JSON(this);
// Use ISO 8601 format for date and datetime.
@@ -134,6 +157,35 @@ public class ApiClient {
return responseHeaders;
}
public boolean isVerifyingSsl() {
return verifyingSsl;
}
/**
* Configure whether to verify certificate and hostname when making https requests.
* Default to true.
* NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks.
*/
public ApiClient setVerifyingSsl(boolean verifyingSsl) {
this.verifyingSsl = verifyingSsl;
applySslSettings();
return this;
}
public InputStream getSslCaCert() {
return sslCaCert;
}
/**
* Configure the CA certificate to be trusted when making https requests.
* Use null to reset to default.
*/
public ApiClient setSslCaCert(InputStream sslCaCert) {
this.sslCaCert = sslCaCert;
applySslSettings();
return this;
}
public String getDateFormat() {
return dateFormat;
}
@@ -501,7 +553,7 @@ public class ApiClient {
}
/**
* Deserialize response body to Java object, according the Content-Type
* Deserialize response body to Java object, according to the Content-Type
* response header.
*
* @param response HTTP response
@@ -840,4 +892,69 @@ public class ApiClient {
return contentType;
}
}
/**
* Apply SSL related settings to httpClient according to the current values of
* verifyingSsl and sslCaCert.
*/
private void applySslSettings() {
try {
KeyManager[] keyManagers = null;
TrustManager[] trustManagers = null;
HostnameVerifier hostnameVerifier = null;
if (!verifyingSsl) {
TrustManager trustAll = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() { return null; }
};
SSLContext sslContext = SSLContext.getInstance("TLS");
trustManagers = new TrustManager[]{ trustAll };
hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) { return true; }
};
} else if (sslCaCert != null) {
char[] password = null; // Any password will work.
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
KeyStore caKeyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = "ca" + Integer.toString(index++);
caKeyStore.setCertificateEntry(certificateAlias, certificate);
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(caKeyStore);
trustManagers = trustManagerFactory.getTrustManagers();
}
if (keyManagers != null || trustManagers != null) {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, new SecureRandom());
httpClient.setSslSocketFactory(sslContext.getSocketFactory());
} else {
httpClient.setSslSocketFactory(null);
}
httpClient.setHostnameVerifier(hostnameVerifier);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}