关于ssl:HttpClient和Android上的自定义TrustManager

HttpClient and custom TrustManager on android

我一直在尝试向apache HttpClient库注册我的自定义TrustManger。 以下链接包含有关如何执行此操作的说明:Https Connection Android

不幸的是,我想使用的构造函数(public SSLSocketFactory(SSLContext sslContext))在Android版本的HttpClient中不可用。 我将使用sslContext初始化我的自定义TrustManager。 似乎Android已将其替换为" KeyStore"。

我的问题是:(如何)我可以在Android的DefaultHttpClient中注册自定义TrustManger? KeyStore类中是否有替代方法?

最终,我现在想忽略证书检查...
请仅考虑HttpClient库,因为我的整个应用程序都基于该库。


解决方案是创建自己的套接字工厂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
    }  
}

然后创建一个使用TrustManager的SSLContext,然后创建一个AndroidHttpClient并将其https模式替换为使用SocketFactory的https模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    /**
     * 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;
    }

现在的客户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * 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;
}

如果您不知道如何创建新的密钥库,请执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    /**
     * Get the current KeyStore or if not yet created, create a new one. This
     * will NOT 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;
    }

上面的解决方案是解决方案的开始,允许您创建一个TrustManager来验证到"系统密钥库"的证书以及您自己的"私人密钥库"(两个密钥库)。然后,您无需尝试将证书添加到系统密钥库。您可以在getFilesDir()文件夹中创建自己的KeyStore。

我仍然没有完成从HttpResult = HttpClient.execute(HttpPost);捕获证书的逻辑。方法,但我现在正在积极编写。如果您需要帮助,我现在可以与您合作。

如果有人知道如何从HttpRequestBase对象内的SSLSocekt捕获/获取证书,请告诉我。我正在努力寻找答案。


实际上,该方法存在但被隐藏。 getHttpSocketFactory()在内部使用它。我只是通过使用反射使它起作用。不安全,但是可以用于开发目的。

1
2
3
Class<org.apache.http.conn.ssl.SSLSocketFactory> c = org.apache.http.conn.ssl.SSLSocketFactory.class;
Constructor<org.apache.http.conn.ssl.SSLSocketFactory> ctor = c.getConstructor(javax.net.ssl.SSLSocketFactory.class);
SSLSocketFactory ssf = ctor.newInstance(SSLCertificateSocketFactory.getInsecure(getSSLHandshakeTimeout(), null));