本文介绍了SSL / TLS协议和加密套件与AndroidHttpClient的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:道歉,如果我原来的职位措辞不当。这导致了一些混乱,再通过注释到原来的职位psented $ P $。因此,让我再试一次:

我开始用的问题。我想解决在Android上有问题,但不知道怎么办。我花了很多时间环视网解决方案,但发现此事没有一个讨论的任何地方。尽管如此,一些讨论,其中包括计算器线程中,使我看起来有前途的技术。我解决了这个问题。但是,该解决方案是一个有点复杂。所以我决定后这里的问题,思考了)必须有一个更好的解决方案,并希望有人会知道,并在这里发表的answere;或b)也许这是一个很好的解决方案,因为我发现这件事其他地方在网络上没有讨论,也许我的问题的解决将是有益的其他人试图做同样的事情。无论哪种方式,其结果将是计算器了新的贡献:未在其他地方回答,与最终的问题,正确的答案的一种方式或其他。事实上,计算器甚至邀请我来回答我的问题通过分享我的知识,当我最初发布它的方式。这是,事实上,部分我的动机。这件事情连事实都没有收集任何地方,我已经找到。

所以:

问。当使用AndroidHttpClient使通过HTTPS REST请求,我怎么可以指定哪些SSL协议和密码使用?

这是非常重要的。的意思是很好的,有许多可在服务器上进行,但也有限制。同一服务器具有服务于浏览器,包括旧的,以及其他客户端。这意味着服务器必须支持协议和密码浩如烟海。即使在Android的,如果你要支持很多不同的版本,你将不得不支持许多不同的协议和密码的。

更重要的是,在默认情况下,OpenSSL的荣誉客户端的密码preference,没有的服务器,在SSL握手期间。看到这个帖子,例如,它说,你可以通过设置SSL_OP_CIPHER_SERVER_ $覆盖客户端上的行为p $ pFERENCE。这并不完全清楚,如果这个选项甚至可以在Java中的SSLSocket来设置。即使可以,你可以自己设置密码列表,或告诉你的客户兑现服务器的列表。否则,你得到了Android默认的,不管它可能是你正在运行的版本(不是你对链接的版本)。

如果您采用默认值,由客户通过杰利贝恩4.2+客户端发送到服务器的preference列表中可以看出here,各地开始行504协议的默认列表开始围绕线620虽然杰利贝恩4.2+包括OpenSSL的1.0.1,特别是TLSv1.1和TLSv1.2支持,这些协​​议不是默认启用的。如果你没有做这样的事情我做了什么,你不能利用TLSv1.2支持,尽管对于TLSv1.2支持发布在最新的Andr​​oid版本的事实。而细节有很大不同,你去背透previous的Andr​​oid版本。至少,你不妨仔细看看所有受支持版本的缺省值,看看你的客户实际上是在做。你可能会惊讶。

还有很多更可以说有关各种协议和加密算法的支持。的点是,可以在时间是需要在客户机更改这些设置。

一个。使用自定义SSLSocketFactory的

这工作很适合我,而在最后,这是不是很code。但是,这是一个有点棘手了几个原因:

  • 有两种不同的SSLSocketFactory类。客户端需要一个org.apache.http.conn.ssl.SSLSocketFactory,但OpenSSL的返回javax.net.ssl​​.SSLSocketFactory。这绝对是令人困惑的。我用了代表团做一个呼叫对方,没有太大的问题。
  • 当心OpenSSLContextImpl之间的差异SSLContextImpl。一个只是包装对方,但他们都没有互换。当我用SSLContextImpl.engineGetSocketFactory方法 - 我忘了究竟是什么发生的事,但悄悄地失败了。一定要使用OpenSSLContextImpl让你的套接字工厂,而不是SSLContextImpl。您可能还可以使用javax.net.ssl​​.SSLSocketFactory.getDefault(),但我不知道。
  • 您不能轻易继承AndroidHttpClient,因为它的构造是私人。这是不幸的,因为它提供了其它一些不错好吃的东西,就像确保您关闭它,而不是正确的泄漏资源。该DefaultHttpClient工作得很好。我借了从AndroidHttpClient newInstance方法(约行105)。

