use of okhttp3.tls.HeldCertificate in project okhttp by square.
the class CertificateChainCleanerTest method unorderedChainOfCertificatesWithRoot.
@Test
public void unorderedChainOfCertificatesWithRoot() throws Exception {
HeldCertificate root = new HeldCertificate.Builder().serialNumber(1L).certificateAuthority(2).build();
HeldCertificate certA = new HeldCertificate.Builder().serialNumber(2L).certificateAuthority(1).signedBy(root).build();
HeldCertificate certB = new HeldCertificate.Builder().serialNumber(3L).certificateAuthority(0).signedBy(certA).build();
HeldCertificate certC = new HeldCertificate.Builder().serialNumber(4L).signedBy(certB).build();
CertificateChainCleaner cleaner = CertificateChainCleaner.Companion.get(root.certificate());
assertThat(cleaner.clean(list(certC, certA, root, certB), "hostname")).isEqualTo(list(certC, certB, certA, root));
}
use of okhttp3.tls.HeldCertificate in project okhttp by square.
the class CertificateChainCleanerTest method orderedChainOfCertificatesWithoutRoot.
@Test
public void orderedChainOfCertificatesWithoutRoot() throws Exception {
HeldCertificate root = new HeldCertificate.Builder().serialNumber(1L).certificateAuthority(1).build();
HeldCertificate certA = new HeldCertificate.Builder().serialNumber(2L).certificateAuthority(0).signedBy(root).build();
HeldCertificate certB = new HeldCertificate.Builder().serialNumber(3L).signedBy(certA).build();
CertificateChainCleaner cleaner = CertificateChainCleaner.Companion.get(root.certificate());
// Root is added!
assertThat(cleaner.clean(list(certB, certA), "hostname")).isEqualTo(list(certB, certA, root));
}
use of okhttp3.tls.HeldCertificate in project okhttp by square.
the class CertificateChainCleanerTest method trustedRootNotSelfSigned.
@Test
public void trustedRootNotSelfSigned() throws Exception {
HeldCertificate unknownSigner = new HeldCertificate.Builder().serialNumber(1L).certificateAuthority(2).build();
HeldCertificate trusted = new HeldCertificate.Builder().signedBy(unknownSigner).certificateAuthority(1).serialNumber(2L).build();
HeldCertificate intermediateCa = new HeldCertificate.Builder().signedBy(trusted).certificateAuthority(0).serialNumber(3L).build();
HeldCertificate certificate = new HeldCertificate.Builder().signedBy(intermediateCa).serialNumber(4L).build();
CertificateChainCleaner cleaner = CertificateChainCleaner.Companion.get(trusted.certificate());
assertThat(cleaner.clean(list(certificate, intermediateCa), "hostname")).isEqualTo(list(certificate, intermediateCa, trusted));
assertThat(cleaner.clean(list(certificate, intermediateCa, trusted), "hostname")).isEqualTo(list(certificate, intermediateCa, trusted));
}
use of okhttp3.tls.HeldCertificate in project okhttp by square.
the class MockWebServerTest method httpsWithClientAuth.
@Test
public void httpsWithClientAuth() throws Exception {
assumeFalse(getPlatform().equals("conscrypt"));
HeldCertificate clientCa = new HeldCertificate.Builder().certificateAuthority(0).build();
HeldCertificate serverCa = new HeldCertificate.Builder().certificateAuthority(0).build();
HeldCertificate serverCertificate = new HeldCertificate.Builder().signedBy(serverCa).addSubjectAlternativeName(server.getHostName()).build();
HandshakeCertificates serverHandshakeCertificates = new HandshakeCertificates.Builder().addTrustedCertificate(clientCa.certificate()).heldCertificate(serverCertificate).build();
server.useHttps(serverHandshakeCertificates.sslSocketFactory(), false);
server.enqueue(new MockResponse().setBody("abc"));
server.requestClientAuth();
HeldCertificate clientCertificate = new HeldCertificate.Builder().signedBy(clientCa).build();
HandshakeCertificates clientHandshakeCertificates = new HandshakeCertificates.Builder().addTrustedCertificate(serverCa.certificate()).heldCertificate(clientCertificate).build();
HttpUrl url = server.url("/");
HttpsURLConnection connection = (HttpsURLConnection) url.url().openConnection();
connection.setSSLSocketFactory(clientHandshakeCertificates.sslSocketFactory());
connection.setHostnameVerifier(new RecordingHostnameVerifier());
assertThat(connection.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK);
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF_8));
assertThat(reader.readLine()).isEqualTo("abc");
RecordedRequest request = server.takeRequest();
assertThat(request.getRequestUrl().scheme()).isEqualTo("https");
Handshake handshake = request.getHandshake();
assertThat(handshake.tlsVersion()).isNotNull();
assertThat(handshake.cipherSuite()).isNotNull();
assertThat(handshake.localPrincipal()).isNotNull();
assertThat(handshake.localCertificates().size()).isEqualTo(1);
assertThat(handshake.peerPrincipal()).isNotNull();
assertThat(handshake.peerCertificates().size()).isEqualTo(1);
}
use of okhttp3.tls.HeldCertificate in project okhttp by square.
the class CertificatePinnerChainValidationTest method signersMustHaveCaBitSet.
/**
* Not checking the CA bit created a vulnerability in old OkHttp releases. It is exploited by
* triggering different chains to be discovered by the TLS engine and our chain cleaner. In this
* attack there's several different chains.
*
* <p>The victim's gets a non-CA certificate signed by a CA, and pins the CA root and/or
* intermediate. This is business as usual.
*
* <pre>{@code
*
* pinnedRoot (trusted by CertificatePinner)
* -> pinnedIntermediate (trusted by CertificatePinner)
* -> realVictim
*
* }</pre>
*
* <p>The attacker compromises a CA. They take the public key from an intermediate certificate
* signed by the compromised CA's certificate and uses it in a non-CA certificate. They ask the
* pinned CA above to sign it for non-certificate-authority uses:
*
* <pre>{@code
*
* pinnedRoot (trusted by CertificatePinner)
* -> pinnedIntermediate (trusted by CertificatePinner)
* -> attackerSwitch
*
* }</pre>
*
* <p>The attacker serves a set of certificates that yields a too-long chain in our certificate
* pinner. The served certificates (incorrectly) formed a single chain to the pinner:
*
* <pre>{@code
*
* attackerCa
* -> attackerIntermediate
* -> pinnedRoot (trusted by CertificatePinner)
* -> pinnedIntermediate (trusted by CertificatePinner)
* -> attackerSwitch (not a CA certificate!)
* -> phonyVictim
*
* }</pre>
*
* But this chain is wrong because the attackerSwitch certificate is being used in a CA role even
* though it is not a CA certificate. There are pinned certificates in the chain! The correct
* chain is much shorter because it skips the non-CA certificate.
*
* <pre>{@code
*
* attackerCa
* -> attackerIntermediate
* -> phonyVictim
*
* }</pre>
*
* Some implementations fail the TLS handshake when they see the long chain, and don't give
* CertificatePinner the opportunity to produce a different chain from their own. This includes
* the OpenJDK 11 TLS implementation, which itself fails the handshake when it encounters a non-CA
* certificate.
*/
@Test
public void signersMustHaveCaBitSet() throws Exception {
HeldCertificate attackerCa = new HeldCertificate.Builder().serialNumber(1L).certificateAuthority(4).commonName("attacker ca").build();
HeldCertificate attackerIntermediate = new HeldCertificate.Builder().serialNumber(2L).certificateAuthority(3).commonName("attacker").signedBy(attackerCa).build();
HeldCertificate pinnedRoot = new HeldCertificate.Builder().serialNumber(3L).certificateAuthority(2).commonName("pinned root").signedBy(attackerIntermediate).build();
HeldCertificate pinnedIntermediate = new HeldCertificate.Builder().serialNumber(4L).certificateAuthority(1).commonName("pinned intermediate").signedBy(pinnedRoot).build();
HeldCertificate attackerSwitch = new HeldCertificate.Builder().serialNumber(5L).keyPair(// share keys between compromised CA and leaf!
attackerIntermediate.keyPair()).commonName("attacker").addSubjectAlternativeName("attacker.com").signedBy(pinnedIntermediate).build();
HeldCertificate phonyVictim = new HeldCertificate.Builder().serialNumber(6L).signedBy(attackerSwitch).addSubjectAlternativeName("victim.com").commonName("victim").build();
CertificatePinner certificatePinner = new CertificatePinner.Builder().add(server.getHostName(), CertificatePinner.pin(pinnedRoot.certificate())).build();
HandshakeCertificates handshakeCertificates = new HandshakeCertificates.Builder().addTrustedCertificate(pinnedRoot.certificate()).addTrustedCertificate(attackerCa.certificate()).build();
OkHttpClient client = clientTestRule.newClientBuilder().sslSocketFactory(handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager()).hostnameVerifier(new RecordingHostnameVerifier()).certificatePinner(certificatePinner).build();
HandshakeCertificates serverHandshakeCertificates = new HandshakeCertificates.Builder().heldCertificate(phonyVictim, attackerSwitch.certificate(), pinnedIntermediate.certificate(), pinnedRoot.certificate(), attackerIntermediate.certificate()).build();
server.useHttps(serverHandshakeCertificates.sslSocketFactory(), false);
server.enqueue(new MockResponse());
// Make a request from client to server. It should succeed certificate checks (unfortunately the
// rogue CA is trusted) but it should fail certificate pinning.
Request request = new Request.Builder().url(server.url("/")).build();
Call call = client.newCall(request);
try (Response response = call.execute()) {
fail("expected connection failure but got " + response);
} catch (SSLPeerUnverifiedException expected) {
// Certificate pinning fails!
String message = expected.getMessage();
assertThat(message).startsWith("Certificate pinning failure!");
} catch (SSLHandshakeException expected) {
// We didn't have the opportunity to do certificate pinning because the handshake failed.
assertThat(expected).hasMessageContaining("this is not a CA certificate");
}
}
Aggregations