blob: 91b5a590f13256b3faf1f3a5692ba2dbfe5fc4f2 [file] [log] [blame]
/*
* Copyright (C) 2014 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.okhttp;
import com.squareup.okhttp.internal.SslContextBuilder;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.Set;
import javax.net.ssl.SSLPeerUnverifiedException;
import okio.ByteString;
import org.junit.Test;
import static com.squareup.okhttp.TestUtil.setOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public final class CertificatePinnerTest {
static SslContextBuilder sslContextBuilder;
static KeyPair keyPairA;
static X509Certificate keypairACertificate1;
static String keypairACertificate1Pin;
static ByteString keypairACertificate1PinBase64;
static KeyPair keyPairB;
static X509Certificate keypairBCertificate1;
static String keypairBCertificate1Pin;
static ByteString keypairBCertificate1PinBase64;
static KeyPair keyPairC;
static X509Certificate keypairCCertificate1;
static String keypairCCertificate1Pin;
static {
try {
sslContextBuilder = new SslContextBuilder("example.com");
keyPairA = sslContextBuilder.generateKeyPair();
keypairACertificate1 = sslContextBuilder.selfSignedCertificate(keyPairA, "1");
keypairACertificate1Pin = CertificatePinner.pin(keypairACertificate1);
keypairACertificate1PinBase64 = pinToBase64(keypairACertificate1Pin);
keyPairB = sslContextBuilder.generateKeyPair();
keypairBCertificate1 = sslContextBuilder.selfSignedCertificate(keyPairB, "1");
keypairBCertificate1Pin = CertificatePinner.pin(keypairBCertificate1);
keypairBCertificate1PinBase64 = pinToBase64(keypairBCertificate1Pin);
keyPairC = sslContextBuilder.generateKeyPair();
keypairCCertificate1 = sslContextBuilder.selfSignedCertificate(keyPairC, "1");
keypairCCertificate1Pin = CertificatePinner.pin(keypairCCertificate1);
} catch (GeneralSecurityException e) {
throw new AssertionError(e);
}
}
static ByteString pinToBase64(String pin) {
return ByteString.decodeBase64(pin.substring("sha1/".length()));
}
@Test public void malformedPin() throws Exception {
CertificatePinner.Builder builder = new CertificatePinner.Builder();
try {
builder.add("example.com", "md5/DmxUShsZuNiqPQsX2Oi9uv2sCnw=");
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test public void malformedBase64() throws Exception {
CertificatePinner.Builder builder = new CertificatePinner.Builder();
try {
builder.add("example.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw*");
fail();
} catch (IllegalArgumentException expected) {
}
}
/** Multiple certificates generated from the same keypair have the same pin. */
@Test public void sameKeypairSamePin() throws Exception {
X509Certificate keypairACertificate2 = sslContextBuilder.selfSignedCertificate(keyPairA, "2");
String keypairACertificate2Pin = CertificatePinner.pin(keypairACertificate2);
X509Certificate keypairBCertificate2 = sslContextBuilder.selfSignedCertificate(keyPairB, "2");
String keypairBCertificate2Pin = CertificatePinner.pin(keypairBCertificate2);
assertTrue(keypairACertificate1Pin.equals(keypairACertificate2Pin));
assertTrue(keypairBCertificate1Pin.equals(keypairBCertificate2Pin));
assertFalse(keypairACertificate1Pin.equals(keypairBCertificate1Pin));
}
@Test public void successfulCheck() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", keypairACertificate1Pin)
.build();
certificatePinner.check("example.com", keypairACertificate1);
}
@Test public void successfulMatchAcceptsAnyMatchingCertificate() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", keypairBCertificate1Pin)
.build();
certificatePinner.check("example.com", keypairACertificate1, keypairBCertificate1);
}
@Test public void unsuccessfulCheck() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", keypairACertificate1Pin)
.build();
try {
certificatePinner.check("example.com", keypairBCertificate1);
fail();
} catch (SSLPeerUnverifiedException expected) {
}
}
@Test public void multipleCertificatesForOneHostname() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", keypairACertificate1Pin, keypairBCertificate1Pin)
.build();
certificatePinner.check("example.com", keypairACertificate1);
certificatePinner.check("example.com", keypairBCertificate1);
}
@Test public void multipleHostnamesForOneCertificate() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", keypairACertificate1Pin)
.add("www.example.com", keypairACertificate1Pin)
.build();
certificatePinner.check("example.com", keypairACertificate1);
certificatePinner.check("www.example.com", keypairACertificate1);
}
@Test public void absentHostnameMatches() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder().build();
certificatePinner.check("example.com", keypairACertificate1);
}
@Test public void successfulCheckForWildcardHostname() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairACertificate1Pin)
.build();
certificatePinner.check("a.example.com", keypairACertificate1);
}
@Test public void successfulMatchAcceptsAnyMatchingCertificateForWildcardHostname() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairBCertificate1Pin)
.build();
certificatePinner.check("a.example.com", keypairACertificate1, keypairBCertificate1);
}
@Test public void unsuccessfulCheckForWildcardHostname() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairACertificate1Pin)
.build();
try {
certificatePinner.check("a.example.com", keypairBCertificate1);
fail();
} catch (SSLPeerUnverifiedException expected) {
}
}
@Test public void multipleCertificatesForOneWildcardHostname() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairACertificate1Pin, keypairBCertificate1Pin)
.build();
certificatePinner.check("a.example.com", keypairACertificate1);
certificatePinner.check("a.example.com", keypairBCertificate1);
}
@Test public void successfulCheckForOneHostnameWithWildcardAndDirectCertificate() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairACertificate1Pin)
.add("a.example.com", keypairBCertificate1Pin)
.build();
certificatePinner.check("a.example.com", keypairACertificate1);
certificatePinner.check("a.example.com", keypairBCertificate1);
}
@Test public void unsuccessfulCheckForOneHostnameWithWildcardAndDirectCertificate() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairACertificate1Pin)
.add("a.example.com", keypairBCertificate1Pin)
.build();
try {
certificatePinner.check("a.example.com", keypairCCertificate1);
fail();
} catch (SSLPeerUnverifiedException expected) {
}
}
@Test public void successfulFindMatchingPins() {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("first.com", keypairACertificate1Pin, keypairBCertificate1Pin)
.add("second.com", keypairCCertificate1Pin)
.build();
Set<ByteString> expectedPins = setOf(keypairACertificate1PinBase64, keypairBCertificate1PinBase64);
Set<ByteString> matchedPins = certificatePinner.findMatchingPins("first.com");
assertEquals(expectedPins, matchedPins);
}
@Test public void successfulFindMatchingPinsForWildcardAndDirectCertificates() {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairACertificate1Pin)
.add("a.example.com", keypairBCertificate1Pin)
.add("b.example.com", keypairCCertificate1Pin)
.build();
Set<ByteString> expectedPins = setOf(keypairACertificate1PinBase64, keypairBCertificate1PinBase64);
Set<ByteString> matchedPins = certificatePinner.findMatchingPins("a.example.com");
assertEquals(expectedPins, matchedPins);
}
@Test public void wildcardHostnameShouldNotMatchThroughDot() throws Exception {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("*.example.com", keypairACertificate1Pin)
.build();
assertNull(certificatePinner.findMatchingPins("example.com"));
assertNull(certificatePinner.findMatchingPins("a.b.example.com"));
}
}