16d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra/* 26d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * Copyright (C) 2012 The Android Open Source Project 36d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * 46d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * Licensed under the Apache License, Version 2.0 (the "License"); 56d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * you may not use this file except in compliance with the License. 66d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * You may obtain a copy of the License at 76d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * 86d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * http://www.apache.org/licenses/LICENSE-2.0 96d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * 106d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * Unless required by applicable law or agreed to in writing, software 116d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * distributed under the License is distributed on an "AS IS" BASIS, 126d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * See the License for the specific language governing permissions and 146d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra * limitations under the License. 156d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra */ 166d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 176d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condrapackage org.apache.harmony.xnet.provider.jsse; 186d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 196d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.io.File; 206d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.io.FileWriter; 216d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.security.cert.X509Certificate; 226d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.security.KeyStore; 236d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.security.MessageDigest; 246d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.security.NoSuchAlgorithmException; 256d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.util.ArrayList; 266d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport java.util.List; 276d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport junit.framework.TestCase; 286d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condraimport libcore.java.security.TestKeyStore; 296d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 306d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condrapublic class CertPinManagerTest extends TestCase { 316d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 326d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private X509Certificate[] chain; 336d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private List<X509Certificate> shortChain; 346d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private List<X509Certificate> longChain; 356d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private String shortPin; 366d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private String longPin; 376d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private List<File> tmpFiles = new ArrayList<File>(); 386d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 396d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private String writeTmpPinFile(String text) throws Exception { 406d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra File tmp = File.createTempFile("pins", null); 416d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra FileWriter fstream = new FileWriter(tmp); 426d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra fstream.write(text); 436d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra fstream.close(); 446d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra tmpFiles.add(tmp); 456d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra return tmp.getPath(); 466d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 476d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 486d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra private static String getFingerprint(X509Certificate cert) throws NoSuchAlgorithmException { 496d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra MessageDigest dgst = MessageDigest.getInstance("SHA512"); 506d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra byte[] encoded = cert.getPublicKey().getEncoded(); 516d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra byte[] fingerprint = dgst.digest(encoded); 526d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra return IntegralToString.bytesToHexString(fingerprint, false); 536d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 546d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 556d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra @Override 566d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void setUp() throws Exception { 576d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra super.setUp(); 586d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // build some valid chains 596d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 606d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra chain = (X509Certificate[]) pke.getCertificateChain(); 616d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra X509Certificate root = chain[2]; 626d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra X509Certificate server = chain[0]; 636d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 646d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // build the short and long chains 656d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra shortChain = new ArrayList<X509Certificate>(); 666d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra shortChain.add(root); 676d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra longChain = new ArrayList<X509Certificate>(); 686d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra longChain.add(server); 696d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 706d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // we'll use the root as the pin for the short entry and the server as the pin for the long 716d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra shortPin = getFingerprint(root); 726d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra longPin = getFingerprint(server); 736d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 746d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 756d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra @Override 766d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void tearDown() throws Exception { 776d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra try { 786d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra for (File f : tmpFiles) { 796d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra f.delete(); 806d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 816d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra tmpFiles.clear(); 826d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } finally { 836d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra super.tearDown(); 846d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 856d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 866d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 876d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void testPinFileMaximumLookup() throws Exception { 886d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 896d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // write a pinfile with two entries, one longer than the other 906d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String shortEntry = "*.google.com=true|" + shortPin; 916d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String longEntry = "*.clients.google.com=true|" + longPin; 926d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 936d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // create the pinFile 946d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String path = writeTmpPinFile(shortEntry + "\n" + longEntry); 95924af71bb26b7c35f702de9a3425109c73184a53Geremy Condra CertPinManager pf = new CertPinManager(path, new TrustedCertificateStore()); 966d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 976d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // verify that the shorter chain doesn't work for a name matching the longer 986d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("short chain long uri failed", 996d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra pf.chainIsNotPinned("android.clients.google.com", shortChain)); 1006d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // verify that the longer chain doesn't work for a name matching the shorter 1016d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("long chain short uri failed", 1026d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra pf.chainIsNotPinned("android.google.com", longChain)); 1036d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // verify that the shorter chain works for the shorter domain 1046d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("short chain short uri failed", 1056d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra !pf.chainIsNotPinned("android.google.com", shortChain)); 1066d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // and the same for the longer 1076d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("long chain long uri failed", 1086d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra !pf.chainIsNotPinned("android.clients.google.com", longChain)); 1096d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1106d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1116d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void testPinEntryMalformedEntry() throws Exception { 1126d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // set up the pinEntry with a bogus entry 1136d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String entry = "*.google.com="; 1146d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra try { 115924af71bb26b7c35f702de9a3425109c73184a53Geremy Condra new PinListEntry(entry, new TrustedCertificateStore()); 1166d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra fail("Accepted an empty pin list entry."); 1176d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } catch (PinEntryException expected) { 1186d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1196d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1206d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1216d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void testPinEntryNull() throws Exception { 1226d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // set up the pinEntry with a bogus entry 1236d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String entry = null; 1246d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra try { 125924af71bb26b7c35f702de9a3425109c73184a53Geremy Condra new PinListEntry(entry, new TrustedCertificateStore()); 1266d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra fail("Accepted a basically wholly bogus entry."); 1276d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } catch (NullPointerException expected) { 1286d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1296d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1306d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1316d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void testPinEntryEmpty() throws Exception { 1326d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // set up the pinEntry with a bogus entry 1336d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra try { 134924af71bb26b7c35f702de9a3425109c73184a53Geremy Condra new PinListEntry("", new TrustedCertificateStore()); 1356d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra fail("Accepted an empty entry."); 1366d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } catch (PinEntryException expected) { 1376d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1386d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1396d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1406d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void testPinEntryPinFailure() throws Exception { 1416d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // write a pinfile with two entries, one longer than the other 1426d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String shortEntry = "*.google.com=true|" + shortPin; 1436d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1446d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // set up the pinEntry with a pinlist that doesn't match what we'll give it 145924af71bb26b7c35f702de9a3425109c73184a53Geremy Condra PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 1466d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("Not enforcing!", e.getEnforcing()); 1476d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // verify that it doesn't accept 1486d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra boolean retval = e.chainIsNotPinned(longChain); 1496d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("Accepted an incorrect pinning, this is very bad", retval); 1506d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1516d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1526d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void testPinEntryPinSuccess() throws Exception { 1536d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // write a pinfile with two entries, one longer than the other 1546d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String shortEntry = "*.google.com=true|" + shortPin; 1556d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1566d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // set up the pinEntry with a pinlist that matches what we'll give it 157924af71bb26b7c35f702de9a3425109c73184a53Geremy Condra PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 1586d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("Not enforcing!", e.getEnforcing()); 1596d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // verify that it accepts 1606d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra boolean retval = e.chainIsNotPinned(shortChain); 1616d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("Failed on a correct pinning, this is very bad", !retval); 1626d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1636d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1646d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra public void testPinEntryNonEnforcing() throws Exception { 1656d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // write a pinfile with two entries, one longer than the other 1666d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra String shortEntry = "*.google.com=false|" + shortPin; 1676d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra 1686d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // set up the pinEntry with a pinlist that matches what we'll give it 169924af71bb26b7c35f702de9a3425109c73184a53Geremy Condra PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); 1706d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertFalse("Enforcing!", e.getEnforcing()); 1716d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra // verify that it accepts 1726d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra boolean retval = e.chainIsNotPinned(shortChain); 1736d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra assertTrue("Failed on an unenforced pinning, this is bad-ish", !retval); 1746d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra } 1756d2a17ab04ab0967e3bff7fe6280066ef66d1d76Geremy Condra} 176