要点:

 公共类SecureSocketFactory扩展的SSLSocketFactory {
    @覆盖
    公共插座中的createSocket(插座S,字符串主机,INT端口,布尔自动关闭)抛出IOException异常{
        //为了不应该事在这里;服务器应选择最高
        //一个来自这个名单,它支持
        s.setEnabledProtocols(新的String [] {TLSv1.2,使用TLSv1});

        //为了在这里事宜;指定preference订单
        s.setEnabledCipherSuites(新的String [] {ECDHE-RSA-RC4-SHA,RC4-SHA});
 

然后:

  //创建时,客户端
的HttpParams PARAMS;
SchemeRegistry schemeRegistry =新SchemeRegistry();

//使用自定义套接字工厂的HTTPS
SSLSocketFactory的SF =新SecureSocketFactory();
schemeRegistry.register(新计划(https开头,SF,443));

//使用默认的HTTP
schemeRegistry.register(新计划(HTTP,
            PlainSocketFactory.getSocketFactory(),80));

ClientConnectionManager经理=
            新ThreadSafeClientConnManager(参数,可以schemeRegistry);

HttpClient的客户端=新DefaultHttpClient(经理,则params);
 

下面的Andr​​oid 3.0(蜂窝/ SDK 11),所支持的加密选项变得更加有限,而且也不太动机覆盖默认值。在升级Froyo / SDK 8,我SecureSocketFactory炸毁出于某种原因,而陪审团是列于10,但它似乎工作了11向上就好了。

完整的解决方案是在公共GitHub的回购

另一种解决方案可能是使用一个HttpsUrlConnection,它可以很容易地使用自定义套接字工厂,但我想你可能会失去更多的高层次的HTTP客户端的便捷性。我没有与HttpsUrlConnection任何经验。

解决方案

我不相信你可以用 AndroidHttpClient 做到这一点。我所做的一切硬化渠道(如密码列表,证书钉扎,和公钥钉扎)的地方需要自定义类,无论是 SSLSocketFactory的 X509TrustManager 。这是Java的,这就是机器人。请参阅How覆盖在使用发送到服务器搭载Android的cipherlist HttpsURLConnection?。

EDIT: Apologies if my original post was poorly worded. It led to some confusion, represented by comments to the original post. So let me try again:

I started with a question. I wanted to solve a problem on Android, but didn't know how. I spent a lot of time looking around the net for solutions, but found not one single discussion of the matter anywhere. Nevertheless, a number of discussions, including StackOverflow threads, led me to a technique that looked promising. I solved the problem. But the solution was a little involved. So I decided to post the question here, thinking a) there must be a better solution, and hopefully someone will know and post the answere here; or b) maybe this is a good solution, and since I found no discussion of the matter anywhere else on the net, maybe my solution to the problem would be useful to others trying to do the same thing. Either way, the result would be a new contribution to StackOverflow: a question that is not answered elsewhere, with, eventually, the correct answer one way or the other. In fact, StackOverflow even invited me to answer my own question by way of sharing my knowledge when I originally posted it. That was, in fact, part of my motivation. Even the facts of this matter are not collected anywhere that I have found.

So:

Q. When using the AndroidHttpClient to make REST requests via HTTPS, how can I specify which SSL protocols and ciphers to use?

This is important. The point is well taken that there is much that can be done on the server, but there are limits. The same server has to serve browsers, including old ones, as well as other clients. That means the server has to support a broad array of protocols and ciphers. Even within Android, if you have to support a lot of different versions, you're going to have to support a number of different protocols and ciphers.

More importantly, by default, OpenSSL honors the client's cipher preference, not the server's, during the SSL handshake. See this post, for example, which says that you can override that behavior in the client by setting SSL_OP_CIPHER_SERVER_PREFERENCE. It's not entirely clear if this option can even be set on an SSLSocket in Java. Even if it can, you can set the cipher list yourself or tell your client to honor the server's list. Otherwise, you're getting the Android default, whatever it may be for the version you're running on (not the version you link against).

If you take the defaults, the preference list sent by the client to the server by a Jellybean 4.2+ client can be seen here, starting around line 504. The default list of protocols starts around line 620. Though Jellybean 4.2+ includes support for OpenSSL 1.0.1, particularly TLSv1.1 and TLSv1.2, these protocols are not enabled by default. If you do not do something like what I've done, you cannot take advantage of TLSv1.2 support, despite the fact that support for TLSv1.2 is advertised on recent versions of Android. And the details vary quite a bit as you go back through previous Android versions. At least, you may wish to take a close look at the defaults on all supported versions and see what your client is actually doing. You might be surprised.

There is lots more you can say about support for various protocols and ciphers. The point is, there can at times be a need to change these settings in a client.

A. Use a custom SSLSocketFactory

This worked well for me, and in the end, it was not very much code. But it was a little thorny for a couple of reasons:

  • There are two different SSLSocketFactory classes. The client needs anorg.apache.http.conn.ssl.SSLSocketFactory, but OpenSSL returns ajavax.net.ssl.SSLSocketFactory. This is definitely confusing. I useddelegation to make the one call the other, without much problem.
  • Watch out for the difference between the OpenSSLContextImpl and theSSLContextImpl. One just wraps the other, but they are notinterchangeable. When I used theSSLContextImpl.engineGetSocketFactory method—I forget what exactlyhappened, but something quietly failed. Be sure to use anOpenSSLContextImpl to get your socket factory, not an SSLContextImpl.You might also be able to usejavax.net.ssl.SSLSocketFactory.getDefault(), but I'm not sure.
  • You cannot easily subclass AndroidHttpClient, because its constructor isprivate. This is unfortunate, since it provides some other nicegoodies, like making sure you shut it down properly instead ofleaking resources. The DefaultHttpClient works just fine. I borrowedthe newInstance method from AndroidHttpClient (around line 105).

The key points:

public class SecureSocketFactory extends SSLSocketFactory {
    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        // order should not matter here; the server should select the highest
        // one from this list that it supports
        s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1" });

        // order matters here; specify in preference order
        s.setEnabledCipherSuites(new String[] { "ECDHE-RSA-RC4-SHA", "RC4-SHA" });

Then:

// when creating client
HttpParams params;
SchemeRegistry schemeRegistry = new SchemeRegistry();

// use custom socket factory for https
SSLSocketFactory sf = new SecureSocketFactory();
schemeRegistry.register(new Scheme("https", sf, 443));

// use the default for http
schemeRegistry.register(new Scheme("http",
            PlainSocketFactory.getSocketFactory(), 80));

ClientConnectionManager manager =
            new ThreadSafeClientConnManager(params, schemeRegistry);

HttpClient client = new DefaultHttpClient(manager, params);

Below Android 3.0 (Honeycomb/SDK 11), the supported cipher choices become more limited, and there's less motivation to override the defaults. On FROYO/SDK 8, my SecureSocketFactory blows up for some reason, and the jury is out on 10. But it seems to work for 11 upward just fine.

The full solution is in a public github repo.

Another solution might be to use an HttpsUrlConnection, which makes it easy to use a custom socket factory, but I imagine you'll probably lose even more of the convenience of the high-level HTTP client. I don't have any experience with HttpsUrlConnection.

解决方案

I don't believe you can do it with AndroidHttpClient. Everything I've done to harden the channel (like cipher lists, certificate pinning, and public key pinning) required a custom class somewhere, whether it was SSLSocketFactory or X509TrustManager. That's Java and that's Android. See How to override the cipherlist sent to the server by Android when using HttpsURLConnection?.

这篇关于SSL / TLS协议和加密套件与AndroidHttpClient的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 14:14