blob: f8f8a29278fe1e654e09eec6082a8bc2c40a4aed [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.os.SystemProperties;
20import android.util.Config;
21import android.util.Log;
22
23import java.io.IOException;
24import java.net.InetAddress;
25import java.net.Socket;
26import java.security.GeneralSecurityException;
27import java.security.KeyManagementException;
28import java.security.KeyStore;
29import java.security.KeyStoreException;
30import java.security.NoSuchAlgorithmException;
31import java.security.cert.Certificate;
32import java.security.cert.X509Certificate;
33
34import javax.net.SocketFactory;
Dan Egnor7fc93c32010-06-29 17:51:28 -070035import javax.net.ssl.HostnameVerifier;
36import javax.net.ssl.HttpsURLConnection;
37import javax.net.ssl.SSLException;
38import javax.net.ssl.SSLPeerUnverifiedException;
39import javax.net.ssl.SSLSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import javax.net.ssl.SSLSocket;
41import javax.net.ssl.SSLSocketFactory;
42import javax.net.ssl.TrustManager;
43import javax.net.ssl.TrustManagerFactory;
44import javax.net.ssl.X509TrustManager;
45
Brian Carlstrom3c7c3512010-08-04 15:44:39 -070046import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl;
Dan Egnor60586f22010-02-08 21:56:38 -080047import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
50/**
Dan Egnor60586f22010-02-08 21:56:38 -080051 * SSLSocketFactory implementation with several extra features:
Dan Egnor7fc93c32010-06-29 17:51:28 -070052 *
Dan Egnor60586f22010-02-08 21:56:38 -080053 * <ul>
54 * <li>Timeout specification for SSL handshake operations
Dan Egnor7fc93c32010-06-29 17:51:28 -070055 * <li>Hostname verification in most cases (see WARNINGs below)
Dan Egnor60586f22010-02-08 21:56:38 -080056 * <li>Optional SSL session caching with {@link SSLSessionCache}
Dan Egnor9d4b5752010-02-13 20:34:51 -080057 * <li>Optionally bypass all SSL certificate checks
Dan Egnor60586f22010-02-08 21:56:38 -080058 * </ul>
Dan Egnor7fc93c32010-06-29 17:51:28 -070059 *
60 * The handshake timeout does not apply to actual TCP socket connection.
61 * If you want a connection timeout as well, use {@link #createSocket()}
62 * and {@link Socket#connect(SocketAddress, int)}, after which you
63 * must verify the identity of the server you are connected to.
64 *
65 * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not
66 * verify the server's identity, allowing man-in-the-middle attacks.</b>
67 * This implementation does check the server's certificate hostname, but only
68 * for createSocket variants that specify a hostname. When using methods that
69 * use {@link InetAddress} or which return an unconnected socket, you MUST
70 * verify the server's identity yourself to ensure a secure connection.</p>
71 *
72 * <p>One way to verify the server's identity is to use
73 * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
74 * {@link HostnameVerifier} to verify the certificate hostname.
75 *
76 * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
77 * SSL certificate and hostname checks for testing purposes. This setting
78 * requires root access.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 */
80public class SSLCertificateSocketFactory extends SSLSocketFactory {
Dan Egnor60586f22010-02-08 21:56:38 -080081 private static final String TAG = "SSLCertificateSocketFactory";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
Dan Egnor60586f22010-02-08 21:56:38 -080083 private static final TrustManager[] INSECURE_TRUST_MANAGER = new TrustManager[] {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 new X509TrustManager() {
Dan Egnor60586f22010-02-08 21:56:38 -080085 public X509Certificate[] getAcceptedIssuers() { return null; }
86 public void checkClientTrusted(X509Certificate[] certs, String authType) { }
87 public void checkServerTrusted(X509Certificate[] certs, String authType) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 }
89 };
90
Dan Egnor7fc93c32010-06-29 17:51:28 -070091 private static final HostnameVerifier HOSTNAME_VERIFIER =
92 HttpsURLConnection.getDefaultHostnameVerifier();
93
Dan Egnor60586f22010-02-08 21:56:38 -080094 private SSLSocketFactory mInsecureFactory = null;
95 private SSLSocketFactory mSecureFactory = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096
Dan Egnor60586f22010-02-08 21:56:38 -080097 private final int mHandshakeTimeoutMillis;
98 private final SSLClientSessionCache mSessionCache;
Dan Egnor9d4b5752010-02-13 20:34:51 -080099 private final boolean mSecure;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
Dan Egnor60586f22010-02-08 21:56:38 -0800101 /** @deprecated Use {@link #getDefault(int)} instead. */
Jesse Wilson28c74252010-11-04 14:09:18 -0700102 @Deprecated
Dan Egnor60586f22010-02-08 21:56:38 -0800103 public SSLCertificateSocketFactory(int handshakeTimeoutMillis) {
Dan Egnor9d4b5752010-02-13 20:34:51 -0800104 this(handshakeTimeoutMillis, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 }
106
Dan Egnor9d4b5752010-02-13 20:34:51 -0800107 private SSLCertificateSocketFactory(
108 int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) {
Dan Egnor60586f22010-02-08 21:56:38 -0800109 mHandshakeTimeoutMillis = handshakeTimeoutMillis;
110 mSessionCache = cache == null ? null : cache.mSessionCache;
Dan Egnor9d4b5752010-02-13 20:34:51 -0800111 mSecure = secure;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 }
113
114 /**
Dan Egnor9d4b5752010-02-13 20:34:51 -0800115 * Returns a new socket factory instance with an optional handshake timeout.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 *
Dan Egnor60586f22010-02-08 21:56:38 -0800117 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
118 * for none. The socket timeout is reset to 0 after the handshake.
Dan Egnor7fc93c32010-06-29 17:51:28 -0700119 * @return a new SSLSocketFactory with the specified parameters
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 */
Dan Egnor60586f22010-02-08 21:56:38 -0800121 public static SocketFactory getDefault(int handshakeTimeoutMillis) {
Dan Egnor9d4b5752010-02-13 20:34:51 -0800122 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 }
124
125 /**
Dan Egnor9d4b5752010-02-13 20:34:51 -0800126 * Returns a new socket factory instance with an optional handshake timeout
127 * and SSL session cache.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 *
Dan Egnor60586f22010-02-08 21:56:38 -0800129 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
130 * for none. The socket timeout is reset to 0 after the handshake.
131 * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
Dan Egnor7fc93c32010-06-29 17:51:28 -0700132 * @return a new SSLSocketFactory with the specified parameters
Dan Egnor60586f22010-02-08 21:56:38 -0800133 */
Dan Egnor9d4b5752010-02-13 20:34:51 -0800134 public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) {
135 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true);
136 }
137
138 /**
139 * Returns a new instance of a socket factory with all SSL security checks
140 * disabled, using an optional handshake timeout and SSL session cache.
Dan Egnor7fc93c32010-06-29 17:51:28 -0700141 *
142 * <p class="caution"><b>Warning:</b> Sockets created using this factory
143 * are vulnerable to man-in-the-middle attacks!</p>
Dan Egnor9d4b5752010-02-13 20:34:51 -0800144 *
145 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
146 * for none. The socket timeout is reset to 0 after the handshake.
147 * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
Dan Egnor7fc93c32010-06-29 17:51:28 -0700148 * @return an insecure SSLSocketFactory with the specified parameters
Dan Egnor9d4b5752010-02-13 20:34:51 -0800149 */
150 public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) {
151 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false);
Dan Egnor60586f22010-02-08 21:56:38 -0800152 }
153
154 /**
155 * Returns a socket factory (also named SSLSocketFactory, but in a different
156 * namespace) for use with the Apache HTTP stack.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 *
Dan Egnor60586f22010-02-08 21:56:38 -0800158 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
159 * for none. The socket timeout is reset to 0 after the handshake.
160 * @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
161 * @return a new SocketFactory with the specified parameters
162 */
163 public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(
164 int handshakeTimeoutMillis,
165 SSLSessionCache cache) {
166 return new org.apache.http.conn.ssl.SSLSocketFactory(
Dan Egnor9d4b5752010-02-13 20:34:51 -0800167 new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true));
Dan Egnor60586f22010-02-08 21:56:38 -0800168 }
169
Dan Egnor7fc93c32010-06-29 17:51:28 -0700170 /**
171 * Verify the hostname of the certificate used by the other end of a
172 * connected socket. You MUST call this if you did not supply a hostname
173 * to {@link #createSocket()}. It is harmless to call this method
174 * redundantly if the hostname has already been verified.
175 *
176 * <p>Wildcard certificates are allowed to verify any matching hostname,
177 * so "foo.bar.example.com" is verified if the peer has a certificate
178 * for "*.example.com".
179 *
180 * @param socket An SSL socket which has been connected to a server
181 * @param hostname The expected hostname of the remote server
182 * @throws IOException if something goes wrong handshaking with the server
183 * @throws SSLPeerUnverifiedException if the server cannot prove its identity
184 *
185 * @hide
186 */
187 public static void verifyHostname(Socket socket, String hostname) throws IOException {
188 if (!(socket instanceof SSLSocket)) {
189 throw new IllegalArgumentException("Attempt to verify non-SSL socket");
190 }
191
192 if (!isSslCheckRelaxed()) {
193 // The code at the start of OpenSSLSocketImpl.startHandshake()
194 // ensures that the call is idempotent, so we can safely call it.
195 SSLSocket ssl = (SSLSocket) socket;
196 ssl.startHandshake();
197
198 SSLSession session = ssl.getSession();
199 if (session == null) {
200 throw new SSLException("Cannot verify SSL socket without session");
201 }
202 if (!HOSTNAME_VERIFIER.verify(hostname, session)) {
203 throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname);
204 }
205 }
206 }
207
Dan Egnor60586f22010-02-08 21:56:38 -0800208 private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 try {
Brian Carlstrom3c7c3512010-08-04 15:44:39 -0700210 OpenSSLContextImpl sslContext = new OpenSSLContextImpl();
Brian Carlstrom2c42c8f2010-09-14 00:11:14 -0700211 sslContext.engineInit(null, trustManagers, null);
212 sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
Dan Egnor60586f22010-02-08 21:56:38 -0800213 return sslContext.engineGetSocketFactory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 } catch (KeyManagementException e) {
Dan Egnor60586f22010-02-08 21:56:38 -0800215 Log.wtf(TAG, e);
216 return (SSLSocketFactory) SSLSocketFactory.getDefault(); // Fallback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 }
218 }
219
Dan Egnor7fc93c32010-06-29 17:51:28 -0700220 private static boolean isSslCheckRelaxed() {
221 return "1".equals(SystemProperties.get("ro.debuggable")) &&
222 "yes".equals(SystemProperties.get("socket.relaxsslcheck"));
223 }
224
Dan Egnor60586f22010-02-08 21:56:38 -0800225 private synchronized SSLSocketFactory getDelegate() {
Dan Egnor9d4b5752010-02-13 20:34:51 -0800226 // Relax the SSL check if instructed (for this factory, or systemwide)
Dan Egnor7fc93c32010-06-29 17:51:28 -0700227 if (!mSecure || isSslCheckRelaxed()) {
Dan Egnor60586f22010-02-08 21:56:38 -0800228 if (mInsecureFactory == null) {
Dan Egnor9d4b5752010-02-13 20:34:51 -0800229 if (mSecure) {
230 Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***");
231 } else {
232 Log.w(TAG, "Bypassing SSL security checks at caller's request");
233 }
Dan Egnor60586f22010-02-08 21:56:38 -0800234 mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 }
Dan Egnor60586f22010-02-08 21:56:38 -0800236 return mInsecureFactory;
237 } else {
238 if (mSecureFactory == null) {
239 mSecureFactory = makeSocketFactory(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 }
Dan Egnor60586f22010-02-08 21:56:38 -0800241 return mSecureFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 }
243 }
244
Dan Egnor7fc93c32010-06-29 17:51:28 -0700245 /**
246 * {@inheritDoc}
247 *
Andrew Stadlerdf27c0c2010-07-12 15:31:40 -0700248 * <p>This method verifies the peer's certificate hostname after connecting
249 * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
Dan Egnor7fc93c32010-06-29 17:51:28 -0700250 */
Dan Egnor60586f22010-02-08 21:56:38 -0800251 @Override
252 public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
253 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
254 s.setHandshakeTimeout(mHandshakeTimeoutMillis);
Andrew Stadlerdf27c0c2010-07-12 15:31:40 -0700255 if (mSecure) {
256 verifyHostname(s, host);
257 }
Dan Egnor60586f22010-02-08 21:56:38 -0800258 return s;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 }
260
Dan Egnor7fc93c32010-06-29 17:51:28 -0700261 /**
262 * Creates a new socket which is not connected to any remote host.
263 * You must use {@link Socket#connect} to connect the socket.
264 *
265 * <p class="caution"><b>Warning:</b> Hostname verification is not performed
266 * with this method. You MUST verify the server's identity after connecting
267 * the socket to avoid man-in-the-middle attacks.</p>
268 */
Dan Egnor60586f22010-02-08 21:56:38 -0800269 @Override
270 public Socket createSocket() throws IOException {
271 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
272 s.setHandshakeTimeout(mHandshakeTimeoutMillis);
273 return s;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275
Dan Egnor7fc93c32010-06-29 17:51:28 -0700276 /**
277 * {@inheritDoc}
278 *
279 * <p class="caution"><b>Warning:</b> Hostname verification is not performed
280 * with this method. You MUST verify the server's identity after connecting
281 * the socket to avoid man-in-the-middle attacks.</p>
282 */
Dan Egnor60586f22010-02-08 21:56:38 -0800283 @Override
284 public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort)
285 throws IOException {
286 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
287 addr, port, localAddr, localPort);
288 s.setHandshakeTimeout(mHandshakeTimeoutMillis);
289 return s;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 }
291
Dan Egnor7fc93c32010-06-29 17:51:28 -0700292 /**
293 * {@inheritDoc}
294 *
295 * <p class="caution"><b>Warning:</b> Hostname verification is not performed
296 * with this method. You MUST verify the server's identity after connecting
297 * the socket to avoid man-in-the-middle attacks.</p>
298 */
Dan Egnor60586f22010-02-08 21:56:38 -0800299 @Override
300 public Socket createSocket(InetAddress addr, int port) throws IOException {
301 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
302 s.setHandshakeTimeout(mHandshakeTimeoutMillis);
303 return s;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 }
305
Dan Egnor7fc93c32010-06-29 17:51:28 -0700306 /**
307 * {@inheritDoc}
308 *
Andrew Stadlerdf27c0c2010-07-12 15:31:40 -0700309 * <p>This method verifies the peer's certificate hostname after connecting
310 * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
Dan Egnor7fc93c32010-06-29 17:51:28 -0700311 */
Dan Egnor60586f22010-02-08 21:56:38 -0800312 @Override
313 public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
314 throws IOException {
315 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
316 host, port, localAddr, localPort);
317 s.setHandshakeTimeout(mHandshakeTimeoutMillis);
Andrew Stadlerdf27c0c2010-07-12 15:31:40 -0700318 if (mSecure) {
319 verifyHostname(s, host);
320 }
Dan Egnor60586f22010-02-08 21:56:38 -0800321 return s;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 }
323
Dan Egnor7fc93c32010-06-29 17:51:28 -0700324 /**
325 * {@inheritDoc}
326 *
Andrew Stadlerdf27c0c2010-07-12 15:31:40 -0700327 * <p>This method verifies the peer's certificate hostname after connecting
328 * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
Dan Egnor7fc93c32010-06-29 17:51:28 -0700329 */
Dan Egnor60586f22010-02-08 21:56:38 -0800330 @Override
331 public Socket createSocket(String host, int port) throws IOException {
332 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
333 s.setHandshakeTimeout(mHandshakeTimeoutMillis);
Andrew Stadlerdf27c0c2010-07-12 15:31:40 -0700334 if (mSecure) {
335 verifyHostname(s, host);
336 }
Dan Egnor60586f22010-02-08 21:56:38 -0800337 return s;
338 }
339
340 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 public String[] getDefaultCipherSuites() {
Dan Egnor60586f22010-02-08 21:56:38 -0800342 return getDelegate().getSupportedCipherSuites();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 }
344
Dan Egnor60586f22010-02-08 21:56:38 -0800345 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 public String[] getSupportedCipherSuites() {
Dan Egnor60586f22010-02-08 21:56:38 -0800347 return getDelegate().getSupportedCipherSuites();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
349}