Home | History | Annotate | Download | only in conscrypt
      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.conscrypt;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertTrue;
     21 import static org.junit.Assert.fail;
     22 
     23 import java.io.IOException;
     24 import java.security.KeyStore;
     25 import java.security.Principal;
     26 import java.security.cert.Certificate;
     27 import java.security.cert.CertificateException;
     28 import java.security.cert.X509Certificate;
     29 import java.util.Arrays;
     30 import java.util.List;
     31 import javax.net.ssl.HandshakeCompletedListener;
     32 import javax.net.ssl.HttpsURLConnection;
     33 import javax.net.ssl.SSLParameters;
     34 import javax.net.ssl.SSLPeerUnverifiedException;
     35 import javax.net.ssl.SSLSession;
     36 import javax.net.ssl.SSLSessionContext;
     37 import javax.net.ssl.SSLSocket;
     38 import javax.net.ssl.X509TrustManager;
     39 import org.conscrypt.java.security.TestKeyStore;
     40 import org.junit.Test;
     41 import org.junit.runner.RunWith;
     42 import org.junit.runners.JUnit4;
     43 
     44 @RunWith(JUnit4.class)
     45 public class TrustManagerImplTest {
     46 
     47     /**
     48      * Ensure that our non-standard behavior of learning to trust new
     49      * intermediate CAs does not regress. http://b/3404902
     50      */
     51     @Test
     52     public void testLearnIntermediate() throws Exception {
     53         TestUtils.assumeExtendedTrustManagerAvailable();
     54         // chain3 should be server/intermediate/root
     55         KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
     56         X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain();
     57         X509Certificate root = chain3[2];
     58         X509Certificate intermediate = chain3[1];
     59         X509Certificate server = chain3[0];
     60         X509Certificate[] chain2 =  new X509Certificate[] { server, intermediate };
     61         X509Certificate[] chain1 =  new X509Certificate[] { server };
     62 
     63         // Normal behavior
     64         assertValid(chain3,   trustManager(root));
     65         assertValid(chain2,   trustManager(root));
     66         assertInvalid(chain1, trustManager(root));
     67         assertValid(chain3,   trustManager(intermediate));
     68         assertValid(chain2,   trustManager(intermediate));
     69         assertValid(chain1,   trustManager(intermediate));
     70         assertValid(chain3,   trustManager(server));
     71         assertValid(chain2,   trustManager(server));
     72         assertValid(chain1,   trustManager(server));
     73 
     74         // non-standard behavior
     75         X509TrustManager tm = trustManager(root);
     76         // fail on short chain with only root trusted
     77         assertInvalid(chain1, tm);
     78         // succeed on longer chain, learn intermediate
     79         assertValid(chain2, tm);
     80         // now we can validate the short chain
     81         assertValid(chain1, tm);
     82     }
     83 
     84     // We should ignore duplicate cruft in the certificate chain
     85     // See https://code.google.com/p/android/issues/detail?id=52295 http://b/8313312
     86     @Test
     87     public void testDuplicateInChain() throws Exception {
     88         TestUtils.assumeExtendedTrustManagerAvailable();
     89         // chain3 should be server/intermediate/root
     90         KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
     91         X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain();
     92         X509Certificate root = chain3[2];
     93         X509Certificate intermediate = chain3[1];
     94         X509Certificate server = chain3[0];
     95 
     96         X509Certificate[] chain4 = new X509Certificate[] { server, intermediate,
     97                                                            server, intermediate
     98         };
     99         assertValid(chain4, trustManager(root));
    100     }
    101 
    102     @Test
    103     public void testGetFullChain() throws Exception {
    104         TestUtils.assumeExtendedTrustManagerAvailable();
    105         // build the trust manager
    106         KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
    107         X509Certificate[] chain3 = (X509Certificate[]) pke.getCertificateChain();
    108         X509Certificate root = chain3[2];
    109         X509TrustManager tm = trustManager(root);
    110 
    111         // build the chains we'll use for testing
    112         X509Certificate intermediate = chain3[1];
    113         X509Certificate server = chain3[0];
    114         X509Certificate[] chain2 =  new X509Certificate[] { server, intermediate };
    115         X509Certificate[] chain1 =  new X509Certificate[] { server };
    116 
    117         assertTrue(tm instanceof TrustManagerImpl);
    118         TrustManagerImpl tmi = (TrustManagerImpl) tm;
    119         List<X509Certificate> certs = tmi.checkServerTrusted(chain2, "RSA", new FakeSSLSession(
    120                 "purple.com"));
    121         assertEquals(Arrays.asList(chain3), certs);
    122         certs = tmi.checkServerTrusted(chain1, "RSA", new FakeSSLSession("purple.com"));
    123         assertEquals(Arrays.asList(chain3), certs);
    124     }
    125 
    126     @Test
    127     public void testHttpsEndpointIdentification() throws Exception {
    128         TestUtils.assumeExtendedTrustManagerAvailable();
    129 
    130         KeyStore.PrivateKeyEntry pke = TestKeyStore.getServerHostname().getPrivateKey("RSA", "RSA");
    131         X509Certificate[] chain = (X509Certificate[]) pke.getCertificateChain();
    132         X509Certificate root = chain[2];
    133         TrustManagerImpl tmi = (TrustManagerImpl) trustManager(root);
    134 
    135         String goodHostname = TestKeyStore.CERT_HOSTNAME;
    136         String badHostname = "definitelywrong.nopenopenope";
    137 
    138         // The default hostname verifier on OpenJDK rejects all hostnames, so use our own
    139         javax.net.ssl.HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier();
    140         try {
    141             HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
    142 
    143             SSLParameters params = new SSLParameters();
    144 
    145             // Without endpoint identification this should pass despite the mismatched hostname
    146             params.setEndpointIdentificationAlgorithm(null);
    147 
    148             List<X509Certificate> certs = tmi.getTrustedChainForServer(chain, "RSA",
    149                 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
    150             assertEquals(Arrays.asList(chain), certs);
    151 
    152             // Turn on endpoint identification
    153             params.setEndpointIdentificationAlgorithm("HTTPS");
    154 
    155             try {
    156                 tmi.getTrustedChainForServer(chain, "RSA",
    157                     new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
    158             } catch (CertificateException expected) {
    159             }
    160 
    161             certs = tmi.getTrustedChainForServer(chain, "RSA",
    162                 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
    163             assertEquals(Arrays.asList(chain), certs);
    164 
    165             // Override the global default hostname verifier with a Conscrypt-specific one that
    166             // always passes.  Both scenarios should pass.
    167             Conscrypt.setDefaultHostnameVerifier(new ConscryptHostnameVerifier() {
    168                 @Override public boolean verify(String s, SSLSession sslSession) { return true; }
    169             });
    170 
    171             certs = tmi.getTrustedChainForServer(chain, "RSA",
    172                 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
    173             assertEquals(Arrays.asList(chain), certs);
    174 
    175             certs = tmi.getTrustedChainForServer(chain, "RSA",
    176                 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
    177             assertEquals(Arrays.asList(chain), certs);
    178 
    179             // Now set an instance-specific verifier on the trust manager.  The bad hostname should
    180             // fail again.
    181             Conscrypt.setHostnameVerifier(tmi, new TestHostnameVerifier());
    182 
    183             try {
    184                 tmi.getTrustedChainForServer(chain, "RSA",
    185                     new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
    186             } catch (CertificateException expected) {
    187             }
    188 
    189             certs = tmi.getTrustedChainForServer(chain, "RSA",
    190                 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
    191             assertEquals(Arrays.asList(chain), certs);
    192 
    193             // Remove the instance-specific verifier, and both should pass again.
    194             Conscrypt.setHostnameVerifier(tmi, null);
    195 
    196             certs = tmi.getTrustedChainForServer(chain, "RSA",
    197                 new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
    198             assertEquals(Arrays.asList(chain), certs);
    199 
    200             certs = tmi.getTrustedChainForServer(chain, "RSA",
    201                 new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
    202             assertEquals(Arrays.asList(chain), certs);
    203         } finally {
    204             Conscrypt.setDefaultHostnameVerifier(null);
    205             HttpsURLConnection.setDefaultHostnameVerifier(oldDefault);
    206         }
    207     }
    208 
    209     private X509TrustManager trustManager(X509Certificate ca) throws Exception {
    210         KeyStore keyStore = TestKeyStore.createKeyStore();
    211         keyStore.setCertificateEntry("alias", ca);
    212 
    213         return new TrustManagerImpl(keyStore);
    214     }
    215 
    216     private void assertValid(X509Certificate[] chain, X509TrustManager tm) throws Exception {
    217         if (tm instanceof TrustManagerImpl) {
    218             TrustManagerImpl tmi = (TrustManagerImpl) tm;
    219             tmi.checkServerTrusted(chain, "RSA");
    220         }
    221         tm.checkServerTrusted(chain, "RSA");
    222     }
    223 
    224     private void assertInvalid(X509Certificate[] chain, X509TrustManager tm) {
    225         try {
    226             tm.checkClientTrusted(chain, "RSA");
    227             fail();
    228         } catch (CertificateException expected) {
    229             // Expected.
    230         }
    231         try {
    232             tm.checkServerTrusted(chain, "RSA");
    233             fail();
    234         } catch (CertificateException expected) {
    235             // Expected.
    236         }
    237     }
    238 
    239     private static class FakeSSLSession implements SSLSession {
    240         private final String hostname;
    241         private final X509Certificate[] peerCerts;
    242 
    243         FakeSSLSession(String hostname) {
    244             this.hostname = hostname;
    245             peerCerts = null;
    246         }
    247 
    248         FakeSSLSession(String hostname, X509Certificate[] peerCerts) {
    249             this.hostname = hostname;
    250             this.peerCerts = peerCerts.clone();
    251         }
    252 
    253         @Override
    254         public int getApplicationBufferSize() {
    255             throw new UnsupportedOperationException();
    256         }
    257 
    258         @Override
    259         public String getCipherSuite() {
    260             throw new UnsupportedOperationException();
    261         }
    262 
    263         @Override
    264         public long getCreationTime() {
    265             throw new UnsupportedOperationException();
    266         }
    267 
    268         @Override
    269         public byte[] getId() {
    270             throw new UnsupportedOperationException();
    271         }
    272 
    273         @Override
    274         public long getLastAccessedTime() {
    275             throw new UnsupportedOperationException();
    276         }
    277 
    278         @Override
    279         public Certificate[] getLocalCertificates() {
    280             throw new UnsupportedOperationException();
    281         }
    282 
    283         @Override
    284         public Principal getLocalPrincipal() {
    285             throw new UnsupportedOperationException();
    286         }
    287 
    288         @Override
    289         public int getPacketBufferSize() {
    290             throw new UnsupportedOperationException();
    291         }
    292 
    293         @Override
    294         public javax.security.cert.X509Certificate[] getPeerCertificateChain()
    295                 throws SSLPeerUnverifiedException {
    296             throw new UnsupportedOperationException();
    297         }
    298 
    299         @Override
    300         public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
    301             if (peerCerts == null) {
    302                 throw new SSLPeerUnverifiedException("Null peerCerts");
    303             } else {
    304                 return peerCerts.clone();
    305             }
    306         }
    307 
    308         @Override
    309         public String getPeerHost() {
    310             return hostname;
    311         }
    312 
    313         @Override
    314         public int getPeerPort() {
    315             throw new UnsupportedOperationException();
    316         }
    317 
    318         @Override
    319         public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
    320             throw new UnsupportedOperationException();
    321         }
    322 
    323         @Override
    324         public String getProtocol() {
    325             throw new UnsupportedOperationException();
    326         }
    327 
    328         @Override
    329         public SSLSessionContext getSessionContext() {
    330             throw new UnsupportedOperationException();
    331         }
    332 
    333         @Override
    334         public Object getValue(String name) {
    335             throw new UnsupportedOperationException();
    336         }
    337 
    338         @Override
    339         public String[] getValueNames() {
    340             throw new UnsupportedOperationException();
    341         }
    342 
    343         @Override
    344         public void invalidate() {
    345             throw new UnsupportedOperationException();
    346         }
    347 
    348         @Override
    349         public boolean isValid() {
    350             throw new UnsupportedOperationException();
    351         }
    352 
    353         @Override
    354         public void putValue(String name, Object value) {
    355             throw new UnsupportedOperationException();
    356         }
    357 
    358         @Override
    359         public void removeValue(String name) {
    360             throw new UnsupportedOperationException();
    361         }
    362     }
    363 
    364     private static class FakeSSLSocket extends SSLSocket {
    365 
    366         private final SSLSession session;
    367         private final SSLParameters parameters;
    368 
    369         public FakeSSLSocket(SSLSession session, SSLParameters parameters) {
    370             this.session = session;
    371             this.parameters = parameters;
    372         }
    373 
    374         @Override
    375         public SSLParameters getSSLParameters() {
    376             return parameters;
    377         }
    378 
    379         @Override
    380         public String[] getSupportedCipherSuites() {
    381             throw new UnsupportedOperationException();
    382         }
    383 
    384         @Override
    385         public String[] getEnabledCipherSuites() {
    386             throw new UnsupportedOperationException();
    387         }
    388 
    389         @Override
    390         public void setEnabledCipherSuites(String[] strings) {
    391             throw new UnsupportedOperationException();
    392         }
    393 
    394         @Override
    395         public String[] getSupportedProtocols() {
    396             throw new UnsupportedOperationException();
    397         }
    398 
    399         @Override
    400         public String[] getEnabledProtocols() {
    401             throw new UnsupportedOperationException();
    402         }
    403 
    404         @Override
    405         public void setEnabledProtocols(String[] strings) {
    406             throw new UnsupportedOperationException();
    407         }
    408 
    409         @Override
    410         public SSLSession getSession() {
    411             return session;
    412         }
    413 
    414         @Override
    415         public SSLSession getHandshakeSession() {
    416             return session;
    417         }
    418 
    419         @Override
    420         public void addHandshakeCompletedListener(
    421             HandshakeCompletedListener handshakeCompletedListener) {
    422             throw new UnsupportedOperationException();
    423         }
    424 
    425         @Override
    426         public void removeHandshakeCompletedListener(
    427             HandshakeCompletedListener handshakeCompletedListener) {
    428             throw new UnsupportedOperationException();
    429         }
    430 
    431         @Override
    432         public void startHandshake() throws IOException {
    433             throw new UnsupportedOperationException();
    434         }
    435 
    436         @Override
    437         public void setUseClientMode(boolean b) {
    438             throw new UnsupportedOperationException();
    439         }
    440 
    441         @Override
    442         public boolean getUseClientMode() {
    443             throw new UnsupportedOperationException();
    444         }
    445 
    446         @Override
    447         public void setNeedClientAuth(boolean b) {
    448             throw new UnsupportedOperationException();
    449         }
    450 
    451         @Override
    452         public boolean getNeedClientAuth() {
    453             throw new UnsupportedOperationException();
    454         }
    455 
    456         @Override
    457         public void setWantClientAuth(boolean b) {
    458             throw new UnsupportedOperationException();
    459         }
    460 
    461         @Override
    462         public boolean getWantClientAuth() {
    463             throw new UnsupportedOperationException();
    464         }
    465 
    466         @Override
    467         public void setEnableSessionCreation(boolean b) {
    468             throw new UnsupportedOperationException();
    469         }
    470 
    471         @Override
    472         public boolean getEnableSessionCreation() {
    473             throw new UnsupportedOperationException();
    474         }
    475     }
    476 
    477     private static class TestHostnameVerifier
    478         extends org.conscrypt.javax.net.ssl.TestHostnameVerifier
    479         implements ConscryptHostnameVerifier {}
    480 }
    481