【问题标题】:AndroidHttpClient need more info. Self-signed SSL. Possible?AndroidHttpClient 需要更多信息。自签名 SSL。可能的?
【发布时间】:2011-04-13 19:53:50
【问题描述】:

关于 AndroidHttpClient 的信息很少,特别是我找不到任何好的例子。从我读到的 - 我可以使用这个客户端,它是为 SSL 预配置的。我的目标是 2.2+,所以它对我很有效。

  1. 有没有关于我如何使用它的好示例?专门用于 REST 服务 POST
  2. 是否有关于如何允许自签名证书的示例?我不介意只允许任何证书而不是将特定证书导入本地商店。

谢谢!

我自己的答案(见下面的代码)。

  1. 我有带有自签名证书的 IIS 服务器。我不得不采取额外的步骤并生成与外部名称匹配的证书,而不是服务器名称。
  2. 我使用 AndroidHttpClient。据说,此客户端具有适用于 android 的所有“正确”设置,并从版本 8 开始支持
  3. 我在 Application 对象中创建 AndroidHttpClient 并共享。
  4. 我在注入自定义证书的地方分离了代码,以便以后轻松摆脱它。我注意到应用启动时从资源加载证书确实需要一些时间。

我的应用程序单例版本。请参阅顶部的 cmets 以及我用来生成所有内容的命令行的详细信息。始终使用相同的密码以确保其正常工作。 PKS 文件密码必须匹配。

import android.net.http.AndroidHttpClient;
import android.app.Application;
import android.util.Log;
import idatt.mobile.android.providers.DBLog;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;

import java.io.InputStream;
import java.security.KeyStore;

/*
To generate PKS:
1. Created cert in IIS7 and then exported as pfx. Follow instruction on SelfSSL: http://www.robbagby.com/iis/self-signed-certificates-on-iis-7-the-easy-way-and-the-most-effective-way/
1a. Download tool: http://cid-3c8d41bb553e84f5.skydrive.live.com/browse.aspx/SelfSSL
1b. Run: SelfSSL /N:CN=mydomainname /V:1000 /S:1 /P:8081
 I use port 8081 on my server
1c. Export from IIS manager to cert.pfx
2. Run command line in SSL to convert file into X.509:
openssl pkcs12 -in C:\cert.pfx -out C:\cert.cer -nodes
3. Edit file and delete all except -----BEGIN.... END CERTIFICATE----- IMPORTANT! It was working when I got proper (5) amount of dashes and put tags and data on separate lines
4. use keytool. C:\Java\JDK\bcprov.jar was downloaded separately
 C:\Users\Ivan>keytool -import -v -trustcacerts -alias key_alias -file C:\cert.cer -keystore C:\mystore.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath C:\Java\JDK\bcprov.jar -storepass 123456

*/

public class MyApplication extends Application
{
    private static final String LOG_TAG = "MyApplication";
    private AndroidHttpClient androidHttpClient;

    @Override
    public void onCreate()
    {
        super.onCreate();
        androidHttpClient = createAndroidHttpClient();
    }

    @Override
    public void onLowMemory()
    {
        super.onLowMemory();
        shutdownAndroidHttpClient();
    }

    @Override
    public void onTerminate()
    {
        super.onTerminate();
        shutdownAndroidHttpClient();
    }


    private AndroidHttpClient createAndroidHttpClient()
    {
        Log.d(LOG_TAG,"createAndroidHttpClient");

        AndroidHttpClient client = AndroidHttpClient.newInstance("Android");

        //This is optional call to inject custom BKS that was created from self-signed certificate
        client = addCustomCertificate(client);

        return client;
    }

    public AndroidHttpClient getAndroidHttpClient()
    {
        return androidHttpClient;
    }

    private void shutdownAndroidHttpClient()
    {
        if(androidHttpClient!=null && androidHttpClient.getConnectionManager()!=null)
        {
            androidHttpClient.getConnectionManager().shutdown();
        }
    }

    private AndroidHttpClient addCustomCertificate(AndroidHttpClient client)
    {
        SSLSocketFactory sf = SSLSocketFactory.getSocketFactory();

        try
        {
            InputStream in = getResources().openRawResource(R.raw.home_server);

            KeyStore trustStore = KeyStore.getInstance("BKS");

            trustStore.load(in, "123456".toCharArray());
            in.close();

            sf = new SSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
        }
        catch (Exception t)
        {
            DBLog.InsertError(this, t);
        }

        //Lets register our custom factory here
        client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", sf, 443));

        return client;
    }
}

