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.apache.harmony.xnet.provider.jsse; 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 // write a pinfile with two entries, one longer than the other 90 String shortEntry = "*.google.com=true|" + shortPin; 91 String longEntry = "*.clients.google.com=true|" + longPin; 92 93 // create the pinFile 94 String path = writeTmpPinFile(shortEntry + "\n" + longEntry); 95 CertPinManager pf = new CertPinManager(path, new TrustedCertificateStore()); 96 97 // verify that the shorter chain doesn't work for a name matching the longer 98 assertTrue("short chain long uri failed", 99 pf.chainIsNotPinned("android.clients.google.com", shortChain)); 100 // verify that the longer chain doesn't work for a name matching the shorter 101 assertTrue("long chain short uri failed", 102 pf.chainIsNotPinned("android.google.com", longChain)); 103 // verify that the shorter chain works for the shorter domain 104 assertTrue("short chain short uri failed", 105 !pf.chainIsNotPinned("android.google.com", shortChain)); 106 // and the same for the longer 107 assertTrue("long chain long uri failed", 108 !pf.chainIsNotPinned("android.clients.google.com", longChain)); 109 } 110 111 public void testPinEntryMalformedEntry() throws Exception { 112 // set up the pinEntry with a bogus entry 113 String entry = "*.google.com="; 114 try { 115 new PinListEntry(entry, new TrustedCertificateStore()); 116 fail("Accepted an empty pin list entry."); 117 } catch (PinEntryException expected) { 118 } 119 } 120 121 public void testPinEntryNull() throws Exception { 122 // set up the pinEntry with a bogus entry 123 String entry = null; 124 try { 125 new PinListEntry(entry, new TrustedCertificateStore()); 126 fail("Accepted a basically wholly bogus entry."); 127 } catch (NullPointerException expected) { 128 } 129 } 130 131 public void testPinEntryEmpty() throws Exception { 132 // set up the pinEntry with a bogus entry 133 try { 134 new PinListEntry("", new TrustedCertificateStore()); 135 fail("Accepted an empty entry."); 136 } catch (PinEntryException expected) { 137 } 138 } 139 140 public void testPinEntryPinFailure() throws Exception { 141 // write a pinfile with two entries, one longer than the other 142 String shortEntry = "*.google.com=true|" + shortPin; 143 144 // set up the pinEntry with a pinlist that doesn't match what we'll give it 145 PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 146 assertTrue("Not enforcing!", e.getEnforcing()); 147 // verify that it doesn't accept 148 boolean retval = e.chainIsNotPinned(longChain); 149 assertTrue("Accepted an incorrect pinning, this is very bad", retval); 150 } 151 152 public void testPinEntryPinSuccess() throws Exception { 153 // write a pinfile with two entries, one longer than the other 154 String shortEntry = "*.google.com=true|" + shortPin; 155 156 // set up the pinEntry with a pinlist that matches what we'll give it 157 PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 158 assertTrue("Not enforcing!", e.getEnforcing()); 159 // verify that it accepts 160 boolean retval = e.chainIsNotPinned(shortChain); 161 assertTrue("Failed on a correct pinning, this is very bad", !retval); 162 } 163 164 public void testPinEntryNonEnforcing() throws Exception { 165 // write a pinfile with two entries, one longer than the other 166 String shortEntry = "*.google.com=false|" + shortPin; 167 168 // set up the pinEntry with a pinlist that matches what we'll give it 169 PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 170 assertFalse("Enforcing!", e.getEnforcing()); 171 // verify that it accepts 172 boolean retval = e.chainIsNotPinned(shortChain); 173 assertTrue("Failed on an unenforced pinning, this is bad-ish", !retval); 174 } 175} 176