1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/* 2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2014 Square, Inc. 3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License. 6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at 7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software 11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and 14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License. 15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpackage com.squareup.okhttp; 17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 18e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.SslContextBuilder; 19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.security.GeneralSecurityException; 20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.security.KeyPair; 21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.security.cert.X509Certificate; 22e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport javax.net.ssl.SSLPeerUnverifiedException; 23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Test; 24e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertFalse; 26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertTrue; 27e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.fail; 28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class CertificatePinnerTest { 30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static SslContextBuilder sslContextBuilder; 31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static KeyPair keyPairA; 33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static X509Certificate keypairACertificate1; 34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static String keypairACertificate1Pin; 35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static KeyPair keyPairB; 37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static X509Certificate keypairBCertificate1; 38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static String keypairBCertificate1Pin; 39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static { 41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sslContextBuilder = new SslContextBuilder("example.com"); 43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller keyPairA = sslContextBuilder.generateKeyPair(); 45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller keypairACertificate1 = sslContextBuilder.selfSignedCertificate(keyPairA, "1"); 46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller keypairACertificate1Pin = CertificatePinner.pin(keypairACertificate1); 47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 48e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller keyPairB = sslContextBuilder.generateKeyPair(); 49e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller keypairBCertificate1 = sslContextBuilder.selfSignedCertificate(keyPairB, "1"); 50e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller keypairBCertificate1Pin = CertificatePinner.pin(keypairBCertificate1); 51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (GeneralSecurityException e) { 52e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new AssertionError(e); 53e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 55e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 56e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void malformedPin() throws Exception { 57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner.Builder builder = new CertificatePinner.Builder(); 58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller builder.add("example.com", "md5/DmxUShsZuNiqPQsX2Oi9uv2sCnw="); 60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (IllegalArgumentException expected) { 62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void malformedBase64() throws Exception { 66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner.Builder builder = new CertificatePinner.Builder(); 67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller builder.add("example.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw*"); 69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (IllegalArgumentException expected) { 71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Multiple certificates generated from the same keypair have the same pin. */ 75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void sameKeypairSamePin() throws Exception { 76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller X509Certificate keypairACertificate2 = sslContextBuilder.selfSignedCertificate(keyPairA, "2"); 77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller String keypairACertificate2Pin = CertificatePinner.pin(keypairACertificate2); 78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller X509Certificate keypairBCertificate2 = sslContextBuilder.selfSignedCertificate(keyPairB, "2"); 80e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller String keypairBCertificate2Pin = CertificatePinner.pin(keypairBCertificate2); 81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(keypairACertificate1Pin.equals(keypairACertificate2Pin)); 83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(keypairBCertificate1Pin.equals(keypairBCertificate2Pin)); 84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertFalse(keypairACertificate1Pin.equals(keypairBCertificate1Pin)); 85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void successfulCheck() throws Exception { 88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner certificatePinner = new CertificatePinner.Builder() 89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .add("example.com", keypairACertificate1Pin) 90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .build(); 91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("example.com", keypairACertificate1); 93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void successfulMatchAcceptsAnyMatchingCertificate() throws Exception { 96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner certificatePinner = new CertificatePinner.Builder() 97e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .add("example.com", keypairBCertificate1Pin) 98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .build(); 99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("example.com", keypairACertificate1, keypairBCertificate1); 101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void unsuccessfulCheck() throws Exception { 104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner certificatePinner = new CertificatePinner.Builder() 105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .add("example.com", keypairACertificate1Pin) 106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .build(); 107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("example.com", keypairBCertificate1); 110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (SSLPeerUnverifiedException expected) { 112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void multipleCertificatesForOneHostname() throws Exception { 116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner certificatePinner = new CertificatePinner.Builder() 117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .add("example.com", keypairACertificate1Pin, keypairBCertificate1Pin) 118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .build(); 119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("example.com", keypairACertificate1); 121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("example.com", keypairBCertificate1); 122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void multipleHostnamesForOneCertificate() throws Exception { 125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner certificatePinner = new CertificatePinner.Builder() 126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .add("example.com", keypairACertificate1Pin) 127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .add("www.example.com", keypairACertificate1Pin) 128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .build(); 129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("example.com", keypairACertificate1); 131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("www.example.com", keypairACertificate1); 132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void absentHostnameMatches() throws Exception { 135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller CertificatePinner certificatePinner = new CertificatePinner.Builder().build(); 136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller certificatePinner.check("example.com", keypairACertificate1); 137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller} 139