auto import from //depot/cupcake/@132589
This commit is contained in:
@@ -16,46 +16,39 @@
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.Config;
|
||||
import android.net.http.DomainNameChecker;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.net.SSLSessionCache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
* SSLSocketFactory that allows skipping the certificate chain validation
|
||||
* based on system setting (socket.relaxsslcheck=yes, ro.secure=1 - for
|
||||
* testing only).
|
||||
*
|
||||
* It also adds a readTimeout that will be set on each created socket.
|
||||
* The factory will use SSL session persistence if enabled by config.
|
||||
*/
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class SSLCertificateSocketFactory extends SSLSocketFactory {
|
||||
|
||||
private static final boolean DBG = true;
|
||||
private static final String LOG_TAG = "SSLCertificateSocketFactory";
|
||||
|
||||
private static X509TrustManager sDefaultTrustManager;
|
||||
|
||||
private final int socketReadTimeoutForSslHandshake;
|
||||
|
||||
static {
|
||||
try {
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
|
||||
@@ -90,36 +83,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
|
||||
}
|
||||
};
|
||||
|
||||
private static SSLSocketFactory factory;
|
||||
|
||||
/**
|
||||
* Initialize a single default factory to be used for all returned
|
||||
* sockets.
|
||||
*
|
||||
* Because of the signature of getDefault(int timeout) it needs to create
|
||||
* a new instance which encapsulates the timeout on each call. We want
|
||||
* to share a single SSLContext and SSLSessionCache.
|
||||
*
|
||||
* Can be called multiple times - but only the first will initialize the factory.
|
||||
*
|
||||
* @param androidContext will be used for SSL session persistence. Null for backward
|
||||
* compatibility, no SSL persistence.
|
||||
* @hide
|
||||
*/
|
||||
public static synchronized void setupDefaultFactory(Context androidContext) {
|
||||
if ( factory != null) {
|
||||
// Can only be initialized once, to avoid having multiple caches.
|
||||
return;
|
||||
}
|
||||
factory = SSLSessionCache.getSocketFactory(androidContext, TRUST_MANAGER);
|
||||
}
|
||||
|
||||
private final int socketReadTimeoutForSslHandshake;
|
||||
private SSLSocketFactory factory;
|
||||
|
||||
public SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake)
|
||||
throws NoSuchAlgorithmException, KeyManagementException {
|
||||
this.socketReadTimeoutForSslHandshake
|
||||
= socketReadTimeoutForSslHandshake;
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(null, TRUST_MANAGER, new java.security.SecureRandom());
|
||||
factory = (SSLSocketFactory) context.getSocketFactory();
|
||||
this.socketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,11 +103,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
|
||||
*/
|
||||
public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake) {
|
||||
try {
|
||||
if (factory == null) {
|
||||
// The delegated factory was not initialized explicitely with a context.
|
||||
// Use a default one.
|
||||
setupDefaultFactory(null);
|
||||
}
|
||||
return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.e(LOG_TAG,
|
||||
|
||||
@@ -47,8 +47,6 @@ import org.apache.http.params.HttpProtocolParams;
|
||||
import org.apache.http.protocol.BasicHttpProcessor;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.protocol.BasicHttpContext;
|
||||
import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
|
||||
import org.apache.harmony.xnet.provider.jsse.SSLContextImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -57,7 +55,6 @@ import java.io.OutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import java.net.URI;
|
||||
import java.security.KeyManagementException;
|
||||
|
||||
import android.util.Log;
|
||||
import android.content.ContentResolver;
|
||||
@@ -101,13 +98,10 @@ public final class AndroidHttpClient implements HttpClient {
|
||||
|
||||
/**
|
||||
* Create a new HttpClient with reasonable defaults (which you can update).
|
||||
*
|
||||
* @param userAgent to report in your HTTP requests.
|
||||
* @param sessionCache persistent session cache
|
||||
* @return AndroidHttpClient for you to use for all your requests.
|
||||
*/
|
||||
public static AndroidHttpClient newInstance(String userAgent,
|
||||
SSLClientSessionCache sessionCache) {
|
||||
public static AndroidHttpClient newInstance(String userAgent) {
|
||||
HttpParams params = new BasicHttpParams();
|
||||
|
||||
// Turn off stale checking. Our connections break all the time anyway,
|
||||
@@ -129,8 +123,7 @@ public final class AndroidHttpClient implements HttpClient {
|
||||
schemeRegistry.register(new Scheme("http",
|
||||
PlainSocketFactory.getSocketFactory(), 80));
|
||||
schemeRegistry.register(new Scheme("https",
|
||||
socketFactoryWithCache(sessionCache), 443));
|
||||
|
||||
SSLSocketFactory.getSocketFactory(), 443));
|
||||
ClientConnectionManager manager =
|
||||
new ThreadSafeClientConnManager(params, schemeRegistry);
|
||||
|
||||
@@ -139,41 +132,6 @@ public final class AndroidHttpClient implements HttpClient {
|
||||
return new AndroidHttpClient(manager, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a socket factory backed by the given persistent session cache.
|
||||
*
|
||||
* @param sessionCache to retrieve sessions from, null for no cache
|
||||
*/
|
||||
private static SSLSocketFactory socketFactoryWithCache(
|
||||
SSLClientSessionCache sessionCache) {
|
||||
if (sessionCache == null) {
|
||||
// Use the default factory which doesn't support persistent
|
||||
// caching.
|
||||
return SSLSocketFactory.getSocketFactory();
|
||||
}
|
||||
|
||||
// Create a new SSL context backed by the cache.
|
||||
// TODO: Keep a weak *identity* hash map of caches to engines. In the
|
||||
// mean time, if we have two engines for the same cache, they'll still
|
||||
// share sessions but will have to do so through the persistent cache.
|
||||
SSLContextImpl sslContext = new SSLContextImpl();
|
||||
try {
|
||||
sslContext.engineInit(null, null, null, sessionCache, null);
|
||||
} catch (KeyManagementException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return new SSLSocketFactory(sslContext.engineGetSocketFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new HttpClient with reasonable defaults (which you can update).
|
||||
* @param userAgent to report in your HTTP requests.
|
||||
* @return AndroidHttpClient for you to use for all your requests.
|
||||
*/
|
||||
public static AndroidHttpClient newInstance(String userAgent) {
|
||||
return newInstance(userAgent, null /* session cache */);
|
||||
}
|
||||
|
||||
private final HttpClient delegate;
|
||||
|
||||
private RuntimeException mLeakedException = new IllegalStateException(
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.net.http;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.cert.Certificate;
|
||||
@@ -26,13 +28,23 @@ import java.security.cert.X509Certificate;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
|
||||
import org.bouncycastle.asn1.x509.X509Name;
|
||||
|
||||
/**
|
||||
* Class responsible for all server certificate validation functionality
|
||||
*
|
||||
@@ -40,6 +52,9 @@ import javax.net.ssl.X509TrustManager;
|
||||
*/
|
||||
class CertificateChainValidator {
|
||||
|
||||
private static long sTotal = 0;
|
||||
private static long sTotalReused = 0;
|
||||
|
||||
/**
|
||||
* The singleton instance of the certificate chain validator
|
||||
*/
|
||||
@@ -95,42 +110,91 @@ class CertificateChainValidator {
|
||||
* @return An SSL error object if there is an error and null otherwise
|
||||
*/
|
||||
public SslError doHandshakeAndValidateServerCertificates(
|
||||
HttpsConnection connection, SSLSocket sslSocket, String domain)
|
||||
throws IOException {
|
||||
X509Certificate[] serverCertificates = null;
|
||||
HttpsConnection connection, SSLSocket sslSocket, String domain)
|
||||
throws SSLHandshakeException, IOException {
|
||||
|
||||
// start handshake, close the socket if we fail
|
||||
try {
|
||||
sslSocket.setUseClientMode(true);
|
||||
sslSocket.startHandshake();
|
||||
} catch (IOException e) {
|
||||
closeSocketThrowException(
|
||||
sslSocket, e.getMessage(),
|
||||
"failed to perform SSL handshake");
|
||||
++sTotal;
|
||||
|
||||
SSLContext sslContext = HttpsConnection.getContext();
|
||||
if (sslContext == null) {
|
||||
closeSocketThrowException(sslSocket, "SSL context is null");
|
||||
}
|
||||
|
||||
// retrieve the chain of the server peer certificates
|
||||
Certificate[] peerCertificates =
|
||||
sslSocket.getSession().getPeerCertificates();
|
||||
X509Certificate[] serverCertificates = null;
|
||||
|
||||
if (peerCertificates == null || peerCertificates.length <= 0) {
|
||||
closeSocketThrowException(
|
||||
sslSocket, "failed to retrieve peer certificates");
|
||||
} else {
|
||||
serverCertificates =
|
||||
new X509Certificate[peerCertificates.length];
|
||||
for (int i = 0; i < peerCertificates.length; ++i) {
|
||||
serverCertificates[i] =
|
||||
(X509Certificate)(peerCertificates[i]);
|
||||
long sessionBeforeHandshakeLastAccessedTime = 0;
|
||||
byte[] sessionBeforeHandshakeId = null;
|
||||
|
||||
SSLSession sessionAfterHandshake = null;
|
||||
|
||||
synchronized(sslContext) {
|
||||
// get SSL session before the handshake
|
||||
SSLSession sessionBeforeHandshake =
|
||||
getSSLSession(sslContext, connection.getHost());
|
||||
if (sessionBeforeHandshake != null) {
|
||||
sessionBeforeHandshakeLastAccessedTime =
|
||||
sessionBeforeHandshake.getLastAccessedTime();
|
||||
|
||||
sessionBeforeHandshakeId =
|
||||
sessionBeforeHandshake.getId();
|
||||
}
|
||||
|
||||
// update the SSL certificate associated with the connection
|
||||
if (connection != null) {
|
||||
if (serverCertificates[0] != null) {
|
||||
connection.setCertificate(
|
||||
new SslCertificate(serverCertificates[0]));
|
||||
// start handshake, close the socket if we fail
|
||||
try {
|
||||
sslSocket.setUseClientMode(true);
|
||||
sslSocket.startHandshake();
|
||||
} catch (IOException e) {
|
||||
closeSocketThrowException(
|
||||
sslSocket, e.getMessage(),
|
||||
"failed to perform SSL handshake");
|
||||
}
|
||||
|
||||
// retrieve the chain of the server peer certificates
|
||||
Certificate[] peerCertificates =
|
||||
sslSocket.getSession().getPeerCertificates();
|
||||
|
||||
if (peerCertificates == null || peerCertificates.length <= 0) {
|
||||
closeSocketThrowException(
|
||||
sslSocket, "failed to retrieve peer certificates");
|
||||
} else {
|
||||
serverCertificates =
|
||||
new X509Certificate[peerCertificates.length];
|
||||
for (int i = 0; i < peerCertificates.length; ++i) {
|
||||
serverCertificates[i] =
|
||||
(X509Certificate)(peerCertificates[i]);
|
||||
}
|
||||
|
||||
// update the SSL certificate associated with the connection
|
||||
if (connection != null) {
|
||||
if (serverCertificates[0] != null) {
|
||||
connection.setCertificate(
|
||||
new SslCertificate(serverCertificates[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get SSL session after the handshake
|
||||
sessionAfterHandshake =
|
||||
getSSLSession(sslContext, connection.getHost());
|
||||
}
|
||||
|
||||
if (sessionBeforeHandshakeLastAccessedTime != 0 &&
|
||||
sessionAfterHandshake != null &&
|
||||
Arrays.equals(
|
||||
sessionBeforeHandshakeId, sessionAfterHandshake.getId()) &&
|
||||
sessionBeforeHandshakeLastAccessedTime <
|
||||
sessionAfterHandshake.getLastAccessedTime()) {
|
||||
|
||||
if (HttpLog.LOGV) {
|
||||
HttpLog.v("SSL session was reused: total reused: "
|
||||
+ sTotalReused
|
||||
+ " out of total of: " + sTotal);
|
||||
|
||||
++sTotalReused;
|
||||
}
|
||||
|
||||
// no errors!!!
|
||||
return null;
|
||||
}
|
||||
|
||||
// check if the first certificate in the chain is for this site
|
||||
@@ -152,6 +216,7 @@ class CertificateChainValidator {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// first, we validate the chain using the standard validation
|
||||
// solution; if we do not find any errors, we are done; if we
|
||||
// fail the standard validation, we re-validate again below,
|
||||
@@ -328,14 +393,14 @@ class CertificateChainValidator {
|
||||
}
|
||||
|
||||
private void closeSocketThrowException(
|
||||
SSLSocket socket, String errorMessage, String defaultErrorMessage)
|
||||
throws IOException {
|
||||
SSLSocket socket, String errorMessage, String defaultErrorMessage)
|
||||
throws SSLHandshakeException, IOException {
|
||||
closeSocketThrowException(
|
||||
socket, errorMessage != null ? errorMessage : defaultErrorMessage);
|
||||
}
|
||||
|
||||
private void closeSocketThrowException(SSLSocket socket,
|
||||
String errorMessage) throws IOException {
|
||||
private void closeSocketThrowException(SSLSocket socket, String errorMessage)
|
||||
throws SSLHandshakeException, IOException {
|
||||
if (HttpLog.LOGV) {
|
||||
HttpLog.v("validation error: " + errorMessage);
|
||||
}
|
||||
@@ -351,4 +416,29 @@ class CertificateChainValidator {
|
||||
|
||||
throw new SSLHandshakeException(errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sslContext The SSL context shared accross all the SSL sessions
|
||||
* @param host The host associated with the session
|
||||
* @return A suitable SSL session from the SSL context
|
||||
*/
|
||||
private SSLSession getSSLSession(SSLContext sslContext, HttpHost host) {
|
||||
if (sslContext != null && host != null) {
|
||||
Enumeration en = sslContext.getClientSessionContext().getIds();
|
||||
while (en.hasMoreElements()) {
|
||||
byte[] id = (byte[]) en.nextElement();
|
||||
if (id != null) {
|
||||
SSLSession session =
|
||||
sslContext.getClientSessionContext().getSession(id);
|
||||
if (session.isValid() &&
|
||||
host.getHostName().equals(session.getPeerHost()) &&
|
||||
host.getPort() == session.getPeerPort()) {
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user