Android - 如何使用 HttpClient 接受任何证书并同时传递证书

Posted

技术标签:

【中文标题】Android - 如何使用 HttpClient 接受任何证书并同时传递证书【英文标题】:Android - How to accept any certificates using HttpClient and passing a certificate at the same time 【发布时间】:2011-09-27 18:53:24 【问题描述】:

我有一个场景,我必须将证书传递给我的服务器,然后服务器将他的证书发送给我,我必须接受该证书才能访问服务器。我为此使用了 HttpURLConnection,没有任何问题。

但是,我最近遇到了 HttpURLConnection 的问题。我使用的代码从 HTTPS 服务器检索图像。如果图像很小(

javax.net.ssl.SSLProtocolException: 读取错误: ssl=0x3c97e8: SSL 库失败,通常是协议错误

我在网上读到过,很多人说用 HttpClient 代替 HttpURLConnection 是要走的路(一个例子是这个网站http://soan.tistory.com/62 ,认为是用韩文写的,我看不懂但我认为它是这么说的)。

这是我的旧代码,使用 URLConnection:

    public static URLConnection CreateFromP12(String uri, String keyFilePath,
        String keyPass, TrustManager[] trustPolicy, HostnameVerifier hv) 
    try 

        SSLContext sslContext = SSLContext.getInstance("TLS");
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");

        keyStore.load(new FileInputStream(keyFilePath),
                keyPass.toCharArray());

        kmf.init(keyStore, keyPass.toCharArray());
        sslContext.init(kmf.getKeyManagers(), trustPolicy, null);

        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext
                .getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
     catch (Exception ex) 
        return null;
    

    URL url;
    URLConnection conn;
    try 
        url = new URL(uri);
        conn = url.openConnection();
     catch (MalformedURLException e) 
        return null;
     catch (IOException e) 
        return null;
    

    return conn;

这是新的,使用 HttpClient:

public class HttpC2Connection 

public static HttpEntity CreateHttpEntityFromP12(String uri,
        String keyFilePath, String keyPass) throws Exception 

    KeyStore keyStore = KeyStore.getInstance("PKCS12");
    keyStore.load(new FileInputStream(keyFilePath), keyPass.toCharArray());

    SSLSocketFactory sf = new MySSLSocketFactory(keyStore);
    sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

    HttpParams params = new BasicHttpParams();
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

    SchemeRegistry registry = new SchemeRegistry();

    registry.register(new Scheme("https", sf, 443));

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(params,
            registry);

    HttpClient httpclient = new DefaultHttpClient(ccm, params);
    HttpGet httpget = new HttpGet(uri);
    HttpResponse response = httpclient.execute(httpget);
    HttpEntity entity = response.getEntity();

    return entity;

但是现在,使用 HttpClient,我的服务器返回一个错误,说我必须通过证书,所以我猜

SSLSocketFactory sf = new MySSLSocketFactory(keyStore); 

没有加载我的证书。

那么,我怎样才能同时做以下两件事:

1.) 将证书传递给我的服务器; 2.) 接受来自我的服务器的任何证书

使用 HttpClient 类?

PS:我使用的是 android 3.0

谢谢

【问题讨论】:

【参考方案1】:

以下代码禁用对任何新的 HttpsUrlConnection 实例的 SSL 证书检查:

https://gist.github.com/aembleton/889392

/**
 * Disables the SSL certificate checking for new instances of @link HttpsURLConnection This has been created to
 * aid testing on a local box, not for use on production.
 */
private static void disableSSLCertificateChecking() 
    TrustManager[] trustAllCerts = new TrustManager[]  new X509TrustManager() 
        public X509Certificate[] getAcceptedIssuers() 
            return null;
        

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException 
            // Not implemented
        

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException 
            // Not implemented
        
     ;

    try 
        SSLContext sc = SSLContext.getInstance("TLS");

        sc.init(null, trustAllCerts, new java.security.SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
     catch (KeyManagementException e) 
        e.printStackTrace();
     catch (NoSuchAlgorithmException e) 
        e.printStackTrace();
    

【讨论】:

【参考方案2】:

不要只接受任何证书。 不要使用会危及安全的自制SSLSocketFactory。使用 SDK 中的 SSLSocketFactory,并传递一个信任库(包含服务器证书或颁发它的 CA 证书)和一个密钥库(包含您的客户端证书和私钥)。您可以使用this 构造函数来实现这一点,JavaDoc 中有关于如何创建密钥存储的详细信息。

【讨论】:

谢谢你,尼古拉。我会看看。但就我而言,接受任何证书都没有什么大不了的,因为我在测试环境中运行。无论如何,我会看看并发布反馈。 谢谢,尼古拉。对于遇到同样问题的任何人,阅读以下内容:blog.crazybob.org/2010/02/… 也非常有用。

以上是关于Android - 如何使用 HttpClient 接受任何证书并同时传递证书的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 和/或 Java 中使用 HttpClient 管理 cookie?

Android 上的 HttpClient 和自定义 TrustManager

Android 已弃用 apache 模块(HttpClient、HttpResponse 等)

如何在 HttpClient 4 中使用 SPDY?

如何从 HttpClient 检索照片?

在 httpClient 中使用 POST 时如何确保数据被加密