【问题标题】:How to force Commons HTTPClient 3.1 to use TLS 1.2 only for HTTPS?如何强制 Commons HTTPClient 3.1 仅将 TLS 1.2 用于 HTTPS?
【发布时间】:2015-12-11 18:33:47
【问题描述】:

我希望强制 Apache Commons HTTP-Client(版本 3.1)使用 TLS 1.2 作为 HTTPS 协议。

这是由于服务器据称已升级到 TLS 1.2 并且不再接受任何旧协议(导致返回“连接重置”)。

对于进一步的上下文,可能无关紧要,HTTP-Client 与 Axis2 一起用于制作 SOAP;一些用于设置 HttpClient 的代码如下:

MultiThreadedHttpConnectionManager connMgr = new MultiThreadedHttpConnectionManager();
this.httpClient = new HttpClient(connMgr);

// initialize HttpClient parameters
HttpClientParams hcParams = this.httpClient.getParams();

// Maximum time to wait to receive connection from pool
hcParams.setConnectionManagerTimeout(this.maxWait);
hcParams.setSoTimeout(this.timeout);
hcParams.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(this.retryCount, false));

// Initialize global Connection manager parameters
HttpConnectionManagerParams cmParams = connMgr.getParams();
cmParams.setDefaultMaxConnectionsPerHost(this.maxActive);
cmParams.setStaleCheckingEnabled(this.checkStaleConnections);
cmParams.setConnectionTimeout(this.timeout);

非常感谢您的帮助!

【问题讨论】:

  • 由于您使用的是这个旧的且无人维护的软件,我假设您也使用的是旧的 Java 版本。您确定您的 Java 完全可以使用 Java 1.2(即您使用的是哪个版本的 Java?)
  • 不,我用的是Java 7,而且代码还不算太旧,到现在为止都运行良好。

标签: java ssl https apache-commons-httpclient


【解决方案1】:

这取决于您编写客户端的方式以及您使用的 JRE 版本:

如果您使用的是 JRE8(除非您已替换 JRE8 附带的默认 SunJSSE),则有一个系统属性“jdk.tls.client.protocols”。默认情况下,您在此处提及的任何内容都将用于所有客户端通信。

如果您使用 HttpsURLConnection 对象进行客户端连接,您可以使用系统属性“https.protocols”。这适用于所有 JRE 版本,而不仅仅是 JRE8。

如果您不指定任何内容,对于 TLS 客户端,在 JRE8 中,TLSv1、v1.1 和 v1.2 已启用,因此它将与支持其中任何一个版本的服务器一起使用。但是在 JRE7 中,默认情况下仅启用 TLSv1。

在您的代码中,您始终可以覆盖默认值或您通过系统属性传递的内容。您在代码中设置的内容将具有更高的优先级。在代码中覆盖...

1) 如果您使用的是原始套接字和 SSLEngine,您可以在 SSLEngine 中设置协议和密码 (sslEngine.setEnabledProtocols(..)

2) 如果您使用 SSLSocket,您可以在 SSLSocket 中设置协议和密码 (sslSocket.setEnabledProtocols(..)

您还可以获得启用了所需协议的 SSLContext,并将其用于您使用的任何 SSL 组件。 SSLContext.getInstance("TLSvx.x")。请注意,默认情况下,它将返回一个包含所有低于 TLSvx.x 启用的协议的上下文。如果你配置了“jdk.tls.client.protocols”,这将返回一个启用了这些协议的上下文。

在代码中硬编码协议不是一个好主意。很多时候,我们会遇到某些客户想要特定版本,要么是因为他们使用旧服务器,要么是在某些 TLS 版本中遇到了一些严重的漏洞。通过系统属性设置它,或者即使您在 sslsocket 或 sslengine 中显式设置,也可以从某个 conf 文件中读取。

另请参考:

https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html

http://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html

【讨论】:

    【解决方案2】:

    您的代码中需要一个 Socket 引用。然后你可以像这样设置启用的协议:

    if (socket != null && (socket instanceof SSLSocket)) {
        ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
    }
    

    【讨论】:

    • 我很困惑如何在我的 HttpClient (3.1) 实现中使用它。套接字对象在哪里初始化?我还想确保我设置的协议仅由我的 HttpClient 使用,但没有其他可能在其他地方发出 http 请求的类/实现。使用 Java 8,目前无法升级到其他版本的 HttpClient。
    • 这是否意味着 HttpClient,其指南说“HttpClient 通过利用 Java 安全套接字扩展 (JSSE) 提供对基于安全套接字层 (SSL) 或 IETF 传输层安全 (TLS) 协议的 HTTP 的完全支持).",表示默认情况下 Java 8 提供的套接字是启用的?那么支持到 TLSv1.2 呢?我的目的是找出默认使用的协议,然后确保至少支持 SSL 3.0 和转发。结果发现比预期的更难,也许我问错了问题。谢谢!
    【解决方案3】:

    太糟糕了,没有人回答;我能做到,首先你写一个CustomHttpSocketFactory,然后你就做:

    String scheme = "https";
    Protocol baseHttps = Protocol.getProtocol(scheme);
    int defaultPort = baseHttps.getDefaultPort();
    
    ProtocolSocketFactory baseFactory = baseHttps.getSocketFactory();
    ProtocolSocketFactory customFactory = new CustomHttpsSocketFactory(baseFactory);
    
    Protocol customHttps = new Protocol(scheme, customFactory, defaultPort);
    Protocol.registerProtocol(scheme, customHttps); 
    

    here 找到了一个示例自定义套接字工厂代码,但我做了:

    public class CustomHttpsSocketFactory implements SecureProtocolSocketFactory
    {
    
       private final SecureProtocolSocketFactory base;
    
       public CustomHttpsSocketFactory(ProtocolSocketFactory base)
       {
          if(base == null || !(base instanceof SecureProtocolSocketFactory)) throw new IllegalArgumentException();
          this.base = (SecureProtocolSocketFactory) base;
       }
    
       private Socket acceptOnlyTLS12(Socket socket)
       {
          if(!(socket instanceof SSLSocket)) return socket;
          SSLSocket sslSocket = (SSLSocket) socket;
          sslSocket.setEnabledProtocols(new String[]{"TLSv1.2" });
          return sslSocket;
       }
    
       @Override
       public Socket createSocket(String host, int port) throws IOException
       {
          return acceptOnlyTLS12(base.createSocket(host, port));
       }
       @Override
       public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException
       {
          return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort));
       }
       @Override
       public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException
       {
          return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort, params));
       }
       @Override
       public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException
       {
          return acceptOnlyTLS12(base.createSocket(socket, host, port, autoClose));
       }
    
    }
    

    【讨论】:

    • SecureProtocolSocketFactorySSLSocketFactory 不同吗?
    • SecureProtocolSocketFactory 是 Apache 的 HTTPClient 中的一个接口,有助于实现自定义套接字工厂;我相信 java 的 SSLSocketFactory 在这种情况下会不太有用。
    • @united-expression 我有相同的要求,只是我使用的是 HTTP-Client(4.1 版)。您的代码很有帮助,尽管我仍然不清楚您在哪里将 httpClient 与此注册协议绑定?
    • @united-expression 在这种情况下为什么需要一个单独的类以及 RegisterProtocol 的用途是什么? CustomHttpsSocketFactory 的方法如何调用?
    • 它显示 setEnabledProtocols(String[]); 未定义类型 javax.net.ssl.SSLSocket
    猜你喜欢
    • 2018-12-10
    • 2018-03-08
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多