3

My android application connects to an URL provided by the user. In case of HTTPS connections, if the server's certificate is issued by a CA that already exists in the Android's TrustManager, everything is fine.

But if the server uses a self-signed certificate how can I obtain that certificate and store it in the TrustManager on first connection?

I am using OkHttp library for performing network tasks. The solution that I have currently forces me to add the certificate in the application's raw folder during development but this will not work for the above mentioned scenario. The code that I am using currently is as below:

private KeyStore readKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

    String password = "testPass";

    InputStream is = null;

    try {
        is = activity.getApplicationContext().getResources().openRawResource(R.raw.server_key);
        ks.load(is, password.toCharArray());
    } finally {
        if (is != null)
            is.close();
    }

    return ks;
}

private OkHttpClient getOkHttpClient() throws CertificateException, IOException, KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {

    SSLContext sslContext = SSLContext.getInstance("SSL");
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

    trustManagerFactory.init(readKeyStore());
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(readKeyStore(), "testPass".toCharArray());

    sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

    return new OkHttpClient().setSslSocketFactory(sslContext.getSocketFactory());
}
Prerak Sola
  • 9,517
  • 7
  • 36
  • 67

2 Answers2

4

The simple solution

Here's a code example of a trust manager callback. From the callback you can either store the self-signed certificates or just accept them right away. But you SHOULD NOT do neither. By accepting self-signed certs, you are subject to connecting to fake or malicious servers which can steal personal data, install malware, and do other nasty things.

The better-than-simple solution

If a server offers a self-signed certificate that you, the developer, trust at compile time, you can bundle the cert into the app like you're already doing. It would be much better if the server had a CA-signed certificate, but if you really need to connect to a server that offers a self-signed cert, this will (have to) do.

A nicer solution

Never accept self-signed certificates in runtime. If you want to allow your users to connect to these kind of servers, you can show a warning message like "proceed at your own risk" before accepting the self-signed cert.

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
nandsito
  • 3,782
  • 2
  • 19
  • 26
1

You should not add it on first connection. Ideally they should just pay to get a proper certificate signed by a CA.

But even then you might find popular new CAs like Let's Encrypt that are not supported by the installed Java version.

In these cases you can either add the individual cert or the new CA manually using keytool for a JVM, or load it via standard Android mechanisms.

Or you can bundle those with the app. This example code shows how to load bundles certificates in addition to existing system certs using a Merged TrustStore https://github.com/yschimke/oksocial/blob/master/src/main/java/com/baulsupp/oksocial/security/CertificateUtils.java

Yuri Schimke
  • 12,435
  • 3
  • 35
  • 69