Home | History | Annotate | Download | only in jsse
      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