1/* 2 * Copyright (C) 2012 The Android Open Source Project 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 */ 16 17package org.conscrypt; 18 19import java.io.File; 20import java.io.FileWriter; 21import java.security.cert.X509Certificate; 22import java.security.KeyStore; 23import java.security.MessageDigest; 24import java.security.NoSuchAlgorithmException; 25import java.util.ArrayList; 26import java.util.List; 27import junit.framework.TestCase; 28import libcore.java.security.TestKeyStore; 29 30public class CertPinManagerTest extends TestCase { 31 32 private X509Certificate[] chain; 33 private List<X509Certificate> shortChain; 34 private List<X509Certificate> longChain; 35 private String shortPin; 36 private String longPin; 37 private List<File> tmpFiles = new ArrayList<File>(); 38 39 private String writeTmpPinFile(String text) throws Exception { 40 File tmp = File.createTempFile("pins", null); 41 FileWriter fstream = new FileWriter(tmp); 42 fstream.write(text); 43 fstream.close(); 44 tmpFiles.add(tmp); 45 return tmp.getPath(); 46 } 47 48 private static String getFingerprint(X509Certificate cert) throws NoSuchAlgorithmException { 49 MessageDigest dgst = MessageDigest.getInstance("SHA512"); 50 byte[] encoded = cert.getPublicKey().getEncoded(); 51 byte[] fingerprint = dgst.digest(encoded); 52 return IntegralToString.bytesToHexString(fingerprint, false); 53 } 54 55 @Override 56 public void setUp() throws Exception { 57 super.setUp(); 58 // build some valid chains 59 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 60 chain = (X509Certificate[]) pke.getCertificateChain(); 61 X509Certificate root = chain[2]; 62 X509Certificate server = chain[0]; 63 64 // build the short and long chains 65 shortChain = new ArrayList<X509Certificate>(); 66 shortChain.add(root); 67 longChain = new ArrayList<X509Certificate>(); 68 longChain.add(server); 69 70 // we'll use the root as the pin for the short entry and the server as the pin for the long 71 shortPin = getFingerprint(root); 72 longPin = getFingerprint(server); 73 } 74 75 @Override 76 public void tearDown() throws Exception { 77 try { 78 for (File f : tmpFiles) { 79 f.delete(); 80 } 81 tmpFiles.clear(); 82 } finally { 83 super.tearDown(); 84 } 85 } 86 87 public void testPinFileMaximumLookup() throws Exception { 88 89 // Hostnames to match 90 String longHostname = "android.clients.google.com"; 91 String shortHostname = "android.google.com"; 92 93 // Write a pinfile with two entries, one longer than the other. 94 // NOTE: "shortChain", "longChain", "shortPin", and "longPin" 95 // does not have any bearing on the test. It's simply used to 96 // distinguish the following pin entries. 97 String shortHostnameEntry = "*.google.com=true|" + shortPin; 98 String longHostnameEntry = "*.clients.google.com=true|" + longPin; 99 100 // create the pinFile 101 String path = writeTmpPinFile(shortHostnameEntry + "\n" + longHostnameEntry); 102 CertPinManager pf = new CertPinManager(path, new TrustedCertificateStore()); 103 104 assertFalse("Short entry should NOT match longer hostname", 105 pf.isChainValid(longHostname, shortChain)); 106 assertFalse("Long entry should NOT match shorter hostname", 107 pf.isChainValid(shortHostname, longChain)); 108 assertTrue("Short entry should match short hostname", 109 pf.isChainValid(shortHostname, shortChain)); 110 assertTrue("Long entry should match long name", 111 pf.isChainValid(longHostname, longChain)); 112 } 113 114 public void testPinEntryMalformedEntry() throws Exception { 115 // set up the pinEntry with a bogus entry 116 String entry = "*.google.com="; 117 try { 118 new PinListEntry(entry, new TrustedCertificateStore()); 119 fail("Accepted an empty pin list entry."); 120 } catch (PinEntryException expected) { 121 } 122 } 123 124 public void testPinEntryNull() throws Exception { 125 // set up the pinEntry with a bogus entry 126 String entry = null; 127 try { 128 new PinListEntry(entry, new TrustedCertificateStore()); 129 fail("Accepted a basically wholly bogus entry."); 130 } catch (NullPointerException expected) { 131 } 132 } 133 134 public void testPinEntryEmpty() throws Exception { 135 // set up the pinEntry with a bogus entry 136 try { 137 new PinListEntry("", new TrustedCertificateStore()); 138 fail("Accepted an empty entry."); 139 } catch (PinEntryException expected) { 140 } 141 } 142 143 public void testPinEntryPinFailure() throws Exception { 144 // write a pinfile with two entries, one longer than the other 145 String shortEntry = "*.google.com=true|" + shortPin; 146 147 // set up the pinEntry with a pinlist that doesn't match what we'll give it 148 PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 149 assertTrue("Not enforcing!", e.getEnforcing()); 150 // verify that it doesn't accept 151 boolean retval = e.isChainValid(longChain); 152 assertFalse("Accepted an incorrect pinning, this is very bad", retval); 153 } 154 155 public void testPinEntryPinSuccess() throws Exception { 156 // write a pinfile with two entries, one longer than the other 157 String shortEntry = "*.google.com=true|" + shortPin; 158 159 // set up the pinEntry with a pinlist that matches what we'll give it 160 PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 161 assertTrue("Not enforcing!", e.getEnforcing()); 162 // verify that it accepts 163 boolean retval = e.isChainValid(shortChain); 164 assertTrue("Failed on a correct pinning, this is very bad", retval); 165 } 166 167 public void testPinEntryNonEnforcing() throws Exception { 168 // write a pinfile with two entries, one longer than the other 169 String shortEntry = "*.google.com=false|" + shortPin; 170 171 // set up the pinEntry with a pinlist that matches what we'll give it 172 PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 173 assertFalse("Enforcing!", e.getEnforcing()); 174 // verify that it accepts 175 boolean retval = e.isChainValid(shortChain); 176 assertTrue("Failed on an unenforced pinning, this is bad-ish", retval); 177 } 178} 179