TLS: pin to fingerprint of public key, not certificate
Currently, it looks like schleuder-cli
pins to the fingerprint of the certificate:
fingerprint = OpenSSL::Digest::SHA256.new(cert.to_der).to_s
this is strictly worse than pinning to the public key material contained in the certificate itself. Certificates have expiration dates, metadata, and other markers that may make them (over time) less useful to clients that might in the future use a more flexible and robust certificate verification mechanism than a raw pin. By pinning to the certificate, you're holding all the non-key information immutable.
If you pin to the public key material, it's possible to get new certificates issued over the same public key material, so that one pinned client doesn't keep the server from ever updating its cert. (e.g. asking let's encrypt to provide a refreshed certificate over the same public key material)
the simplest fix would be to extract the subject public key information from the cert, transform it to DER encoding, and fingerprint that. I don't know enough ruby to know how to extract the public key info, but i'm happy to review a proposed change if you can point me to the docs.
if you have to deal with older clients, though, they might get upset that an upgrade to schleuder-cli broke their stored. You can do a phased upgrade by picking a future version X
of schleuder-cli
that will remove cert-matching. Then starting as soon as possible, test the stated pin against both the cert and the public key. If it matches the public key, all is good. If it matches the cert, then warn that the matching is currently deprecated and due to be removed in version X
, and offer to replace the pin automatically with value that should be used to pin the public key.
Note that schleuder itself should probably also be updated to announce its public key fingerprint, not its certificate fingerprint.