Does OkHttp support accepting self-signed SSL certs?
Yes, It does.
Retrofit allows you to set your custom HTTP client, that is configured to your needs.
As for self-signed SSL certs there is a discussion here. The link contains code samples to add self-signed SSL to Android's DefaultHttpClient
and to load this client to Retrofit.
If you need OkHttpClient
to accept self signed SSL, you need to pass it custom javax.net.ssl.SSLSocketFactory
instance via setSslSocketFactory(SSLSocketFactory sslSocketFactory)
method.
The easiest method to get a socket factory is to get one from javax.net.ssl.SSLContext
as discussed here.
Here is a sample for configuring OkHttpClient:
OkHttpClient client = new OkHttpClient();
KeyStore keyStore = readKeyStore(); //your method to obtain KeyStore
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "keystore_pass".toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(), new SecureRandom());
client.setSslSocketFactory(sslContext.getSocketFactory());
Updated code for okhttp3 (using builder):
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.build();
the client
here is now configured to use certificates from your KeyStore
. However it will only trust the certificates in your KeyStore
and will not trust anything else, even if your system trust them by default. (If you have only self signed certs in your KeyStore
and try to connect to Google main page via HTTPS you will get SSLHandshakeException
).
You can obtain KeyStore
instance from file as seen in docs:
KeyStore readKeyStore() {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = getPassword();
java.io.FileInputStream fis = null;
try {
fis = new java.io.FileInputStream("keyStoreName");
ks.load(fis, password);
} finally {
if (fis != null) {
fis.close();
}
}
return ks;
}
If you are on android you can put it in res/raw
folder and get it from a Context
instance using
fis = context.getResources().openRawResource(R.raw.your_keystore_filename);
There are several discussions on how to create your keystore. For example here
For okhttp3.OkHttpClient Version com.squareup.okhttp3:okhttp:3.2.0 you have to use the code below :
import okhttp3.Call;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
......
OkHttpClient.Builder clientBuilder = client.newBuilder().readTimeout(LOGIN_TIMEOUT_SEC, TimeUnit.SECONDS);
boolean allowUntrusted = true;
if ( allowUntrusted) {
Log.w(TAG,"**** Allow untrusted SSL connection ****");
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] cArrr = new X509Certificate[0];
return cArrr;
}
@Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}
@Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}
}};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
clientBuilder.sslSocketFactory(sslContext.getSocketFactory());
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
Log.d(TAG, "Trust Host :" + hostname);
return true;
}
};
clientBuilder.hostnameVerifier( hostnameVerifier);
}
final Call call = clientBuilder.build().newCall(request);
Another thing to note, if you pre-install the CA on the device, you can make regular https calls with OKHttp, and no special ssl hoops. The key is to add the network security configs to your manifest.
The key for me to know to do this was that I was getting the following exception.
"Trust anchor for certification path not found."
Here is a good article from Google about how to configure it. https://developer.android.com/training/articles/security-config
Here is an example of my network_security_config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="user"/>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>