• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package org.conscrypt;
18 
19 import java.io.File;
20 import java.io.FileWriter;
21 import java.security.cert.X509Certificate;
22 import java.security.KeyStore;
23 import java.security.MessageDigest;
24 import java.security.NoSuchAlgorithmException;
25 import java.util.ArrayList;
26 import java.util.List;
27 import junit.framework.TestCase;
28 import libcore.java.security.TestKeyStore;
29 
30 public 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 
writeTmpPinFile(String text)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 
getFingerprint(X509Certificate cert)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
setUp()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
tearDown()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 
testPinFileMaximumLookup()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 
testPinEntryMalformedEntry()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 
testPinEntryNull()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 
testPinEntryEmpty()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 
testPinEntryPinFailure()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 
testPinEntryPinSuccess()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 
testPinEntryNonEnforcing()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