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