Securing HTTPS in Android/Java apps

Securing HTTPS  in Android/Java apps
Ares, The Greek God of War.

1. You can avoid system certificates completely.

Why would you want that?

  1. Certificates installed in the OS may include root CAs like China Internet Network Information Center and so on (see Settings -> Security -> Trusted credentials). That means that those CAs may do MiTM attacks silently and view/modify requests and responses (unless you use Certificate Pinning).

Android OS has about 200+ installed root CAs, also, device manufacturers may add own certificates. Are you sure you want to trust all of them? For example Lenovo put 3rd party root CA certificates to their laptops and they even leaked private keys -> MiTMs, f*ck you Lenovo).

  1. On Android root CAs certificates get updated only with Android OS updates.

First thing -> as an app developer you can not update system certificates. This is not very critical because usually they have expiration period ~20 years. But ->

Second thing -> If some of the root CAs will be "hacked" and its private keys will be stolen, they can be used for global/personal MiTMs and you won't be protected from them (unless you use Certificate Pinning).

Ok, how can I avoid system certificates and have checked https connections to 3rd party services like Amazon S3 at the same time?

Google helps you here. They have Google Internet Authority G2 project https://pki.google.com. They maintain set of "good" certificate authorities that you can bundle with your app and control which CAs you will trust and which you won't. Also, that means that even on very old OS you'll be able to have updated set of CAs and secure the connection correctly.

2. Think about Certificate Pinning

In short: certificate pinning is when you not only trust some CAs but also have a rule(s) that checks that certificate chain used to connect to some host contains the certificate you expect it to contain.

You should understand the order of secure socket connection checking:

  1. Socket stuff
  2. Certificate chain used in handshake goes to TrustManager that either says that it trusts that chain or not. "Trust" means that all certificates in the chain either were produced by known trusted CAs or that those certificates were installed directly to the OS (you can do it manually).
  3. If and only if certificates in the chain were trusted by TrustManager they can be checked by optional Certificate Pinning step.

As you can see, without certificate pinning any certificates produced by "trusted" CAs will be accepted, like certificate from China Internet Network Information Center -> possible MiTM (but not by "regular" man of course).


Example: you want to prevent MiTMs from China Internet Network Information Center/other CAs and you have regular SSL/TLS certificate made by some other trusted CAs, for example GeoTrust.

You can add a rule: certificatePinner.add("api.myhost.com", "sha256/fingerPrintOfTheCertificateThatYouTrust").

This will ensure that at least one certificate in the chain is the one that you trust and the only way to MiTM you will be to stole private key of that certificate. Usually it's impossible (if it's your own certificate) or much harder to fake chain and include trusted certificate.

Note that you can pin multiple fingerprints to one domain -> increase security.


Now when you know how certificate pinning works you should understand that it's not only increases security but also may brake old versions of your app!

Because if you pin against certificate that is expired and was removed/replaced from the chain connection won't be established.

DO NOT PIN CERTIFICATES WITHOUT TALKING TO YOUR BACKEND ADMINISTRATORS/DEVOPS.

Most of the apps won't pin certificates just because it doesn't worth it or they think that it doesn't worth it.

But for apps that transfer highly confidential/person/business critical information (Banking clients, for example) better to break connection and force user to update the app instead of allowing possible problems.

Carefully decide what certificate(s) from the chain you can pin. Again: TALK TO YOUR BACKEND ADMINISTRATORS/DEVOPS FIRST.

Keep in mind that you can prepare app for new certificates before actually switching to them on the server side, you can do this long time before the actual update, just add fingerPrint of the new certificate to the CertificatePinner and release the app update.

3. What library should you use for HTTPS?

  1. Do not use HttpUrlConnection or especially Apache HTTP Client bundled into JDK. Reason for that is because you'll stuck on version of the classes that are bundled into JDK -> OS. From time to time researchers find bugs in such libraries and you won't be able to fix them by updating the library because they live in JDK and depends on OS updates.

  2. Use OkHttp.

First of all it's regulary updated (including security fixes) and you can update it without depending on OS updates.

Secondly, it has all you need for securing https connections including API to set custom SSLSocketFactory with own trusted certificates and CertificatePinner API (which supports wildcards btw).

// And OkHttp is just great in many aspects.