SSLCertificateSocketFactory.java revision ee8dd6baf848130400af8864e5de142c8a5e3718
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.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; 35import javax.net.ssl.HostnameVerifier; 36import javax.net.ssl.HttpsURLConnection; 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.TrustManagerFactory; 44import javax.net.ssl.X509TrustManager; 45 46import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl; 47import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl; 48import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; 49 50/** 51 * SSLSocketFactory implementation with several extra features: 52 * 53 * <ul> 54 * <li>Timeout specification for SSL handshake operations 55 * <li>Hostname verification in most cases (see WARNINGs below) 56 * <li>Optional SSL session caching with {@link SSLSessionCache} 57 * <li>Optionally bypass all SSL certificate checks 58 * </ul> 59 * 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. 79 */ 80public class SSLCertificateSocketFactory extends SSLSocketFactory { 81 private static final String TAG = "SSLCertificateSocketFactory"; 82 83 private static final TrustManager[] INSECURE_TRUST_MANAGER = new TrustManager[] { 84 new X509TrustManager() { 85 public X509Certificate[] getAcceptedIssuers() { return null; } 86 public void checkClientTrusted(X509Certificate[] certs, String authType) { } 87 public void checkServerTrusted(X509Certificate[] certs, String authType) { } 88 } 89 }; 90 91 private static final HostnameVerifier HOSTNAME_VERIFIER = 92 HttpsURLConnection.getDefaultHostnameVerifier(); 93 94 private SSLSocketFactory mInsecureFactory = null; 95 private SSLSocketFactory mSecureFactory = null; 96 97 private final int mHandshakeTimeoutMillis; 98 private final SSLClientSessionCache mSessionCache; 99 private final boolean mSecure; 100 101 /** @deprecated Use {@link #getDefault(int)} instead. */ 102 public SSLCertificateSocketFactory(int handshakeTimeoutMillis) { 103 this(handshakeTimeoutMillis, null, true); 104 } 105 106 private SSLCertificateSocketFactory( 107 int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) { 108 mHandshakeTimeoutMillis = handshakeTimeoutMillis; 109 mSessionCache = cache == null ? null : cache.mSessionCache; 110 mSecure = secure; 111 } 112 113 /** 114 * Returns a new socket factory instance with an optional handshake timeout. 115 * 116 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 117 * for none. The socket timeout is reset to 0 after the handshake. 118 * @return a new SSLSocketFactory with the specified parameters 119 */ 120 public static SocketFactory getDefault(int handshakeTimeoutMillis) { 121 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true); 122 } 123 124 /** 125 * Returns a new socket factory instance with an optional handshake timeout 126 * and SSL session cache. 127 * 128 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 129 * for none. The socket timeout is reset to 0 after the handshake. 130 * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. 131 * @return a new SSLSocketFactory with the specified parameters 132 */ 133 public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) { 134 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true); 135 } 136 137 /** 138 * Returns a new instance of a socket factory with all SSL security checks 139 * disabled, using an optional handshake timeout and SSL session cache. 140 * 141 * <p class="caution"><b>Warning:</b> Sockets created using this factory 142 * are vulnerable to man-in-the-middle attacks!</p> 143 * 144 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 145 * for none. The socket timeout is reset to 0 after the handshake. 146 * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. 147 * @return an insecure SSLSocketFactory with the specified parameters 148 */ 149 public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) { 150 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false); 151 } 152 153 /** 154 * Returns a socket factory (also named SSLSocketFactory, but in a different 155 * namespace) for use with the Apache HTTP stack. 156 * 157 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 158 * for none. The socket timeout is reset to 0 after the handshake. 159 * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. 160 * @return a new SocketFactory with the specified parameters 161 */ 162 public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory( 163 int handshakeTimeoutMillis, 164 SSLSessionCache cache) { 165 return new org.apache.http.conn.ssl.SSLSocketFactory( 166 new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true)); 167 } 168 169 /** 170 * Verify the hostname of the certificate used by the other end of a 171 * connected socket. You MUST call this if you did not supply a hostname 172 * to {@link #createSocket()}. It is harmless to call this method 173 * redundantly if the hostname has already been verified. 174 * 175 * <p>Wildcard certificates are allowed to verify any matching hostname, 176 * so "foo.bar.example.com" is verified if the peer has a certificate 177 * for "*.example.com". 178 * 179 * @param socket An SSL socket which has been connected to a server 180 * @param hostname The expected hostname of the remote server 181 * @throws IOException if something goes wrong handshaking with the server 182 * @throws SSLPeerUnverifiedException if the server cannot prove its identity 183 * 184 * @hide 185 */ 186 public static void verifyHostname(Socket socket, String hostname) throws IOException { 187 if (!(socket instanceof SSLSocket)) { 188 throw new IllegalArgumentException("Attempt to verify non-SSL socket"); 189 } 190 191 if (!isSslCheckRelaxed()) { 192 // The code at the start of OpenSSLSocketImpl.startHandshake() 193 // ensures that the call is idempotent, so we can safely call it. 194 SSLSocket ssl = (SSLSocket) socket; 195 ssl.startHandshake(); 196 197 SSLSession session = ssl.getSession(); 198 if (session == null) { 199 throw new SSLException("Cannot verify SSL socket without session"); 200 } 201 if (!HOSTNAME_VERIFIER.verify(hostname, session)) { 202 throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname); 203 } 204 } 205 } 206 207 private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) { 208 try { 209 OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); 210 sslContext.engineInit(null, trustManagers, null); 211 sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache); 212 return sslContext.engineGetSocketFactory(); 213 } catch (KeyManagementException e) { 214 Log.wtf(TAG, e); 215 return (SSLSocketFactory) SSLSocketFactory.getDefault(); // Fallback 216 } 217 } 218 219 private static boolean isSslCheckRelaxed() { 220 return "1".equals(SystemProperties.get("ro.debuggable")) && 221 "yes".equals(SystemProperties.get("socket.relaxsslcheck")); 222 } 223 224 private synchronized SSLSocketFactory getDelegate() { 225 // Relax the SSL check if instructed (for this factory, or systemwide) 226 if (!mSecure || isSslCheckRelaxed()) { 227 if (mInsecureFactory == null) { 228 if (mSecure) { 229 Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***"); 230 } else { 231 Log.w(TAG, "Bypassing SSL security checks at caller's request"); 232 } 233 mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER); 234 } 235 return mInsecureFactory; 236 } else { 237 if (mSecureFactory == null) { 238 mSecureFactory = makeSocketFactory(null); 239 } 240 return mSecureFactory; 241 } 242 } 243 244 /** 245 * {@inheritDoc} 246 * 247 * <p>This method verifies the peer's certificate hostname after connecting 248 * (unless created with {@link #getInsecure(int, SSLSessionCache)}). 249 */ 250 @Override 251 public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException { 252 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close); 253 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 254 if (mSecure) { 255 verifyHostname(s, host); 256 } 257 return s; 258 } 259 260 /** 261 * Creates a new socket which is not connected to any remote host. 262 * You must use {@link Socket#connect} to connect the socket. 263 * 264 * <p class="caution"><b>Warning:</b> Hostname verification is not performed 265 * with this method. You MUST verify the server's identity after connecting 266 * the socket to avoid man-in-the-middle attacks.</p> 267 */ 268 @Override 269 public Socket createSocket() throws IOException { 270 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(); 271 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 272 return s; 273 } 274 275 /** 276 * {@inheritDoc} 277 * 278 * <p class="caution"><b>Warning:</b> Hostname verification is not performed 279 * with this method. You MUST verify the server's identity after connecting 280 * the socket to avoid man-in-the-middle attacks.</p> 281 */ 282 @Override 283 public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) 284 throws IOException { 285 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket( 286 addr, port, localAddr, localPort); 287 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 288 return s; 289 } 290 291 /** 292 * {@inheritDoc} 293 * 294 * <p class="caution"><b>Warning:</b> Hostname verification is not performed 295 * with this method. You MUST verify the server's identity after connecting 296 * the socket to avoid man-in-the-middle attacks.</p> 297 */ 298 @Override 299 public Socket createSocket(InetAddress addr, int port) throws IOException { 300 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port); 301 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 302 return s; 303 } 304 305 /** 306 * {@inheritDoc} 307 * 308 * <p>This method verifies the peer's certificate hostname after connecting 309 * (unless created with {@link #getInsecure(int, SSLSessionCache)}). 310 */ 311 @Override 312 public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) 313 throws IOException { 314 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket( 315 host, port, localAddr, localPort); 316 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 317 if (mSecure) { 318 verifyHostname(s, host); 319 } 320 return s; 321 } 322 323 /** 324 * {@inheritDoc} 325 * 326 * <p>This method verifies the peer's certificate hostname after connecting 327 * (unless created with {@link #getInsecure(int, SSLSessionCache)}). 328 */ 329 @Override 330 public Socket createSocket(String host, int port) throws IOException { 331 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port); 332 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 333 if (mSecure) { 334 verifyHostname(s, host); 335 } 336 return s; 337 } 338 339 @Override 340 public String[] getDefaultCipherSuites() { 341 return getDelegate().getSupportedCipherSuites(); 342 } 343 344 @Override 345 public String[] getSupportedCipherSuites() { 346 return getDelegate().getSupportedCipherSuites(); 347 } 348} 349