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