SSLCertificateSocketFactory.java revision 762b33f9494ba48aa1be3701d345b692e8432af9
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 @Deprecated 103 public SSLCertificateSocketFactory(int handshakeTimeoutMillis) { 104 this(handshakeTimeoutMillis, null, true); 105 } 106 107 private SSLCertificateSocketFactory( 108 int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) { 109 mHandshakeTimeoutMillis = handshakeTimeoutMillis; 110 mSessionCache = cache == null ? null : cache.mSessionCache; 111 mSecure = secure; 112 } 113 114 /** 115 * Returns a new socket factory instance with an optional handshake timeout. 116 * 117 * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 118 * for none. The socket timeout is reset to 0 after the handshake. 119 * @return a new SSLSocketFactory with the specified parameters 120 */ 121 public static SocketFactory getDefault(int handshakeTimeoutMillis) { 122 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true); 123 } 124 125 /** 126 * Returns a new socket factory instance with an optional handshake timeout 127 * and SSL session cache. 128 * 129 * @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. 132 * @return a new SSLSocketFactory with the specified parameters 133 */ 134 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. 141 * 142 * <p class="caution"><b>Warning:</b> Sockets created using this factory 143 * are vulnerable to man-in-the-middle attacks!</p> 144 * 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. 148 * @return an insecure SSLSocketFactory with the specified parameters 149 */ 150 public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) { 151 return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false); 152 } 153 154 /** 155 * Returns a socket factory (also named SSLSocketFactory, but in a different 156 * namespace) for use with the Apache HTTP stack. 157 * 158 * @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( 167 new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true)); 168 } 169 170 /** 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 208 private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) { 209 try { 210 OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); 211 sslContext.engineInit(null, trustManagers, null); 212 sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache); 213 return sslContext.engineGetSocketFactory(); 214 } catch (KeyManagementException e) { 215 Log.wtf(TAG, e); 216 return (SSLSocketFactory) SSLSocketFactory.getDefault(); // Fallback 217 } 218 } 219 220 private static boolean isSslCheckRelaxed() { 221 return "1".equals(SystemProperties.get("ro.debuggable")) && 222 "yes".equals(SystemProperties.get("socket.relaxsslcheck")); 223 } 224 225 private synchronized SSLSocketFactory getDelegate() { 226 // Relax the SSL check if instructed (for this factory, or systemwide) 227 if (!mSecure || isSslCheckRelaxed()) { 228 if (mInsecureFactory == null) { 229 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 } 234 mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER); 235 } 236 return mInsecureFactory; 237 } else { 238 if (mSecureFactory == null) { 239 mSecureFactory = makeSocketFactory(null); 240 } 241 return mSecureFactory; 242 } 243 } 244 245 /** 246 * {@inheritDoc} 247 * 248 * <p>This method verifies the peer's certificate hostname after connecting 249 * (unless created with {@link #getInsecure(int, SSLSessionCache)}). 250 */ 251 @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); 255 if (mSecure) { 256 verifyHostname(s, host); 257 } 258 return s; 259 } 260 261 /** 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 */ 269 @Override 270 public Socket createSocket() throws IOException { 271 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(); 272 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 273 return s; 274 } 275 276 /** 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 */ 283 @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; 290 } 291 292 /** 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 */ 299 @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; 304 } 305 306 /** 307 * {@inheritDoc} 308 * 309 * <p>This method verifies the peer's certificate hostname after connecting 310 * (unless created with {@link #getInsecure(int, SSLSessionCache)}). 311 */ 312 @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); 318 if (mSecure) { 319 verifyHostname(s, host); 320 } 321 return s; 322 } 323 324 /** 325 * {@inheritDoc} 326 * 327 * <p>This method verifies the peer's certificate hostname after connecting 328 * (unless created with {@link #getInsecure(int, SSLSessionCache)}). 329 */ 330 @Override 331 public Socket createSocket(String host, int port) throws IOException { 332 OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port); 333 s.setHandshakeTimeout(mHandshakeTimeoutMillis); 334 if (mSecure) { 335 verifyHostname(s, host); 336 } 337 return s; 338 } 339 340 @Override 341 public String[] getDefaultCipherSuites() { 342 return getDelegate().getSupportedCipherSuites(); 343 } 344 345 @Override 346 public String[] getSupportedCipherSuites() { 347 return getDelegate().getSupportedCipherSuites(); 348 } 349} 350