HttpClient and custom TrustManager on android

10,395

The solution is to create your own socket factory.

public class NetworkSSLSocketFactory implements LayeredSocketFactory {

    private SSLContext sslContext;
    private SSLSocketFactory socketFactory;
    private X509HostnameVerifier hostnameVerifier;

    /**
     * Creates a socket factory that will use the {@link SSLContext} and
     * {@link X509HostnameVerifier} specified. The SSLContext provided should
     * have the {@link NetworkTrustManager} associated with it.
     * 
     * @param sslContext
     * @param hostnameVerifier
     */
    public NetworkSSLSocketFactory(SSLContext sslContext,
            X509HostnameVerifier hostnameVerifier) {
        this.sslContext = sslContext;
        this.socketFactory = sslContext.getSocketFactory();
        this.hostnameVerifier = hostnameVerifier;
    }  
}

Then create an SSLContext that uses your TrustManager and then create a AndroidHttpClient and replace its https schema with one that uses your SocketFactory.

    /**
     * Return the SSLContext for use with our HttpClient or create a new Context
     * if needed.
     * <p>
     * This context uses our {@link NetworkTrustManager}
     * 
     * @return an {@link SSLContext}
     */
    public SSLContext getSSLContext() {

        if (mSSLContextInstance != null)
            return mSSLContextInstance;

        try {
            mSSLContextInstance = SSLContext.getInstance("TLS");
            TrustManager trustManager = new NetworkTrustManager(getKeyStore());
            TrustManager[] tms = new TrustManager[] { trustManager };
            mSSLContextInstance.init(null, tms, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, e.getMessage());
        } catch (KeyManagementException e) {
            Log.e(TAG, e.getMessage());
        }

        return mSSLContextInstance;
    }

Now the client

/**
 * Return an HttpClient using our {@link NetworkTrustManager} and
 * {@link NetworkHostnameVerifier}
 * 
 * @return an {@link HttpClient}
 */
public HttpClient getHttpClient() {

    if (mHttpClientInstance != null)
        return mHttpClientInstance;

    SSLContext sslContext = getSSLContext();

    // Now create our socket factory using our context.
    X509HostnameVerifier hostnameVerifier = new NetworkHostnameVerifier();
    NetworkSSLSocketFactory sslSocketFactory = new NetworkSSLSocketFactory(
            sslContext, hostnameVerifier);

    // Some services (like the KSOAP client) use the HttpsURLConnection
    // class
    // to establish SSL connections.
    HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
    HttpsURLConnection.setDefaultSSLSocketFactory(sslContext
            .getSocketFactory());

    // Generate the Client for the Server
    mHttpClientInstance = AndroidHttpClient.newInstance(getAgent(),
            mContext);

    // Get the registry from the AndroidHttpClient and change the
    // HTTPS scheme to use our socket factory. This way we can
    // control the certificate authority and trust system.
    SchemeRegistry schemeRegistry = mHttpClientInstance
            .getConnectionManager().getSchemeRegistry();

    schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));

    return mHttpClientInstance;
}

If you don't know how to create a new keystore, here you go:

    /**
     * Get the current KeyStore or if not yet created, create a new one. This
     * will <b>NOT</b> load the KeyStore file identified by
     * {@link #KEYSTORE_NAME}. To load the KeyStore file, use the function
     * {@link #loadKeyStore()} which will automatically call this function (so
     * you don't need to).
     * <p>
     * 
     * @return a {@link KeyStore}
     */
    public KeyStore getKeyStore() {

        if (mKeyStore != null)
            return mKeyStore;

        try {
            String defaultType = KeyStore.getDefaultType();
            mKeyStore = KeyStore.getInstance(defaultType);
            mKeyStore.load(null, null);
        } catch (Exception e) {
            Log.w(TAG, e.getMessage());
        }

        return mKeyStore;
    }

The above solution is the beginning of a solution that allows you to create a TrustManager that will validate certificates to the "System KeyStore" as well as you own "Private KeyStore" (two keystores). Then, you don't need to try to add certificates to the System KeyStore. You can create your own KeyStore in your getFilesDir() folder.

I still don't have the logic finished to trap the Certificate from the HttpResult = HttpClient.execute(HttpPost); method, but I am actively writing this now. If you need help, I might be able to work this with you now.

If someone knows how to trap/get the Certificate from the SSLSocekt within the HttpRequestBase object, please let me know. I'm trying to hunt this down.

Share:
10,395
Ivo
Author by

Ivo

Updated on June 05, 2022

Comments

  • Ivo
    Ivo almost 2 years

    I've been trying to register my custom TrustManger with the apache HttpClient library. The following link contains directions on how to perform this: Https Connection Android

    Unfortunately the constructor I would like to use ( public SSLSocketFactory(SSLContext sslContext) ) is not available in the Android version of HttpClient. I would use a sslContext to initialize my custom TrustManager. It seems Android has replaced this by a 'KeyStore'.

    My question is: (how) can I register a custom TrustManger with DefaultHttpClient in Android? Is there an alternative somewhere in the KeyStore classes?

    Ultimately I would like to ignore the certificate checking for now... Please only consider the HttpClient library since my whole app is based on it.