I recently installed Let’s Encrypt on my iOS application’s API server and it’s working great. As another layer of security on top of SSL, I’ve implemented public key pinning in my iOS application.
My question is essentially what keys do I have to pin and what I can do to ensure the application works long into the future.
Currently, I am pinning the public keys of the ISRG Root X1, DST Root CA X3, Let's Encrypt X1/X3, and the Let's Encrypt X2/X4 certificates using TrustKit, a popular iOS framework for implementing SSL pinning.
From what I understand, my application will fail if any of these certificates are compromised, since an attacker could generate a certificate that would contain the public key of the compromised certificate, which would fool my application into accepting the response. Is this correct?
Secondly, it is a goal of mine to ensure that the application works long in the future without updates, so I would like to ensure that the pinned public keys are exhaustive and would not require amendment in the near future. What can I do to ensure this?
I have read that it also makes sense to pin a private, safe key in the case of emergencies (such as compromised keys, changes, etc). What I don’t understand is what exactly one would do with this key in the case of an emergency. I can’t really sign my own SSL certificate with that key since it wouldn’t be trusted by anything, so I can’t grasp why it is necessary.
Thank you for your feedback, in advance. I haven’t been able to find a lot of documentation surrounding public key pinning and exactly what to pin when using Let’s Encrypt, so any direction or validation would be appreciated.
There's a good post on using HPKP (the key pinning mechanism for HTTP) here:
The tl;dr is:
Pin to multiple independent CAs
Pin to a backup key you keep offline (in a safe/bank vault/etc.)
The backup key will be useful if both CAs are compromised (or stop issuing certificates that chain up to one of your pinned keys). At least for HPKP, the pin will be satisfied if any of the keys in the chain match - including the key of your own certificate (typically called end-entity certificate), so using that key in the CSR you use to request a certificate from a new CA should satisfy the pin. I have not personally used TrustKit before so I can't guarantee that it works like that as well, but it would seem counter-intuitive to do it any other way.
As for the keys you need to include for Let's Encrypt specifically, ISRG Root X1 and DST Root CA X3 would be sufficient. Technically, even DST Root CA X3 would be sufficient right now, but to keep things future-proof, including the ISRG root can't hurt. Either way, you should count those two certificates as one CA with regards to the "pin to two independent CAs" recommendation.
As @pfg says, the linked thread is the best source of information.
I’d further say:
Your app should be set up to ignore pinning if it goes more than 60 days without an update.
You should include at least two other CAs other than Let’s Encrypt in your pinning list. Let’s Encrypt is great, but it shouldn’t be a single point of failure between you and your users.