auto import from //depot/cupcake/@132589

This commit is contained in:
The Android Open Source Project
2009-03-03 14:04:24 -08:00
parent 3dec7d563a
commit 076357b856
310 changed files with 4072 additions and 18131 deletions

View File

@@ -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,

View File

@@ -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(

View File

@@ -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;
}
}