Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用AndroidHttpClient的SSL/TLS协议和密码套件_Java_Android_Ssl_Https_Openssl - Fatal编程技术网

Java 使用AndroidHttpClient的SSL/TLS协议和密码套件

Java 使用AndroidHttpClient的SSL/TLS协议和密码套件,java,android,ssl,https,openssl,Java,Android,Ssl,Https,Openssl,编辑:如果我原来的帖子措词不当,我深表歉意。这导致了一些混乱,比如对原始帖子的评论。让我再试一次: 我从一个问题开始。我想在Android上解决一个问题,但不知道如何解决。我花了很多时间在网上寻找解决方案,但在任何地方都找不到关于这个问题的任何讨论。然而,包括StackOverflow线程在内的许多讨论使我找到了一种看起来很有前途的技术。我解决了这个问题。但解决方案有点复杂。所以我决定把这个问题贴在这里,想a)一定有更好的解决办法,希望有人知道并把答案贴在这里;或者b)也许这是一个很好的解决方案

编辑:如果我原来的帖子措词不当,我深表歉意。这导致了一些混乱,比如对原始帖子的评论。让我再试一次:

我从一个问题开始。我想在Android上解决一个问题,但不知道如何解决。我花了很多时间在网上寻找解决方案,但在任何地方都找不到关于这个问题的任何讨论。然而,包括StackOverflow线程在内的许多讨论使我找到了一种看起来很有前途的技术。我解决了这个问题。但解决方案有点复杂。所以我决定把这个问题贴在这里,想a)一定有更好的解决办法,希望有人知道并把答案贴在这里;或者b)也许这是一个很好的解决方案,因为我在网上的其他地方没有发现关于这个问题的讨论,也许我对这个问题的解决方案会对其他尝试做同样事情的人有用。无论哪种方式,其结果都将是对StackOverflow的新贡献:一个其他地方没有回答的问题,最终以一种或另一种方式给出正确答案。事实上,StackOverflow甚至在我最初发布时邀请我通过分享我的知识来回答我自己的问题。事实上,这是我动机的一部分。我所发现的任何地方都没有收集到这件事的事实

因此:

Q.使用AndroidHttpClient通过HTTPS发出REST请求时,如何指定要使用的SSL协议和密码?

这很重要。值得注意的是,在服务器上可以做很多事情,但也有一些限制。同一台服务器必须服务于浏览器,包括旧的浏览器,以及其他客户端。这意味着服务器必须支持广泛的协议和密码。即使在Android中,如果你必须支持很多不同的版本,你也必须支持许多不同的协议和密码

更重要的是,默认情况下,在SSL握手期间,OpenSSL尊重客户端的密码首选项,而不是服务器的密码首选项。例如,您可以通过设置SSL_OP_CIPHER_SERVER_首选项来覆盖客户端中的该行为。在Java中,是否可以在SSLSocket上设置此选项还不完全清楚。即使可以,您也可以自己设置密码列表,或者告诉客户遵守服务器的列表。否则,您将获得Android默认值,无论您运行的版本是什么(而不是您链接的版本)

如果采用默认设置,则可以看到客户机通过Jellybean 4.2+客户机发送到服务器的首选项列表,从第504行开始。协议的默认列表从第620行开始。尽管Jellybean 4.2+包括对OpenSSL 1.0.1的支持,特别是TLSv1.1和TLSv1.2,但默认情况下不会启用这些协议。如果您没有像我所做的那样做,您就无法利用TLSv1.2支持,尽管最近版本的Android上宣传了对TLSv1.2的支持。当你回顾以前的Android版本时,细节会有很大的不同。至少,您可能希望仔细查看所有受支持版本上的默认值,并了解您的客户机实际在做什么。你可能会感到惊讶

关于对各种协议和密码的支持,您可以说得更多。关键是,有时可能需要在客户端中更改这些设置

A.使用自定义SSLSocketFactory

这对我来说效果很好,最后,它不是很好的代码。但它有点棘手,有几个原因:

  • 有两个不同的SSLSocketFactory类。客户需要一个 org.apache.http.conn.ssl.SSLSocketFactory,但OpenSSL返回 javax.net.ssl.SSLSocketFactory。这确实令人困惑。我曾经 代表团一个打电话给另一个,没有太大问题
  • 注意OpenSSLContextImpl和 SSLContextImpl。一个包裹另一个,但它们不是 可互换的。当我使用 SSLContextImpl.engineGetSocketFactory方法-我忘了具体是什么 事情发生了,但悄悄地失败了。请务必使用 打开SSLContextImpl以获取套接字工厂,而不是SSLContextImpl。 您也可以使用 javax.net.ssl.SSLSocketFactory.getDefault(),但我不确定
  • 您不能轻易地将AndroidHttpClient子类化,因为它的构造函数是 私有的这是不幸的,因为它提供了其他一些好处 好东西,比如确保你正确关闭它,而不是 泄露资源。DefaultHttpClient工作正常。我借了 来自AndroidHttpClient的newInstance方法(第105行左右)
要点:

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" });
然后:

在Android 3.0(蜂巢/SDK 11)之下,支持的密码选择变得更加有限,覆盖默认值的动机也越来越少。在FROYO/SDK 8上,我的SecureSocketFactory因某种原因爆炸,陪审团将于10日出局。但对11岁以上的人来说似乎效果不错

完整的解决方案位于公共github中

另一种解决方案可能是使用HttpsUrlConnection,这使得使用自定义套接字工厂变得很容易,但我认为您可能会失去高级HTTP客户端的更多便利性。我对HttpsUrlConnection没有任何经验

使用AndroidHttpClient通过HTTPS发出REST请求时,如何指定要使用的SSL协议和密码


我不相信你能用
AndroidHttpClient
做到这一点。无论是
SSLSocketFactory
还是
X509TrustManager
,我为强化通道所做的一切(如密码列表、证书固定和公钥固定)都需要某个自定义类。那是Java,那是Android。请参阅。

Stackoverflow并不是真正用于讨论的,如果你问这个问题的动机是“我想参与讨论”,请参阅:
中的这句话
// 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);