Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2008 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 android.net;
     18 
     19 import android.os.SystemProperties;
     20 import android.util.Log;
     21 
     22 import com.android.internal.os.RoSystemProperties;
     23 import com.android.org.conscrypt.Conscrypt;
     24 import com.android.org.conscrypt.OpenSSLContextImpl;
     25 import com.android.org.conscrypt.OpenSSLSocketImpl;
     26 import com.android.org.conscrypt.SSLClientSessionCache;
     27 import java.io.IOException;
     28 import java.net.InetAddress;
     29 import java.net.Socket;
     30 import java.net.SocketException;
     31 import java.security.KeyManagementException;
     32 import java.security.PrivateKey;
     33 import java.security.cert.X509Certificate;
     34 import javax.net.SocketFactory;
     35 import javax.net.ssl.HostnameVerifier;
     36 import javax.net.ssl.HttpsURLConnection;
     37 import javax.net.ssl.KeyManager;
     38 import javax.net.ssl.SSLException;
     39 import javax.net.ssl.SSLPeerUnverifiedException;
     40 import javax.net.ssl.SSLSession;
     41 import javax.net.ssl.SSLSocket;
     42 import javax.net.ssl.SSLSocketFactory;
     43 import javax.net.ssl.TrustManager;
     44 import javax.net.ssl.X509TrustManager;
     45 
     46 /**
     47  * SSLSocketFactory implementation with several extra features:
     48  *
     49  * <ul>
     50  * <li>Timeout specification for SSL handshake operations
     51  * <li>Hostname verification in most cases (see WARNINGs below)
     52  * <li>Optional SSL session caching with {@link SSLSessionCache}
     53  * <li>Optionally bypass all SSL certificate checks
     54  * </ul>
     55  *
     56  * The handshake timeout does not apply to actual TCP socket connection.
     57  * If you want a connection timeout as well, use {@link #createSocket()}
     58  * and {@link Socket#connect(SocketAddress, int)}, after which you
     59  * must verify the identity of the server you are connected to.
     60  *
     61  * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not
     62  * verify the server's identity, allowing man-in-the-middle attacks.</b>
     63  * This implementation does check the server's certificate hostname, but only
     64  * for createSocket variants that specify a hostname.  When using methods that
     65  * use {@link InetAddress} or which return an unconnected socket, you MUST
     66  * verify the server's identity yourself to ensure a secure connection.
     67  *
     68  * Refer to
     69  * <a href="https://developer.android.com/training/articles/security-gms-provider.html">
     70  * Updating Your Security Provider to Protect Against SSL Exploits</a>
     71  * for further information.</p>
     72  *
     73  * <p>One way to verify the server's identity is to use
     74  * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
     75  * {@link HostnameVerifier} to verify the certificate hostname.
     76  *
     77  * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
     78  * SSL certificate and hostname checks for testing purposes.  This setting
     79  * requires root access.
     80  */
     81 public class SSLCertificateSocketFactory extends SSLSocketFactory {
     82     private static final String TAG = "SSLCertificateSocketFactory";
     83 
     84     private static final TrustManager[] INSECURE_TRUST_MANAGER = new TrustManager[] {
     85         new X509TrustManager() {
     86             public X509Certificate[] getAcceptedIssuers() { return null; }
     87             public void checkClientTrusted(X509Certificate[] certs, String authType) { }
     88             public void checkServerTrusted(X509Certificate[] certs, String authType) { }
     89         }
     90     };
     91 
     92     private SSLSocketFactory mInsecureFactory = null;
     93     private SSLSocketFactory mSecureFactory = null;
     94     private TrustManager[] mTrustManagers = null;
     95     private KeyManager[] mKeyManagers = null;
     96     private byte[] mNpnProtocols = null;
     97     private byte[] mAlpnProtocols = null;
     98     private PrivateKey mChannelIdPrivateKey = null;
     99 
    100     private final int mHandshakeTimeoutMillis;
    101     private final SSLClientSessionCache mSessionCache;
    102     private final boolean mSecure;
    103 
    104     /** @deprecated Use {@link #getDefault(int)} instead. */
    105     @Deprecated
    106     public SSLCertificateSocketFactory(int handshakeTimeoutMillis) {
    107         this(handshakeTimeoutMillis, null, true);
    108     }
    109 
    110     private SSLCertificateSocketFactory(
    111             int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) {
    112         mHandshakeTimeoutMillis = handshakeTimeoutMillis;
    113         mSessionCache = cache == null ? null : cache.mSessionCache;
    114         mSecure = secure;
    115     }
    116 
    117     /**
    118      * Returns a new socket factory instance with an optional handshake timeout.
    119      *
    120      * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
    121      *         for none.  The socket timeout is reset to 0 after the handshake.
    122      * @return a new SSLSocketFactory with the specified parameters
    123      */
    124     public static SocketFactory getDefault(int handshakeTimeoutMillis) {
    125         return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true);
    126     }
    127 
    128     /**
    129      * Returns a new socket factory instance with an optional handshake timeout
    130      * and SSL session cache.
    131      *
    132      * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
    133      *         for none.  The socket timeout is reset to 0 after the handshake.
    134      * @param cache The {@link SSLSessionCache} to use, or null for no cache.
    135      * @return a new SSLSocketFactory with the specified parameters
    136      */
    137     public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) {
    138         return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true);
    139     }
    140 
    141     /**
    142      * Returns a new instance of a socket factory with all SSL security checks
    143      * disabled, using an optional handshake timeout and SSL session cache.
    144      *
    145      * <p class="caution"><b>Warning:</b> Sockets created using this factory
    146      * are vulnerable to man-in-the-middle attacks!</p>
    147      *
    148      * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
    149      *         for none.  The socket timeout is reset to 0 after the handshake.
    150      * @param cache The {@link SSLSessionCache} to use, or null for no cache.
    151      * @return an insecure SSLSocketFactory with the specified parameters
    152      */
    153     public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) {
    154         return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false);
    155     }
    156 
    157     /**
    158      * Returns a socket factory (also named SSLSocketFactory, but in a different
    159      * namespace) for use with the Apache HTTP stack.
    160      *
    161      * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
    162      *         for none.  The socket timeout is reset to 0 after the handshake.
    163      * @param cache The {@link SSLSessionCache} to use, or null for no cache.
    164      * @return a new SocketFactory with the specified parameters
    165      *
    166      * @deprecated Use {@link #getDefault()} along with a {@link javax.net.ssl.HttpsURLConnection}
    167      *     instead. The Apache HTTP client is no longer maintained and may be removed in a future
    168      *     release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
    169      *     for further details.
    170      *
    171      * @removed
    172      */
    173     @Deprecated
    174     public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(
    175             int handshakeTimeoutMillis, SSLSessionCache cache) {
    176         return new org.apache.http.conn.ssl.SSLSocketFactory(
    177                 new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true));
    178     }
    179 
    180     /**
    181      * Verify the hostname of the certificate used by the other end of a
    182      * connected socket.  You MUST call this if you did not supply a hostname
    183      * to {@link #createSocket()}.  It is harmless to call this method
    184      * redundantly if the hostname has already been verified.
    185      *
    186      * <p>Wildcard certificates are allowed to verify any matching hostname,
    187      * so "foo.bar.example.com" is verified if the peer has a certificate
    188      * for "*.example.com".
    189      *
    190      * @param socket An SSL socket which has been connected to a server
    191      * @param hostname The expected hostname of the remote server
    192      * @throws IOException if something goes wrong handshaking with the server
    193      * @throws SSLPeerUnverifiedException if the server cannot prove its identity
    194      *
    195      * @hide
    196      */
    197     public static void verifyHostname(Socket socket, String hostname) throws IOException {
    198         if (!(socket instanceof SSLSocket)) {
    199             throw new IllegalArgumentException("Attempt to verify non-SSL socket");
    200         }
    201 
    202         if (!isSslCheckRelaxed()) {
    203             // The code at the start of OpenSSLSocketImpl.startHandshake()
    204             // ensures that the call is idempotent, so we can safely call it.
    205             SSLSocket ssl = (SSLSocket) socket;
    206             ssl.startHandshake();
    207 
    208             SSLSession session = ssl.getSession();
    209             if (session == null) {
    210                 throw new SSLException("Cannot verify SSL socket without session");
    211             }
    212             if (!HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session)) {
    213                 throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname);
    214             }
    215         }
    216     }
    217 
    218     private SSLSocketFactory makeSocketFactory(
    219             KeyManager[] keyManagers, TrustManager[] trustManagers) {
    220         try {
    221             OpenSSLContextImpl sslContext =  (OpenSSLContextImpl) Conscrypt.newPreferredSSLContextSpi();
    222             sslContext.engineInit(keyManagers, trustManagers, null);
    223             sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
    224             return sslContext.engineGetSocketFactory();
    225         } catch (KeyManagementException e) {
    226             Log.wtf(TAG, e);
    227             return (SSLSocketFactory) SSLSocketFactory.getDefault();  // Fallback
    228         }
    229     }
    230 
    231     private static boolean isSslCheckRelaxed() {
    232         return RoSystemProperties.DEBUGGABLE &&
    233             SystemProperties.getBoolean("socket.relaxsslcheck", false);
    234     }
    235 
    236     private synchronized SSLSocketFactory getDelegate() {
    237         // Relax the SSL check if instructed (for this factory, or systemwide)
    238         if (!mSecure || isSslCheckRelaxed()) {
    239             if (mInsecureFactory == null) {
    240                 if (mSecure) {
    241                     Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***");
    242                 } else {
    243                     Log.w(TAG, "Bypassing SSL security checks at caller's request");
    244                 }
    245                 mInsecureFactory = makeSocketFactory(mKeyManagers, INSECURE_TRUST_MANAGER);
    246             }
    247             return mInsecureFactory;
    248         } else {
    249             if (mSecureFactory == null) {
    250                 mSecureFactory = makeSocketFactory(mKeyManagers, mTrustManagers);
    251             }
    252             return mSecureFactory;
    253         }
    254     }
    255 
    256     /**
    257      * Sets the {@link TrustManager}s to be used for connections made by this factory.
    258      */
    259     public void setTrustManagers(TrustManager[] trustManager) {
    260         mTrustManagers = trustManager;
    261 
    262         // Clear out all cached secure factories since configurations have changed.
    263         mSecureFactory = null;
    264         // Note - insecure factories only ever use the INSECURE_TRUST_MANAGER so they need not
    265         // be cleared out here.
    266     }
    267 
    268     /**
    269      * Sets the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
    270      * Protocol Negotiation (NPN)</a> protocols that this peer is interested in.
    271      *
    272      * <p>For servers this is the sequence of protocols to advertise as
    273      * supported, in order of preference. This list is sent unencrypted to
    274      * all clients that support NPN.
    275      *
    276      * <p>For clients this is a list of supported protocols to match against the
    277      * server's list. If there is no protocol supported by both client and
    278      * server then the first protocol in the client's list will be selected.
    279      * The order of the client's protocols is otherwise insignificant.
    280      *
    281      * @param npnProtocols a non-empty list of protocol byte arrays. All arrays
    282      *     must be non-empty and of length less than 256.
    283      */
    284     public void setNpnProtocols(byte[][] npnProtocols) {
    285         this.mNpnProtocols = toLengthPrefixedList(npnProtocols);
    286     }
    287 
    288     /**
    289      * Sets the
    290      * <a href="http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-01">
    291      * Application Layer Protocol Negotiation (ALPN)</a> protocols that this peer
    292      * is interested in.
    293      *
    294      * <p>For servers this is the sequence of protocols to advertise as
    295      * supported, in order of preference. This list is sent unencrypted to
    296      * all clients that support ALPN.
    297      *
    298      * <p>For clients this is a list of supported protocols to match against the
    299      * server's list. If there is no protocol supported by both client and
    300      * server then the first protocol in the client's list will be selected.
    301      * The order of the client's protocols is otherwise insignificant.
    302      *
    303      * @param protocols a non-empty list of protocol byte arrays. All arrays
    304      *     must be non-empty and of length less than 256.
    305      * @hide
    306      */
    307     public void setAlpnProtocols(byte[][] protocols) {
    308         this.mAlpnProtocols = toLengthPrefixedList(protocols);
    309     }
    310 
    311     /**
    312      * Returns an array containing the concatenation of length-prefixed byte
    313      * strings.
    314      */
    315     static byte[] toLengthPrefixedList(byte[]... items) {
    316         if (items.length == 0) {
    317             throw new IllegalArgumentException("items.length == 0");
    318         }
    319         int totalLength = 0;
    320         for (byte[] s : items) {
    321             if (s.length == 0 || s.length > 255) {
    322                 throw new IllegalArgumentException("s.length == 0 || s.length > 255: " + s.length);
    323             }
    324             totalLength += 1 + s.length;
    325         }
    326         byte[] result = new byte[totalLength];
    327         int pos = 0;
    328         for (byte[] s : items) {
    329             result[pos++] = (byte) s.length;
    330             for (byte b : s) {
    331                 result[pos++] = b;
    332             }
    333         }
    334         return result;
    335     }
    336 
    337     /**
    338      * Returns the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
    339      * Protocol Negotiation (NPN)</a> protocol selected by client and server, or
    340      * null if no protocol was negotiated.
    341      *
    342      * @param socket a socket created by this factory.
    343      * @throws IllegalArgumentException if the socket was not created by this factory.
    344      */
    345     public byte[] getNpnSelectedProtocol(Socket socket) {
    346         return castToOpenSSLSocket(socket).getNpnSelectedProtocol();
    347     }
    348 
    349     /**
    350      * Returns the
    351      * <a href="http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-01">Application
    352      * Layer Protocol Negotiation (ALPN)</a> protocol selected by client and server, or null
    353      * if no protocol was negotiated.
    354      *
    355      * @param socket a socket created by this factory.
    356      * @throws IllegalArgumentException if the socket was not created by this factory.
    357      * @hide
    358      */
    359     public byte[] getAlpnSelectedProtocol(Socket socket) {
    360         return castToOpenSSLSocket(socket).getAlpnSelectedProtocol();
    361     }
    362 
    363     /**
    364      * Sets the {@link KeyManager}s to be used for connections made by this factory.
    365      */
    366     public void setKeyManagers(KeyManager[] keyManagers) {
    367         mKeyManagers = keyManagers;
    368 
    369         // Clear out any existing cached factories since configurations have changed.
    370         mSecureFactory = null;
    371         mInsecureFactory = null;
    372     }
    373 
    374     /**
    375      * Sets the private key to be used for TLS Channel ID by connections made by this
    376      * factory.
    377      *
    378      * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
    379      *        TLS Channel ID). The private key has to be an Elliptic Curve (EC) key based on the
    380      *        NIST P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
    381      *
    382      * @hide
    383      */
    384     public void setChannelIdPrivateKey(PrivateKey privateKey) {
    385         mChannelIdPrivateKey = privateKey;
    386     }
    387 
    388     /**
    389      * Enables <a href="http://tools.ietf.org/html/rfc5077#section-3.2">session ticket</a>
    390      * support on the given socket.
    391      *
    392      * @param socket a socket created by this factory
    393      * @param useSessionTickets {@code true} to enable session ticket support on this socket.
    394      * @throws IllegalArgumentException if the socket was not created by this factory.
    395      */
    396     public void setUseSessionTickets(Socket socket, boolean useSessionTickets) {
    397         castToOpenSSLSocket(socket).setUseSessionTickets(useSessionTickets);
    398     }
    399 
    400     /**
    401      * Turns on <a href="http://tools.ietf.org/html/rfc6066#section-3">Server
    402      * Name Indication (SNI)</a> on a given socket.
    403      *
    404      * @param socket a socket created by this factory.
    405      * @param hostName the desired SNI hostname, null to disable.
    406      * @throws IllegalArgumentException if the socket was not created by this factory.
    407      */
    408     public void setHostname(Socket socket, String hostName) {
    409         castToOpenSSLSocket(socket).setHostname(hostName);
    410     }
    411 
    412     /**
    413      * Sets this socket's SO_SNDTIMEO write timeout in milliseconds.
    414      * Use 0 for no timeout.
    415      * To take effect, this option must be set before the blocking method was called.
    416      *
    417      * @param socket a socket created by this factory.
    418      * @param timeout the desired write timeout in milliseconds.
    419      * @throws IllegalArgumentException if the socket was not created by this factory.
    420      *
    421      * @hide
    422      */
    423     public void setSoWriteTimeout(Socket socket, int writeTimeoutMilliseconds)
    424             throws SocketException {
    425         castToOpenSSLSocket(socket).setSoWriteTimeout(writeTimeoutMilliseconds);
    426     }
    427 
    428     private static OpenSSLSocketImpl castToOpenSSLSocket(Socket socket) {
    429         if (!(socket instanceof OpenSSLSocketImpl)) {
    430             throw new IllegalArgumentException("Socket not created by this factory: "
    431                     + socket);
    432         }
    433 
    434         return (OpenSSLSocketImpl) socket;
    435     }
    436 
    437     /**
    438      * {@inheritDoc}
    439      *
    440      * <p>This method verifies the peer's certificate hostname after connecting
    441      * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
    442      */
    443     @Override
    444     public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
    445         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
    446         s.setNpnProtocols(mNpnProtocols);
    447         s.setAlpnProtocols(mAlpnProtocols);
    448         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
    449         s.setChannelIdPrivateKey(mChannelIdPrivateKey);
    450         if (mSecure) {
    451             verifyHostname(s, host);
    452         }
    453         return s;
    454     }
    455 
    456     /**
    457      * Creates a new socket which is not connected to any remote host.
    458      * You must use {@link Socket#connect} to connect the socket.
    459      *
    460      * <p class="caution"><b>Warning:</b> Hostname verification is not performed
    461      * with this method.  You MUST verify the server's identity after connecting
    462      * the socket to avoid man-in-the-middle attacks.</p>
    463      */
    464     @Override
    465     public Socket createSocket() throws IOException {
    466         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
    467         s.setNpnProtocols(mNpnProtocols);
    468         s.setAlpnProtocols(mAlpnProtocols);
    469         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
    470         s.setChannelIdPrivateKey(mChannelIdPrivateKey);
    471         return s;
    472     }
    473 
    474     /**
    475      * {@inheritDoc}
    476      *
    477      * <p class="caution"><b>Warning:</b> Hostname verification is not performed
    478      * with this method.  You MUST verify the server's identity after connecting
    479      * the socket to avoid man-in-the-middle attacks.</p>
    480      */
    481     @Override
    482     public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort)
    483             throws IOException {
    484         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
    485                 addr, port, localAddr, localPort);
    486         s.setNpnProtocols(mNpnProtocols);
    487         s.setAlpnProtocols(mAlpnProtocols);
    488         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
    489         s.setChannelIdPrivateKey(mChannelIdPrivateKey);
    490         return s;
    491     }
    492 
    493     /**
    494      * {@inheritDoc}
    495      *
    496      * <p class="caution"><b>Warning:</b> Hostname verification is not performed
    497      * with this method.  You MUST verify the server's identity after connecting
    498      * the socket to avoid man-in-the-middle attacks.</p>
    499      */
    500     @Override
    501     public Socket createSocket(InetAddress addr, int port) throws IOException {
    502         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
    503         s.setNpnProtocols(mNpnProtocols);
    504         s.setAlpnProtocols(mAlpnProtocols);
    505         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
    506         s.setChannelIdPrivateKey(mChannelIdPrivateKey);
    507         return s;
    508     }
    509 
    510     /**
    511      * {@inheritDoc}
    512      *
    513      * <p>This method verifies the peer's certificate hostname after connecting
    514      * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
    515      */
    516     @Override
    517     public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
    518             throws IOException {
    519         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
    520                 host, port, localAddr, localPort);
    521         s.setNpnProtocols(mNpnProtocols);
    522         s.setAlpnProtocols(mAlpnProtocols);
    523         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
    524         s.setChannelIdPrivateKey(mChannelIdPrivateKey);
    525         if (mSecure) {
    526             verifyHostname(s, host);
    527         }
    528         return s;
    529     }
    530 
    531     /**
    532      * {@inheritDoc}
    533      *
    534      * <p>This method verifies the peer's certificate hostname after connecting
    535      * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
    536      */
    537     @Override
    538     public Socket createSocket(String host, int port) throws IOException {
    539         OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
    540         s.setNpnProtocols(mNpnProtocols);
    541         s.setAlpnProtocols(mAlpnProtocols);
    542         s.setHandshakeTimeout(mHandshakeTimeoutMillis);
    543         s.setChannelIdPrivateKey(mChannelIdPrivateKey);
    544         if (mSecure) {
    545             verifyHostname(s, host);
    546         }
    547         return s;
    548     }
    549 
    550     @Override
    551     public String[] getDefaultCipherSuites() {
    552         return getDelegate().getDefaultCipherSuites();
    553     }
    554 
    555     @Override
    556     public String[] getSupportedCipherSuites() {
    557         return getDelegate().getSupportedCipherSuites();
    558     }
    559 }
    560