public final class CertificatePinner
extends java.lang.Object
This class currently pins a certificate's Subject Public Key Info as described on Adam Langley's Weblog. Pins are either base64 SHA-256 hashes as in HTTP Public Key Pinning (HPKP) or SHA-1 base64 hashes as in Chromium's static certificates.
The easiest way to pin a host is turn on pinning with a broken configuration and read the expected configuration when the connection fails. Be sure to do this on a trusted network, and without man-in-the-middle tools like Charles or Fiddler.
For example, to pin https://publicobject.com
, start with a broken
configuration:
String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Request request = new Request.Builder()
.url("https://" + hostname)
.build();
client.newCall(request).execute();
As expected, this fails with a certificate pinning exception:
javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
Peer certificate chain:
sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=: CN=publicobject.com, OU=PositiveSSL
sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=: CN=COMODO RSA Secure Server CA
sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=: CN=COMODO RSA Certification Authority
sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=: CN=AddTrust External CA Root
Pinned certificates for publicobject.com:
sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
at okhttp3.CertificatePinner.check(CertificatePinner.java)
at okhttp3.Connection.upgradeToTls(Connection.java)
at okhttp3.Connection.connect(Connection.java)
at okhttp3.Connection.connectAndSetOwner(Connection.java)
Follow up by pasting the public key hashes from the exception into the
certificate pinner's configuration:
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();
Pinning is per-hostname and/or per-wildcard pattern. To pin both publicobject.com
and
www.publicobject.com
, you must configure both hostnames.
Wildcard pattern rules:
*
is only permitted in the left-most domain name label and must be the
only character in that label (i.e., must match the whole left-most label). For example,
*.example.com
is permitted, while *a.example.com
, a*.example.com
,
a*b.example.com
, a.*.example.com
are not permitted.
*
cannot match across domain name labels. For example,
*.example.com
matches test.example.com
but does not match
sub.test.example.com
.
*.example.com
pinned with pin1
and a.example.com
pinned with
pin2
, to check a.example.com
both pin1
and pin2
will be used.
Pinning certificates limits your server team's abilities to update their TLS certificates. By pinning certificates, you take on additional operational complexity and limit your ability to migrate between certificate authorities. Do not use certificate pinning without the blessing of your server's TLS administrator!
CertificatePinner
can not be used to pin self-signed certificate if such certificate
is not accepted by TrustManager
.
Modifier and Type | Class and Description |
---|---|
static class |
CertificatePinner.Builder
Builds a configured certificate pinner.
|
(package private) static class |
CertificatePinner.Pin |
Modifier and Type | Field and Description |
---|---|
private CertificateChainCleaner |
certificateChainCleaner |
static CertificatePinner |
DEFAULT |
private java.util.Set<CertificatePinner.Pin> |
pins |
Constructor and Description |
---|
CertificatePinner(java.util.Set<CertificatePinner.Pin> pins,
CertificateChainCleaner certificateChainCleaner) |
Modifier and Type | Method and Description |
---|---|
void |
check(java.lang.String hostname,
java.security.cert.Certificate... peerCertificates)
Deprecated.
replaced with
check(String, List) . |
void |
check(java.lang.String hostname,
java.util.List<java.security.cert.Certificate> peerCertificates)
Confirms that at least one of the certificates pinned for
hostname is in peerCertificates . |
boolean |
equals(java.lang.Object other) |
(package private) java.util.List<CertificatePinner.Pin> |
findMatchingPins(java.lang.String hostname)
Returns list of matching certificates' pins for the hostname.
|
int |
hashCode() |
static java.lang.String |
pin(java.security.cert.Certificate certificate)
Returns the SHA-256 of
certificate 's public key. |
(package private) static okio.ByteString |
sha1(java.security.cert.X509Certificate x509Certificate) |
(package private) static okio.ByteString |
sha256(java.security.cert.X509Certificate x509Certificate) |
(package private) CertificatePinner |
withCertificateChainCleaner(CertificateChainCleaner certificateChainCleaner)
Returns a certificate pinner that uses
certificateChainCleaner . |
public static final CertificatePinner DEFAULT
private final java.util.Set<CertificatePinner.Pin> pins
@Nullable private final CertificateChainCleaner certificateChainCleaner
CertificatePinner(java.util.Set<CertificatePinner.Pin> pins, @Nullable CertificateChainCleaner certificateChainCleaner)
public boolean equals(@Nullable java.lang.Object other)
equals
in class java.lang.Object
public int hashCode()
hashCode
in class java.lang.Object
public void check(java.lang.String hostname, java.util.List<java.security.cert.Certificate> peerCertificates) throws javax.net.ssl.SSLPeerUnverifiedException
hostname
is in peerCertificates
. Does nothing if there are no certificates pinned for hostname
.
OkHttp calls this after a successful TLS handshake, but before the connection is used.javax.net.ssl.SSLPeerUnverifiedException
- if peerCertificates
don't match the certificates
pinned for hostname
.public void check(java.lang.String hostname, java.security.cert.Certificate... peerCertificates) throws javax.net.ssl.SSLPeerUnverifiedException
check(String, List)
.javax.net.ssl.SSLPeerUnverifiedException
java.util.List<CertificatePinner.Pin> findMatchingPins(java.lang.String hostname)
CertificatePinner withCertificateChainCleaner(@Nullable CertificateChainCleaner certificateChainCleaner)
certificateChainCleaner
.public static java.lang.String pin(java.security.cert.Certificate certificate)
certificate
's public key.
In OkHttp 3.1.2 and earlier, this returned a SHA-1 hash of the public key. Both types are supported, but SHA-256 is preferred.
static okio.ByteString sha1(java.security.cert.X509Certificate x509Certificate)
static okio.ByteString sha256(java.security.cert.X509Certificate x509Certificate)