Home | History | Annotate | Download | only in ssl
      1 /*
      2  * Copyright (C) 2010 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 libcore.javax.net.ssl;
     18 
     19 import java.io.IOException;
     20 import java.net.InetAddress;
     21 import java.net.Socket;
     22 import java.net.UnknownHostException;
     23 import java.security.KeyStore;
     24 import java.security.Principal;
     25 import java.security.SecureRandom;
     26 import java.security.cert.Certificate;
     27 import java.security.cert.CertificateException;
     28 import java.security.cert.X509Certificate;
     29 import java.util.Collections;
     30 import javax.net.ssl.KeyManager;
     31 import javax.net.ssl.SSLContext;
     32 import javax.net.ssl.SSLServerSocket;
     33 import javax.net.ssl.SSLSocket;
     34 import javax.net.ssl.SSLSocketFactory;
     35 import javax.net.ssl.TrustManager;
     36 import javax.net.ssl.X509ExtendedKeyManager;
     37 import javax.net.ssl.X509TrustManager;
     38 import junit.framework.Assert;
     39 import libcore.java.security.StandardNames;
     40 import libcore.java.security.TestKeyStore;
     41 
     42 /**
     43  * TestSSLContext is a convenience class for other tests that
     44  * want a canned SSLContext and related state for testing so they
     45  * don't have to duplicate the logic.
     46  */
     47 public final class TestSSLContext extends Assert {
     48 
     49     /*
     50      * The RI and Android have very different default SSLSession cache behaviors.
     51      * The RI keeps an unlimited number of SSLSesions around for 1 day.
     52      * Android keeps 10 SSLSessions forever.
     53      */
     54     private static final boolean IS_RI = StandardNames.IS_RI;
     55     public static final int EXPECTED_DEFAULT_CLIENT_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 10;
     56     public static final int EXPECTED_DEFAULT_SERVER_SSL_SESSION_CACHE_SIZE = (IS_RI) ? 0 : 100;
     57     public static final int EXPECTED_DEFAULT_SSL_SESSION_CACHE_TIMEOUT = (IS_RI) ? 86400 : 0;
     58 
     59     /**
     60      * The Android SSLSocket and SSLServerSocket implementations are
     61      * based on a version of OpenSSL which includes support for RFC
     62      * 4507 session tickets. When using session tickets, the server
     63      * does not need to keep a cache mapping session IDs to SSL
     64      * sessions for reuse. Instead, the client presents the server
     65      * with a session ticket it received from the server earlier,
     66      * which is an SSL session encrypted by the server's secret
     67      * key. Since in this case the server does not need to keep a
     68      * cache, some tests may find different results depending on
     69      * whether or not the session tickets are in use. These tests can
     70      * use this function to determine if loopback SSL connections are
     71      * expected to use session tickets and conditionalize their
     72      * results appropriately.
     73      */
     74     public static boolean sslServerSocketSupportsSessionTickets () {
     75         // Disabled session tickets for better compatability b/2682876
     76         // return !IS_RI;
     77         return false;
     78     }
     79 
     80     public final KeyStore clientKeyStore;
     81     public final char[] clientStorePassword;
     82     public final KeyStore serverKeyStore;
     83     public final char[] serverStorePassword;
     84     public final X509ExtendedKeyManager clientKeyManager;
     85     public final X509ExtendedKeyManager serverKeyManager;
     86     public final X509TrustManager clientTrustManager;
     87     public final X509TrustManager serverTrustManager;
     88     public final SSLContext clientContext;
     89     public final SSLContext serverContext;
     90     public final SSLServerSocket serverSocket;
     91     public final InetAddress host;
     92     public final int port;
     93 
     94     private TestSSLContext(KeyStore clientKeyStore,
     95                            char[] clientStorePassword,
     96                            KeyStore serverKeyStore,
     97                            char[] serverStorePassword,
     98                            X509ExtendedKeyManager clientKeyManager,
     99                            X509ExtendedKeyManager serverKeyManager,
    100                            X509TrustManager clientTrustManager,
    101                            X509TrustManager serverTrustManager,
    102                            SSLContext clientContext,
    103                            SSLContext serverContext,
    104                            SSLServerSocket serverSocket,
    105                            InetAddress host,
    106                            int port) {
    107         this.clientKeyStore = clientKeyStore;
    108         this.clientStorePassword = clientStorePassword;
    109         this.serverKeyStore = serverKeyStore;
    110         this.serverStorePassword = serverStorePassword;
    111         this.clientKeyManager = clientKeyManager;
    112         this.serverKeyManager = serverKeyManager;
    113         this.clientTrustManager = clientTrustManager;
    114         this.serverTrustManager = serverTrustManager;
    115         this.clientContext = clientContext;
    116         this.serverContext = serverContext;
    117         this.serverSocket = serverSocket;
    118         this.host = host;
    119         this.port = port;
    120     }
    121 
    122     public void close() {
    123         try {
    124             serverSocket.close();
    125         } catch (Exception e) {
    126             throw new RuntimeException(e);
    127         }
    128     }
    129 
    130     /**
    131      * Usual TestSSLContext creation method, creates underlying
    132      * SSLContext with certificate and key as well as SSLServerSocket
    133      * listening provided host and port.
    134      */
    135     public static TestSSLContext create() {
    136         return create(TestKeyStore.getClient(),
    137                       TestKeyStore.getServer());
    138     }
    139 
    140     /**
    141      * TestSSLContext creation method that allows separate creation of server key store
    142      */
    143     public static TestSSLContext create(TestKeyStore client, TestKeyStore server) {
    144         String provider = StandardNames.JSSE_PROVIDER_NAME;
    145         return create(client, server, provider, provider);
    146     }
    147     public static TestSSLContext create(TestKeyStore client, TestKeyStore server,
    148                                         String clientProvider, String serverProvider) {
    149         String protocol = "TLS";
    150         SSLContext clientContext = createSSLContext(protocol, clientProvider,
    151                                                     client.keyManagers, client.trustManagers);
    152         SSLContext serverContext = createSSLContext(protocol, serverProvider,
    153                                                     server.keyManagers, server.trustManagers);
    154         return create(client.keyStore, client.storePassword,
    155                       server.keyStore, server.storePassword,
    156                       client.keyManagers[0],
    157                       server.keyManagers[0],
    158                       client.trustManagers[0],
    159                       server.trustManagers[0],
    160                       clientContext,
    161                       serverContext);
    162     }
    163 
    164     /**
    165      * TestSSLContext creation method that allows separate creation of client and server key store
    166      */
    167     public static TestSSLContext create(KeyStore clientKeyStore, char[] clientStorePassword,
    168                                         KeyStore serverKeyStore, char[] serverStorePassword,
    169                                         KeyManager clientKeyManagers,
    170                                         KeyManager serverKeyManagers,
    171                                         TrustManager clientTrustManagers,
    172                                         TrustManager serverTrustManagers,
    173                                         SSLContext clientContext,
    174                                         SSLContext serverContext) {
    175         try {
    176             SSLServerSocket serverSocket = (SSLServerSocket)
    177                 serverContext.getServerSocketFactory().createServerSocket(0);
    178             InetAddress host = InetAddress.getLocalHost();
    179             int port = serverSocket.getLocalPort();
    180 
    181             return new TestSSLContext(clientKeyStore, clientStorePassword,
    182                                       serverKeyStore, serverStorePassword,
    183                                       (X509ExtendedKeyManager) clientKeyManagers,
    184                                       (X509ExtendedKeyManager) serverKeyManagers,
    185                                       (X509TrustManager) clientTrustManagers,
    186                                       (X509TrustManager) serverTrustManagers,
    187                                       clientContext, serverContext,
    188                                       serverSocket, host, port);
    189         } catch (RuntimeException e) {
    190             throw e;
    191         } catch (Exception e) {
    192             throw new RuntimeException(e);
    193         }
    194     }
    195 
    196     /**
    197      * Create a SSLContext with a KeyManager using the private key and
    198      * certificate chain from the given KeyStore and a TrustManager
    199      * using the certificates authorities from the same KeyStore.
    200      */
    201     public static final SSLContext createSSLContext(final String protocol,
    202                                                     final String provider,
    203                                                     final KeyManager[] keyManagers,
    204                                                     final TrustManager[] trustManagers)
    205     {
    206         try {
    207             SSLContext context = SSLContext.getInstance(protocol, provider);
    208             context.init(keyManagers, trustManagers, new SecureRandom());
    209             return context;
    210         } catch (Exception e) {
    211             throw new RuntimeException(e);
    212         }
    213     }
    214 
    215     public static void assertCertificateInKeyStore(Principal principal,
    216                                                    KeyStore keyStore) throws Exception {
    217         String subjectName = principal.getName();
    218         boolean found = false;
    219         for (String alias: Collections.list(keyStore.aliases())) {
    220             if (!keyStore.isCertificateEntry(alias)) {
    221                 continue;
    222             }
    223             X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias);
    224             if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) {
    225                 found = true;
    226                 break;
    227             }
    228         }
    229         assertTrue(found);
    230     }
    231 
    232     public static void assertCertificateInKeyStore(Certificate certificate,
    233                                                    KeyStore keyStore) throws Exception {
    234         boolean found = false;
    235         for (String alias: Collections.list(keyStore.aliases())) {
    236             if (!keyStore.isCertificateEntry(alias)) {
    237                 continue;
    238             }
    239             Certificate keyStoreCertificate = keyStore.getCertificate(alias);
    240             if (certificate.equals(keyStoreCertificate)) {
    241                 found = true;
    242                 break;
    243             }
    244         }
    245         assertTrue(found);
    246     }
    247 
    248     public static void assertServerCertificateChain(X509TrustManager trustManager,
    249                                                     Certificate[] serverChain)
    250             throws CertificateException {
    251         X509Certificate[] chain = (X509Certificate[]) serverChain;
    252         trustManager.checkServerTrusted(chain, chain[0].getPublicKey().getAlgorithm());
    253     }
    254 
    255     public static void assertClientCertificateChain(X509TrustManager trustManager,
    256                                                     Certificate[] clientChain)
    257             throws CertificateException {
    258         X509Certificate[] chain = (X509Certificate[]) clientChain;
    259         trustManager.checkClientTrusted(chain, chain[0].getPublicKey().getAlgorithm());
    260     }
    261 
    262     /**
    263      * Returns an SSLSocketFactory that calls setWantClientAuth and
    264      * setNeedClientAuth as specified on all returned sockets.
    265      */
    266     public static SSLSocketFactory clientAuth(final SSLSocketFactory sf,
    267                                               final boolean want,
    268                                               final boolean need) {
    269         return new SSLSocketFactory() {
    270             private SSLSocket set(Socket socket) {
    271                 SSLSocket s = (SSLSocket) socket;
    272                 s.setWantClientAuth(want);
    273                 s.setNeedClientAuth(need);
    274                 return s;
    275             }
    276             public Socket createSocket(String host, int port)
    277                     throws IOException, UnknownHostException {
    278                 return set(sf.createSocket(host, port));
    279             }
    280             public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
    281                     throws IOException, UnknownHostException {
    282                 return set(sf.createSocket(host, port, localHost, localPort));
    283             }
    284             public Socket createSocket(InetAddress host, int port) throws IOException {
    285                 return set(sf.createSocket(host, port));
    286             }
    287             public Socket createSocket(InetAddress address, int port,
    288                                        InetAddress localAddress, int localPort) throws IOException {
    289                 return set(sf.createSocket(address, port));
    290             }
    291 
    292             public String[] getDefaultCipherSuites() {
    293                 return sf.getDefaultCipherSuites();
    294             }
    295             public String[] getSupportedCipherSuites() {
    296                 return sf.getSupportedCipherSuites();
    297             }
    298 
    299             public Socket createSocket(Socket s, String host, int port, boolean autoClose)
    300                     throws IOException {
    301                 return set(sf.createSocket(s, host, port, autoClose));
    302             }
    303         };
    304     }
    305 }
    306