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