Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package org.conscrypt;
     19 
     20 import java.security.KeyManagementException;
     21 import java.security.KeyStore;
     22 import java.security.KeyStoreException;
     23 import java.security.NoSuchAlgorithmException;
     24 import java.security.SecureRandom;
     25 import java.security.UnrecoverableKeyException;
     26 import java.util.Arrays;
     27 import javax.net.ssl.KeyManager;
     28 import javax.net.ssl.KeyManagerFactory;
     29 import javax.net.ssl.TrustManager;
     30 import javax.net.ssl.TrustManagerFactory;
     31 import javax.net.ssl.X509KeyManager;
     32 import javax.net.ssl.X509TrustManager;
     33 
     34 /**
     35  * The instances of this class encapsulate all the info
     36  * about enabled cipher suites and protocols,
     37  * as well as the information about client/server mode of
     38  * ssl socket, whether it require/want client authentication or not,
     39  * and controls whether new SSL sessions may be established by this
     40  * socket or not.
     41  */
     42 public class SSLParametersImpl implements Cloneable {
     43 
     44     // default source of authentication keys
     45     private static volatile X509KeyManager defaultKeyManager;
     46     // default source of authentication trust decisions
     47     private static volatile X509TrustManager defaultTrustManager;
     48     // default source of random numbers
     49     private static volatile SecureRandom defaultSecureRandom;
     50     // default SSL parameters
     51     private static volatile SSLParametersImpl defaultParameters;
     52 
     53     // client session context contains the set of reusable
     54     // client-side SSL sessions
     55     private final ClientSessionContext clientSessionContext;
     56     // server session context contains the set of reusable
     57     // server-side SSL sessions
     58     private final ServerSessionContext serverSessionContext;
     59     // source of authentication keys
     60     private X509KeyManager keyManager;
     61     // source of authentication trust decisions
     62     private X509TrustManager trustManager;
     63     // source of random numbers
     64     private SecureRandom secureRandom;
     65 
     66     // cipher suites available for SSL connection
     67     private CipherSuite[] enabledCipherSuites;
     68     // string representations of available cipher suites
     69     private String[] enabledCipherSuiteNames = null;
     70 
     71     // protocols available for SSL connection
     72     private String[] enabledProtocols = ProtocolVersion.supportedProtocols;
     73 
     74     // if the peer with this parameters tuned to work in client mode
     75     private boolean client_mode = true;
     76     // if the peer with this parameters tuned to require client authentication
     77     private boolean need_client_auth = false;
     78     // if the peer with this parameters tuned to request client authentication
     79     private boolean want_client_auth = false;
     80     // if the peer with this parameters allowed to cteate new SSL session
     81     private boolean enable_session_creation = true;
     82 
     83     protected CipherSuite[] getEnabledCipherSuitesMember() {
     84         if (enabledCipherSuites == null) {
     85             this.enabledCipherSuites = CipherSuite.DEFAULT_CIPHER_SUITES;
     86         }
     87         return enabledCipherSuites;
     88     }
     89 
     90     /**
     91      * Initializes the parameters. Naturally this constructor is used
     92      * in SSLContextImpl.engineInit method which directly passes its
     93      * parameters. In other words this constructor holds all
     94      * the functionality provided by SSLContext.init method.
     95      * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
     96      * SecureRandom)} for more information
     97      */
     98     protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
     99             SecureRandom sr, ClientSessionContext clientSessionContext,
    100             ServerSessionContext serverSessionContext)
    101             throws KeyManagementException {
    102         this.serverSessionContext = serverSessionContext;
    103         this.clientSessionContext = clientSessionContext;
    104 
    105         // It's not described by the spec of SSLContext what should happen
    106         // if the arrays of length 0 are specified. This implementation
    107         // behave as for null arrays (i.e. use installed security providers)
    108 
    109         // initialize keyManager
    110         if ((kms == null) || (kms.length == 0)) {
    111             keyManager = getDefaultKeyManager();
    112         } else {
    113             keyManager = findX509KeyManager(kms);
    114         }
    115 
    116         // initialize trustManager
    117         if ((tms == null) || (tms.length == 0)) {
    118             trustManager = getDefaultTrustManager();
    119         } else {
    120             trustManager = findX509TrustManager(tms);
    121         }
    122         // initialize secure random
    123         // BEGIN android-removed
    124         // if (sr == null) {
    125         //     if (defaultSecureRandom == null) {
    126         //         defaultSecureRandom = new SecureRandom();
    127         //     }
    128         //     secureRandom = defaultSecureRandom;
    129         // } else {
    130         //     secureRandom = sr;
    131         // }
    132         // END android-removed
    133         // BEGIN android-added
    134         // We simply use the SecureRandom passed in by the caller. If it's
    135         // null, we don't replace it by a new instance. The native code below
    136         // then directly accesses /dev/urandom. Not the most elegant solution,
    137         // but faster than going through the SecureRandom object.
    138         secureRandom = sr;
    139         // END android-added
    140     }
    141 
    142     protected static SSLParametersImpl getDefault() throws KeyManagementException {
    143         SSLParametersImpl result = defaultParameters;
    144         if (result == null) {
    145             // single-check idiom
    146             defaultParameters = result = new SSLParametersImpl(null,
    147                                                                null,
    148                                                                null,
    149                                                                new ClientSessionContext(),
    150                                                                new ServerSessionContext());
    151         }
    152         return (SSLParametersImpl) result.clone();
    153     }
    154 
    155     /**
    156      * @return server session context
    157      */
    158     protected ServerSessionContext getServerSessionContext() {
    159         return serverSessionContext;
    160     }
    161 
    162     /**
    163      * @return client session context
    164      */
    165     protected ClientSessionContext getClientSessionContext() {
    166         return clientSessionContext;
    167     }
    168 
    169     /**
    170      * @return key manager
    171      */
    172     protected X509KeyManager getKeyManager() {
    173         return keyManager;
    174     }
    175 
    176     /**
    177      * @return trust manager
    178      */
    179     protected X509TrustManager getTrustManager() {
    180         return trustManager;
    181     }
    182 
    183     /**
    184      * @return secure random
    185      */
    186     protected SecureRandom getSecureRandom() {
    187         if (secureRandom != null) {
    188             return secureRandom;
    189         }
    190         SecureRandom result = defaultSecureRandom;
    191         if (result == null) {
    192             // single-check idiom
    193             defaultSecureRandom = result = new SecureRandom();
    194         }
    195         secureRandom = result;
    196         return secureRandom;
    197     }
    198 
    199     /**
    200      * @return the secure random member reference, even it is null
    201      */
    202     protected SecureRandom getSecureRandomMember() {
    203         return secureRandom;
    204     }
    205 
    206     /**
    207      * @return the names of enabled cipher suites
    208      */
    209     protected String[] getEnabledCipherSuites() {
    210         if (enabledCipherSuiteNames == null) {
    211             CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember();
    212             enabledCipherSuiteNames = new String[enabledCipherSuites.length];
    213             for (int i = 0; i< enabledCipherSuites.length; i++) {
    214                 enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName();
    215             }
    216         }
    217         return enabledCipherSuiteNames.clone();
    218     }
    219 
    220     /**
    221      * Sets the set of available cipher suites for use in SSL connection.
    222      * @param   suites: String[]
    223      * @return
    224      */
    225     protected void setEnabledCipherSuites(String[] suites) {
    226         if (suites == null) {
    227             throw new IllegalArgumentException("suites == null");
    228         }
    229         CipherSuite[] cipherSuites = new CipherSuite[suites.length];
    230         for (int i=0; i<suites.length; i++) {
    231             String suite = suites[i];
    232             if (suite == null) {
    233                 throw new IllegalArgumentException("suites[" + i + "] == null");
    234             }
    235             cipherSuites[i] = CipherSuite.getByName(suite);
    236             if (cipherSuites[i] == null || !cipherSuites[i].supported) {
    237                 throw new IllegalArgumentException(suite + " is not supported.");
    238             }
    239         }
    240         enabledCipherSuites = cipherSuites;
    241         enabledCipherSuiteNames = suites;
    242     }
    243 
    244     /**
    245      * @return the set of enabled protocols
    246      */
    247     protected String[] getEnabledProtocols() {
    248         return enabledProtocols.clone();
    249     }
    250 
    251     /**
    252      * Sets the set of available protocols for use in SSL connection.
    253      * @param protocols String[]
    254      */
    255     protected void setEnabledProtocols(String[] protocols) {
    256         if (protocols == null) {
    257             throw new IllegalArgumentException("protocols == null");
    258         }
    259         for (int i=0; i<protocols.length; i++) {
    260             String protocol = protocols[i];
    261             if (protocol == null) {
    262                 throw new IllegalArgumentException("protocols[" + i + "] == null");
    263             }
    264             if (!ProtocolVersion.isSupported(protocol)) {
    265                 throw new IllegalArgumentException("Protocol " + protocol + " is not supported.");
    266             }
    267         }
    268         enabledProtocols = protocols;
    269     }
    270 
    271     /**
    272      * Tunes the peer holding this parameters to work in client mode.
    273      * @param   mode if the peer is configured to work in client mode
    274      */
    275     protected void setUseClientMode(boolean mode) {
    276         client_mode = mode;
    277     }
    278 
    279     /**
    280      * Returns the value indicating if the parameters configured to work
    281      * in client mode.
    282      */
    283     protected boolean getUseClientMode() {
    284         return client_mode;
    285     }
    286 
    287     /**
    288      * Tunes the peer holding this parameters to require client authentication
    289      */
    290     protected void setNeedClientAuth(boolean need) {
    291         need_client_auth = need;
    292         // reset the want_client_auth setting
    293         want_client_auth = false;
    294     }
    295 
    296     /**
    297      * Returns the value indicating if the peer with this parameters tuned
    298      * to require client authentication
    299      */
    300     protected boolean getNeedClientAuth() {
    301         return need_client_auth;
    302     }
    303 
    304     /**
    305      * Tunes the peer holding this parameters to request client authentication
    306      */
    307     protected void setWantClientAuth(boolean want) {
    308         want_client_auth = want;
    309         // reset the need_client_auth setting
    310         need_client_auth = false;
    311     }
    312 
    313     /**
    314      * Returns the value indicating if the peer with this parameters
    315      * tuned to request client authentication
    316      * @return
    317      */
    318     protected boolean getWantClientAuth() {
    319         return want_client_auth;
    320     }
    321 
    322     /**
    323      * Allows/disallows the peer holding this parameters to
    324      * create new SSL session
    325      */
    326     protected void setEnableSessionCreation(boolean flag) {
    327         enable_session_creation = flag;
    328     }
    329 
    330     /**
    331      * Returns the value indicating if the peer with this parameters
    332      * allowed to cteate new SSL session
    333      */
    334     protected boolean getEnableSessionCreation() {
    335         return enable_session_creation;
    336     }
    337 
    338     /**
    339      * Returns the clone of this object.
    340      * @return the clone.
    341      */
    342     @Override
    343     protected Object clone() {
    344         try {
    345             return super.clone();
    346         } catch (CloneNotSupportedException e) {
    347             throw new AssertionError(e);
    348         }
    349     }
    350 
    351     private static X509KeyManager getDefaultKeyManager() throws KeyManagementException {
    352         X509KeyManager result = defaultKeyManager;
    353         if (result == null) {
    354             // single-check idiom
    355             defaultKeyManager = result = createDefaultKeyManager();
    356         }
    357         return result;
    358     }
    359     private static X509KeyManager createDefaultKeyManager() throws KeyManagementException {
    360         try {
    361             String algorithm = KeyManagerFactory.getDefaultAlgorithm();
    362             KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
    363             kmf.init(null, null);
    364             KeyManager[] kms = kmf.getKeyManagers();
    365             return findX509KeyManager(kms);
    366         } catch (NoSuchAlgorithmException e) {
    367             throw new KeyManagementException(e);
    368         } catch (KeyStoreException e) {
    369             throw new KeyManagementException(e);
    370         } catch (UnrecoverableKeyException e) {
    371             throw new KeyManagementException(e);
    372         }
    373     }
    374     private static X509KeyManager findX509KeyManager(KeyManager[] kms) throws KeyManagementException {
    375         for (KeyManager km : kms) {
    376             if (km instanceof X509KeyManager) {
    377                 return (X509KeyManager)km;
    378             }
    379         }
    380         throw new KeyManagementException("Failed to find an X509KeyManager in " + Arrays.toString(kms));
    381     }
    382 
    383     /**
    384      * Gets the default trust manager.
    385      *
    386      * TODO: Move this to a published API under dalvik.system.
    387      */
    388     public static X509TrustManager getDefaultTrustManager() throws KeyManagementException {
    389         X509TrustManager result = defaultTrustManager;
    390         if (result == null) {
    391             // single-check idiom
    392             defaultTrustManager = result = createDefaultTrustManager();
    393         }
    394         return result;
    395     }
    396     private static X509TrustManager createDefaultTrustManager() throws KeyManagementException {
    397         try {
    398             String algorithm = TrustManagerFactory.getDefaultAlgorithm();
    399             TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
    400             tmf.init((KeyStore) null);
    401             TrustManager[] tms = tmf.getTrustManagers();
    402             X509TrustManager trustManager = findX509TrustManager(tms);
    403             return trustManager;
    404         } catch (NoSuchAlgorithmException e) {
    405             throw new KeyManagementException(e);
    406         } catch (KeyStoreException e) {
    407             throw new KeyManagementException(e);
    408         }
    409     }
    410     private static X509TrustManager findX509TrustManager(TrustManager[] tms) throws KeyManagementException {
    411         for (TrustManager tm : tms) {
    412             if (tm instanceof X509TrustManager) {
    413                 return (X509TrustManager)tm;
    414             }
    415         }
    416         throw new KeyManagementException("Failed to find an X509TrustManager in " +  Arrays.toString(tms));
    417     }
    418 }
    419