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