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