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