Docs should explain benefits of pinning both CA and host certificates
See original GitHub issueThe JavaDocs for CertificatePinner
recommends creating pins for every certificate in the chain, as reported by the “Peer certificate chain” output in LogCat when a pin fails. So, at the moment, the docs have:
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
.add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
.add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
.build();
Near as I can tell, CertificatePinner
implements a logical OR when there are multiple pins set for any given hostname. So long as any certificate in the chain matches a pin for that hostname, the certificate is accepted.
Hence, from a security standpoint, the code in the question is the same as:
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
.build();
or even:
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("publicobject.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
.build();
The recommended advice is not pinning on the specific certificate for that server, but rather for every certificate based on the root CA (in this case, AddTrust’s), since you’re setting up a pin on that root CA certificate. There is nothing wrong with pinning on the root CA, but the code’s superfluous pins makes it appear that you are pinning on the server’s certificate, not the root CA’s.
You might want to modify the docs to:
- Specifically mention that each
add()
is logically OR’d with other pins for that hostname - Revise the example to only pin the specific server certificate (i.e.,
sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=
) - Offer a related example showing pinning the root CA (i.e.,
sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=
) and noting the effective difference - Possibly show implementing a pin set via two
add()
calls (e.g., for migrating to a new certificate)
If I am misunderstanding the nature of add()
and CertificatePinner
, I sincerely apologize.
Issue Analytics
- State:
- Created 6 years ago
- Comments:17 (7 by maintainers)
Top GitHub Comments
I think we should expand the https://github.com/square/okhttp/wiki/HTTPS documentation to show what an example client setup for using certificate pinning with regular certificate rotation would look like. Maybe a page for here’s how you can deploy a client pinning against Let’s Encrypt certificates that rotate every 3 months.
We should probably focus on concise specific advise for small companies without security teams. And assume the Squares and Twitters of the world have their well informed security teams with informed policies, just support them via the APIs.
The bigger risk for the former is only pinning against something temporary or revokable. Hence the OR of multiple pins.
Regarding which root CAs to trust. You do that when you choose who to pay for certs from. And should have more than 1.