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