1 /* 2 * Copyright (C) 2011 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.apache.harmony.xnet.provider.jsse; 18 19 import java.io.File; 20 import java.io.FileWriter; 21 import java.security.cert.CertificateException; 22 import java.security.cert.X509Certificate; 23 import java.security.KeyStore; 24 import java.security.MessageDigest; 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.List; 28 import javax.net.ssl.TrustManager; 29 import javax.net.ssl.TrustManagerFactory; 30 import javax.net.ssl.X509TrustManager; 31 import junit.framework.TestCase; 32 import libcore.java.security.TestKeyStore; 33 34 public class TrustManagerImplTest extends TestCase { 35 36 private List<File> tmpFiles = new ArrayList<File>(); 37 38 private String getFingerprint(X509Certificate cert) throws Exception { 39 MessageDigest dgst = MessageDigest.getInstance("SHA512"); 40 byte[] encoded = cert.getPublicKey().getEncoded(); 41 byte[] fingerprint = dgst.digest(encoded); 42 return IntegralToString.bytesToHexString(fingerprint, false); 43 } 44 45 private String writeTmpPinFile(String text) throws Exception { 46 File tmp = File.createTempFile("pins", null); 47 FileWriter fstream = new FileWriter(tmp); 48 fstream.write(text); 49 fstream.close(); 50 tmpFiles.add(tmp); 51 return tmp.getPath(); 52 } 53 54 @Override 55 public void tearDown() throws Exception { 56 try { 57 for (File f : tmpFiles) { 58 f.delete(); 59 } 60 tmpFiles.clear(); 61 } finally { 62 super.tearDown(); 63 } 64 } 65 66 /** 67 * Ensure that our non-standard behavior of learning to trust new 68 * intermediate CAs does not regress. http://b/3404902 69 */ 70 public void testLearnIntermediate() throws Exception { 71 // chain3 should be server/intermediate/root 72 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 73 X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain(); 74 X509Certificate root = chain3[2]; 75 X509Certificate intermediate = chain3[1]; 76 X509Certificate server = chain3[0]; 77 X509Certificate[] chain2 = new X509Certificate[] { server, intermediate }; 78 X509Certificate[] chain1 = new X509Certificate[] { server }; 79 80 // Normal behavior 81 assertValid(chain3, trustManager(root)); 82 assertValid(chain2, trustManager(root)); 83 assertInvalid(chain1, trustManager(root)); 84 assertValid(chain3, trustManager(intermediate)); 85 assertValid(chain2, trustManager(intermediate)); 86 assertValid(chain1, trustManager(intermediate)); 87 assertValid(chain3, trustManager(server)); 88 assertValid(chain2, trustManager(server)); 89 assertValid(chain1, trustManager(server)); 90 91 // non-standard behavior 92 X509TrustManager tm = trustManager(root); 93 // fail on short chain with only root trusted 94 assertInvalid(chain1, tm); 95 // succeed on longer chain, learn intermediate 96 assertValid(chain2, tm); 97 // now we can validate the short chain 98 assertValid(chain1, tm); 99 } 100 101 public void testGetFullChain() throws Exception { 102 // build the trust manager 103 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 104 X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain(); 105 X509Certificate root = chain3[2]; 106 X509TrustManager tm = trustManager(root); 107 108 // build the chains we'll use for testing 109 X509Certificate intermediate = chain3[1]; 110 X509Certificate server = chain3[0]; 111 X509Certificate[] chain2 = new X509Certificate[] { server, intermediate }; 112 X509Certificate[] chain1 = new X509Certificate[] { server }; 113 114 assertTrue(tm instanceof TrustManagerImpl); 115 TrustManagerImpl tmi = (TrustManagerImpl) tm; 116 List<X509Certificate> certs = tmi.checkServerTrusted(chain2, "RSA", "purple.com"); 117 assertEquals(Arrays.asList(chain3), certs); 118 certs = tmi.checkServerTrusted(chain1, "RSA", "purple.com"); 119 assertEquals(Arrays.asList(chain3), certs); 120 } 121 122 public void testCertPinning() throws Exception { 123 // chain3 should be server/intermediate/root 124 KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA"); 125 X509Certificate[] chain3 = (X509Certificate[]) pke.getCertificateChain(); 126 X509Certificate root = chain3[2]; 127 X509Certificate intermediate = chain3[1]; 128 X509Certificate server = chain3[0]; 129 X509Certificate[] chain2 = new X509Certificate[] { server, intermediate }; 130 X509Certificate[] chain1 = new X509Certificate[] { server }; 131 132 // test without a hostname, expecting failure 133 assertInvalidPinned(chain1, trustManager(root, "gugle.com", root), null); 134 // test without a hostname, expecting success 135 assertValidPinned(chain3, trustManager(root, "gugle.com", root), null, chain3); 136 // test an unpinned hostname that should fail 137 assertInvalidPinned(chain1, trustManager(root, "gugle.com", root), "purple.com"); 138 // test an unpinned hostname that should succeed 139 assertValidPinned(chain3, trustManager(root, "gugle.com", root), "purple.com", chain3); 140 // test a pinned hostname that should fail 141 assertInvalidPinned(chain1, trustManager(intermediate, "gugle.com", root), "gugle.com"); 142 // test a pinned hostname that should succeed 143 assertValidPinned(chain2, trustManager(intermediate, "gugle.com", server), "gugle.com", 144 chain2); 145 } 146 147 private X509TrustManager trustManager(X509Certificate ca) throws Exception { 148 KeyStore keyStore = TestKeyStore.createKeyStore(); 149 keyStore.setCertificateEntry("alias", ca); 150 151 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); 152 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); 153 tmf.init(keyStore); 154 return (X509TrustManager) tmf.getTrustManagers()[0]; 155 } 156 157 private TrustManagerImpl trustManager(X509Certificate ca, String hostname, X509Certificate pin) 158 throws Exception { 159 // build the cert pin manager 160 CertPinManager cm = certManager(hostname, pin); 161 // insert it into the trust manager 162 KeyStore keyStore = TestKeyStore.createKeyStore(); 163 keyStore.setCertificateEntry("alias", ca); 164 return new TrustManagerImpl(keyStore, cm); 165 } 166 167 private CertPinManager certManager(String hostname, X509Certificate pin) throws Exception { 168 String pinString = ""; 169 if (pin != null) { 170 pinString = hostname + "=true|" + getFingerprint(pin); 171 } 172 // write it to a pinfile 173 String path = writeTmpPinFile(pinString); 174 // build the certpinmanager 175 return new CertPinManager(path, new TrustedCertificateStore()); 176 } 177 178 private void assertValid(X509Certificate[] chain, X509TrustManager tm) throws Exception { 179 if (tm instanceof TrustManagerImpl) { 180 TrustManagerImpl tmi = (TrustManagerImpl) tm; 181 tmi.checkServerTrusted(chain, "RSA"); 182 } 183 tm.checkServerTrusted(chain, "RSA"); 184 } 185 186 private void assertValidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname, 187 X509Certificate[] fullChain) throws Exception { 188 if (tm instanceof TrustManagerImpl) { 189 TrustManagerImpl tmi = (TrustManagerImpl) tm; 190 List<X509Certificate> checkedChain = tmi.checkServerTrusted(chain, "RSA", hostname); 191 assertEquals(checkedChain, Arrays.asList(fullChain)); 192 } 193 tm.checkServerTrusted(chain, "RSA"); 194 } 195 196 private void assertInvalid(X509Certificate[] chain, X509TrustManager tm) { 197 try { 198 tm.checkClientTrusted(chain, "RSA"); 199 fail(); 200 } catch (CertificateException expected) { 201 } 202 try { 203 tm.checkServerTrusted(chain, "RSA"); 204 fail(); 205 } catch (CertificateException expected) { 206 } 207 } 208 209 private void assertInvalidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname) 210 throws Exception { 211 assertTrue(tm.getClass().getName(), tm instanceof TrustManagerImpl); 212 try { 213 TrustManagerImpl tmi = (TrustManagerImpl) tm; 214 tmi.checkServerTrusted(chain, "RSA", hostname); 215 fail(); 216 } catch (CertificateException expected) { 217 } 218 } 219 } 220