这是我如何使用这个客户端(我在 AsyncTask 中调用它)

private String processPOST(String url, String requestData)
{
    String responseData = null;
    application = (MyApplication)getApplication();
    AndroidHttpClient client = application.getAndroidHttpClient();
    HttpPost request = new HttpPost(url);

    try
    {
        StringEntity entity = new StringEntity(requestData);
        entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
        request.setEntity(entity);
        ResponseHandler<String> handler = new BasicResponseHandler();
        responseData = client.execute(request, handler);
    }
    catch (Throwable e)
    {
        DBLog.InsertError(ctxt, e);
    }

    return responseData;
}

这种组合似乎 100% 可在 2.2 和 2.3 设备上运行。当我将 sn-ps 与 DefaultHttpClient 一起使用时,我遇到了 2.3.1 请求超时问题(Nexus S)

【问题讨论】:

    标签: android


    【解决方案1】:

    您可以使用 Apache HttpClient。

        public HttpClient getNewHttpClient() {
        try {
            KeyStore trustStore = KeyStore.getInstance("BKS");
            InputStream in = getResources().openRawResource(R.raw.mykeystore);
            try {
                trustStore.load(in, "mypassword".toCharArray());
            } finally {
                in.close();
            }
    
            SSLSocketFactory sf = new SSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_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("http", PlainSocketFactory.getSocketFactory(), 80));
            registry.register(new Scheme("https", sf, 443));
    
            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
            return new DefaultHttpClient(ccm, params);
        } catch (Exception e) {
            return new DefaultHttpClient();
        }
    }
    

    在 web 服务器中,IIS 可以创建自签名证书并导出为 PFX,然后使用 openssl 工具将其转换为 PEM,将其编辑为仅包含证书,然后使用 JDK 和 Bouncy Castle 的 keytool 创建包含证书的密钥库罐。如上代码所示,可以将创建的密钥库导入到您的项目中。

    【讨论】:

    • @bob 你能帮我解决这个问题吗?我到了我有PEM的地步。我应该如何编辑它?然后我完全不知道如何进行。我找到了blog.crazybob.org/2010/02/…,但这对我来说很乱,因为我对 Java 很陌生,而且主要是 .NET 开发人员。
    • @katit 那个博客是我提到的。 PEM 是可测试编辑的,它可能包含密钥和证书。只需删除除证书本身以外的所有内容,使其看起来像博客中显示的内容并保存。然后继续使用 keytool 和 Bouncy Castle jar 创建密钥库。然后可以将密钥库构建到您的应用中。
    • @bob 我快到了 :) 出现不同的错误 - 现在它抱怨名称不匹配,但这是因为 IIS 生成了类似的自签名证书。我发现了另一篇关于如何解决该问题的帖子robbagby.com/iis/…。所以,如果有人感兴趣,我会发布完整的如何在 AndroidHttpClient + 自签名 IIS 证书上
    • @katit 如果您的问题与主机名验证有关,则可以在上面的代码中使用 SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER 代替 SSLSocketFactory.STRICT_HOSTNAME_VERIFIER。是的,我对此很感兴趣;)
    • @bob 我在主帖中发布了我的解决方案。感谢您的帮助,我会标记您的答案。将了解 STRICT_HOSTNAME_VERIFIER。现在我想 - 我学到了一些新东西,至少在证书上进行名称匹配不会有什么坏处。
    【解决方案2】:

    我使用“信任所有”SSL 连接进行了一些测试:HTTPS GET (SSL) with Android and self-signed server certificate

    希望对你有帮助:D

    【讨论】:

      【解决方案3】:

      您可以找到example here - 唯一“不寻常”的是,如果遥控器返回 401(?)代码,您将无法通过 connection.getResponseCode() 获取它,但会抛出异常(也许5xx 也是如此)。

      上面的例子运行在 SSL 上,所以 https 是可能的。我不能告诉任何关于自签名证书的事情。我想我记得其他人说他们有问题,但我不确定。

      【讨论】:

        【解决方案4】:

        Android 集成了Apache http client component。如果您查看链接的文档部分,您将找到教程和示例。

        如果您搜索 Apache Http Client 而不是 Android Http Client,您会发现很多信息。

        编辑:哦 - 我刚刚意识到 Android 中有一个 AndroidHttpClient 类。从来没有用过。看看对我有帮助的this 答案。

        【讨论】:

        • 是的,我想用AndroidHttpClient
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-27
        • 2016-03-03
        相关资源
        最近更新 更多