SSL Pinning and certificate expiry

Chris picture Chris · Jan 2, 2016 · Viewed 11.8k times · Source

This question relates to the use of SSL Pinning in a client app against a web api and certificate expiry.

Scenario:

I own example.com and have a subdomain where an api is hosted, as such: api.example.com

I wish to use the api over SSL, so an SSL Certificate is created for the subdomain.

After the certificate has been acquired, I have:

  • A Public Certificate
  • A Intermediate Certificate
  • A Private Key

It's my understanding that I install these certificates on my webserver.

I then wish for my client app to connect to the api. To mitigate against man-in-the-middle style attacks, I wish to use SSL Pinning, so that the client will only communicate with my api, not someone spoofing it.

In order to pin in the client app, I have two choices, either pin against the public or intermediate certificate.

Let's say I implement this.

What happens when the certificate on api.example.com expires?

It's my understanding that the client app would no longer work.

Do I need to regenerate a complete set of public/intermediate/private items again? and then put a new public or intermediate certificate in the app?

Question:

I would still like the client app to work until the certificate on api.example.com was updated. Of course, a new certificate can be put in the client app, but things like roll-out take time.

How can I handle this?

I've read that Google updates their certificate every month, but somehow manages to keep the public key the same: How to pin the Public key of a certificate on iOS

If that's possible, then the solution is to simply extract the public key from the server and check it against the locally stored public key...but how do Google do it?

Thanks

Chris

Answer

Barry Pollard picture Barry Pollard · Jan 2, 2016

Note: I'm more familiar with browser to server pinning (HTTP Public Key Pinning - HPKP) rather than app to server pinning, but I presume the principal is the same. In HPKP the pinning policy is provided by the server as a HTTP header but understand this is often built into the app rather than read from the HTTP response. So read below answer with all that in mind:

Pinning is usually against the key not the cert and can be a multiple levels. So you've several choices:

  1. Reuse the same key/crt to generate a new cert. Some (rightly in my opinion!) recommend generating a new key each time you renew your cert but this is complicated when you use pinning. So does pinning encourage poor security habits like key reuse?

  2. Have several back up keys in your pinning policy and rotate them around on cert renewal discarding your oldest and adding a new one with plenty of time and updates to never be caught short. Personally I prefer to generate the key at cert renewal time rather than have some backups around which may or may have been compromised so I'm not a particular fan of this either. And how many backups should you have? E.g. If you need to reissue a cert because of compromise around renewal and also mess it up? So 2? 3? 100?

  3. Pin further up. Say the first intermediate or the root CA cert. So any newly issued cert is still trusted (providing it's issued by same cert path) The downside of this is four fold: i) You still leave yourself open to miss-issued certs issued by that pinned cert (not a massive deal IMHO as you've still massively reduced your attack surface but still a concern to some people), ii) you cannot guarantee the client will use that intermediate cert as there are sometimes multiple valid paths. This second one is a much bigger deal. You'd think that providing the intermediate cert would guarantee this would be used but that's not the case (plenty of sha-1 examples of this). iii) There's no guarantee new cert will be issued by same intermediate or root (especially when technologies change like introduction of sha2), so to me this whole option is a non-starter iv) It ties you in to using same cert provider (perhaps not a big deal but I like the freedom to move). Not sure if apps support this feature natively anyway but browsers certainly do.

  4. Renew in advance and do not use the new key until policy cache expires. For example if you have one year certs and a 30 day pinning policy then you can renew after 11 months, add the new key to the policy, then wait 30 days so you can be sure everyone will have picked up new policy or at least the old policy will have expired, then switch keys and certs. Depends on a short policy and potentially wastes a portion of that though (at least 30 days in this example), unless cert provider provides cert in advance starting on day after old policy expires. For an app, if pinning policy is hard coded into it, then this might involve the length of time it takes to push out an update.

Ultimately, because certs do require renewing, I'm not a big fan of pinning. I don't think making something that is subject to periodic renewal, semi-permanent is the right answer. And there are even some talk of pre-loading pinning policies in browsers which just makes me shudder.

Pinning provides assurance that a rogue CA is not issuing certs for your domain but how likely is that really compared to the hassle of pinning? Something like Certificate Transparency - or even report only pinning may be a better answer to that problem even if they don't actually stop that attack.

Finally locally installed roots (e.g. for antivirus scanners or corporate proxies), bypass pinning checks (on the browser at least) which again reduces its effectiveness in my eyes.

So think carefully before using pinning and make sure you understand all the consequences.