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