| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.conscrypt; |
| |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.security.cert.X509Certificate; |
| import java.security.KeyStore; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import junit.framework.TestCase; |
| import libcore.java.security.TestKeyStore; |
| |
| public class CertPinManagerTest extends TestCase { |
| |
| private X509Certificate[] chain; |
| private List<X509Certificate> shortChain; |
| private List<X509Certificate> longChain; |
| private String shortPin; |
| private String longPin; |
| private List<File> tmpFiles = new ArrayList<File>(); |
| |
| private String writeTmpPinFile(String text) throws Exception { |
| File tmp = File.createTempFile("pins", null); |
| FileWriter fstream = new FileWriter(tmp); |
| fstream.write(text); |
| fstream.close(); |
| tmpFiles.add(tmp); |
| return tmp.getPath(); |
| } |
| |
| private static String getFingerprint(X509Certificate cert) throws NoSuchAlgorithmException { |
| MessageDigest dgst = MessageDigest.getInstance("SHA512"); |
| byte[] encoded = cert.getPublicKey().getEncoded(); |
| byte[] fingerprint = dgst.digest(encoded); |
| return IntegralToString.bytesToHexString(fingerprint, false); |
| } |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| // build some valid chains |
| KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); |
| chain = (X509Certificate[]) pke.getCertificateChain(); |
| X509Certificate root = chain[2]; |
| X509Certificate server = chain[0]; |
| |
| // build the short and long chains |
| shortChain = new ArrayList<X509Certificate>(); |
| shortChain.add(root); |
| longChain = new ArrayList<X509Certificate>(); |
| longChain.add(server); |
| |
| // we'll use the root as the pin for the short entry and the server as the pin for the long |
| shortPin = getFingerprint(root); |
| longPin = getFingerprint(server); |
| } |
| |
| @Override |
| public void tearDown() throws Exception { |
| try { |
| for (File f : tmpFiles) { |
| f.delete(); |
| } |
| tmpFiles.clear(); |
| } finally { |
| super.tearDown(); |
| } |
| } |
| |
| public void testPinFileMaximumLookup() throws Exception { |
| |
| // Hostnames to match |
| String longHostname = "android.clients.google.com"; |
| String shortHostname = "android.google.com"; |
| |
| // Write a pinfile with two entries, one longer than the other. |
| // NOTE: "shortChain", "longChain", "shortPin", and "longPin" |
| // does not have any bearing on the test. It's simply used to |
| // distinguish the following pin entries. |
| String shortHostnameEntry = "*.google.com=true|" + shortPin; |
| String longHostnameEntry = "*.clients.google.com=true|" + longPin; |
| |
| // create the pinFile |
| String path = writeTmpPinFile(shortHostnameEntry + "\n" + longHostnameEntry); |
| CertPinManager pf = new CertPinManager(path, new TrustedCertificateStore()); |
| |
| assertFalse("Short entry should NOT match longer hostname", |
| pf.isChainValid(longHostname, shortChain)); |
| assertFalse("Long entry should NOT match shorter hostname", |
| pf.isChainValid(shortHostname, longChain)); |
| assertTrue("Short entry should match short hostname", |
| pf.isChainValid(shortHostname, shortChain)); |
| assertTrue("Long entry should match long name", |
| pf.isChainValid(longHostname, longChain)); |
| } |
| |
| public void testPinEntryMalformedEntry() throws Exception { |
| // set up the pinEntry with a bogus entry |
| String entry = "*.google.com="; |
| try { |
| new PinListEntry(entry, new TrustedCertificateStore()); |
| fail("Accepted an empty pin list entry."); |
| } catch (PinEntryException expected) { |
| } |
| } |
| |
| public void testPinEntryNull() throws Exception { |
| // set up the pinEntry with a bogus entry |
| String entry = null; |
| try { |
| new PinListEntry(entry, new TrustedCertificateStore()); |
| fail("Accepted a basically wholly bogus entry."); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| public void testPinEntryEmpty() throws Exception { |
| // set up the pinEntry with a bogus entry |
| try { |
| new PinListEntry("", new TrustedCertificateStore()); |
| fail("Accepted an empty entry."); |
| } catch (PinEntryException expected) { |
| } |
| } |
| |
| public void testPinEntryPinFailure() throws Exception { |
| // write a pinfile with two entries, one longer than the other |
| String shortEntry = "*.google.com=true|" + shortPin; |
| |
| // set up the pinEntry with a pinlist that doesn't match what we'll give it |
| PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); |
| assertTrue("Not enforcing!", e.getEnforcing()); |
| // verify that it doesn't accept |
| boolean retval = e.isChainValid(longChain); |
| assertFalse("Accepted an incorrect pinning, this is very bad", retval); |
| } |
| |
| public void testPinEntryPinSuccess() throws Exception { |
| // write a pinfile with two entries, one longer than the other |
| String shortEntry = "*.google.com=true|" + shortPin; |
| |
| // set up the pinEntry with a pinlist that matches what we'll give it |
| PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); |
| assertTrue("Not enforcing!", e.getEnforcing()); |
| // verify that it accepts |
| boolean retval = e.isChainValid(shortChain); |
| assertTrue("Failed on a correct pinning, this is very bad", retval); |
| } |
| |
| public void testPinEntryNonEnforcing() throws Exception { |
| // write a pinfile with two entries, one longer than the other |
| String shortEntry = "*.google.com=false|" + shortPin; |
| |
| // set up the pinEntry with a pinlist that matches what we'll give it |
| PinListEntry e = new PinListEntry(shortEntry, new TrustedCertificateStore()); |
| assertFalse("Enforcing!", e.getEnforcing()); |
| // verify that it accepts |
| boolean retval = e.isChainValid(shortChain); |
| assertTrue("Failed on an unenforced pinning, this is bad-ish", retval); |
| } |
| } |