Search code examples
androidsslself-signedokhttp

OkHttp with Certificate Pinning


My Android project (OkHttp 3.3.1) currently works with my HTTPS web service (my PC, IIS web server, Asp.Net Web API, self-signed certificate)

Helper methods:

private SSLSocketFactory getSSLSocketFactory()
        throws CertificateException, KeyStoreException, IOException,
        NoSuchAlgorithmException, KeyManagementException {
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = getResources().openRawResource(R.raw.iis_cert);
    Certificate ca = cf.generateCertificate(caInput);
    caInput.close();
    KeyStore keyStore = KeyStore.getInstance("BKS");
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, tmf.getTrustManagers(), null);
    return sslContext.getSocketFactory();
}

private HostnameVerifier getHostnameVerifier() {
    return new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {            
            HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
            return hv.verify("BNK-PC.LOCALHOST.COM", session);
        }
    };
}

Code A:

OkHttpClient client = new OkHttpClient.Builder()
        .sslSocketFactory(getSSLSocketFactory())
        .hostnameVerifier(getHostnameVerifier())
        .build();

After reading this CertificatePinner guide, my project also works well when adding .certificatePinner(certificatePinner) as below:

Code B:

OkHttpClient client = new OkHttpClient.Builder()
        .sslSocketFactory(getSSLSocketFactory())
        .certificatePinner(certificatePinner)
        .hostnameVerifier(getHostnameVerifier())
        .build();

According to this Wiki, certificate pinning increases security.

However, actually I have not clearly understood this idea. So my question is that whether I need to or have to use certificatePinner or not when my app still works with the Code A. In other words, does Code B have better security than Code A?


Solution

  • Certificate Pinning should help with certain classes of attacks

    1. Any trusted certificate authority gets hacked and generates valid certificates for your domain which a MITM attack e.g. by an invasive government.
    2. Your app is running on a device with additional trusted certificates e.g. installed by the Corporation that supplied the phone.

    I think generally if you issued your certificates from two major CA e.g. verisign, you would pin to their signing certificates rather than your own. This is because you are likely to generate new certificates for your server as a routine thing.