Trusting All Certificates with Okhttp

Trusting all certificates with okHttp

Just in case anyone falls here, the (only) solution that worked for me is creating the OkHttpClient like explained here.

Here is the code:

private static OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}

@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}

@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};

// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});

OkHttpClient okHttpClient = builder.build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

Implementation of trust in all certificates with okhttp

Try to use OkHttpClient.Builder instead of direct OkHttpClient. Then you can setup sslSocketFactory and hostnameVerifier described https://stackoverflow.com/a/25992879/3816129

Okhttp3 - Accept all certificates and use a certificatePinner

The TrustManager, CertificatePinner and Hostname verification all do different but important things. If you want to use self-signed certificates but still have security, as opposed to self-signed certificates purely for ease of local development then you probably want to create a valid TrustManager.

e.g. https://github.com/yschimke/oksocial/blob/3757196cde420b9d0fe37cf385b66f4cdafb1ae1/src/main/java/com/baulsupp/oksocial/security/CertificateUtils.java#L19

  public static X509TrustManager load(List<File> serverCerts)
throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException {
return trustManagerForKeyStore(keyStoreForCerts(serverCerts));
}

public static X509TrustManager trustManagerForKeyStore(KeyStore ks)
throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmf.init(ks);

return (X509TrustManager) tmf.getTrustManagers()[0];
}

public static KeyStore keyStoreForCerts(List<File> serverCerts)
throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);

for (int i = 0; i < serverCerts.size(); i++) {
try (InputStream is = new FileInputStream(serverCerts.get(i))) {
X509Certificate caCert = (X509Certificate) cf.generateCertificate(is);
ks.setCertificateEntry("cacrt." + i, caCert);
}
}
return ks;
}

This will start off with the system certificates loaded, so your client can still be used to load externally hosted images etc.

Then on top of that you can use CertificatePinner to require that only your trusted self-signed certificates are used for your domain.

OkHttp trusting certificate

Let's assume your server app is hosting inside a server machine which has a server certificate in which "Issued to" is "localhost", for example. Then, inside verify method you can verify "localhost".

HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv =
HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify("localhost", session);
}
};

You can read more at the following links:

  1. HostnameVerifier

    It is to be used during a handshake if the URL's hostname does not match the peer's identification hostname.

  2. Common Problems with Hostname Verification

    One reason this can happen is due to a server configuration error. The
    server is configured with a certificate that does not have a subject
    or subject alternative name fields that match the server you are
    trying to reach...

Then, you can use the hostname verifier in your app, by calling client.setHostnameVerifier(hostnameVerifier);. Hope this helps!

P/S: another temporary workaround is return true; inside verify method, however, it's not recommended.

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

How to ignore SSL error in Okhttp?

OkHttpClient.Builder allows you to add your own SSLContext and TrustManager. Here is the code from this gist which ignore all errors:

private static OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
}

@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
}

@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};

// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

return new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}).build();

} catch (Exception e) {
throw new RuntimeException(e);
}
}

Note: This is an unsafe solution. Use it for debugging stuff.

OKhttp Self sign certificate in Kotlin Android

The following code works for me when I put these few lines of code before building my Client connection.

//set self sign certificate
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
}

override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
}

override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())

// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory

// connect to server
val client = OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager).hostnameVerifier{ _, _ -> true }.build()


Related Topics



Leave a reply



Submit