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