Java 如何强制 Commons HTTPClient 3.1 仅将 TLS 1.2 用于 HTTPS?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32587141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:48:28  来源:igfitidea点击:

How to force Commons HTTPClient 3.1 to use TLS 1.2 only for HTTPS?

javasslhttpsapache-commons-httpclient

提问by abdelrahman-sinno

I wish to force Apache Commons HTTP-Client(version 3.1) to use TLS 1.2as the onlyprotocol for HTTPS.

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

This is due to the server supposedly being upgraded to TLS 1.2 and not accepting any older protocol anymore (causing 'Connection Reset' to be returned).

这是因为服务器应该升级到 TLS 1.2 并且不再接受任何旧协议(导致返回“连接重置”)。

For further context, probably irrelevant, the HTTP-Client is used along with Axis2 to make a SOAP; some of the code used for setting up the HttpClient is below:

对于进一步的上下文,可能无关紧要,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);

Thanks a lot for the help!

非常感谢您的帮助!

采纳答案by abdelrahman-sinno

Too bad nobody answered; I was able to do it, first you write a CustomHttpSocketFactory, then you do:

可惜没人回答;我能够做到,首先你写一个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); 

A sample custom socket factory code is found here, but instead I did:

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

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));
   }

}

回答by IgorGanapolsky

You need a Socket reference in your code. Then you can set enabled protocols on it like this:

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

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

回答by Rajesh

It depends on how you are writing your clients and what JRE versions you are using:

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

If you are using JRE8 (unless you have replaced the default SunJSSE that comes with JRE8), there is a system property "jdk.tls.client.protocols". By default, whatever you mention here will be used for all client communication.

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

If you are using HttpsURLConnection object for client connection, u can use the system property "https.protocols". This will work for all JRE versions, not just JRE8.

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

If you don't specify anything, for TLS clients, in JRE8, TLSv1, v1.1 and v1.2 are enabled, so it will work with a server what supports any one of this versions. However in JRE7 by default TLSv1 alone is enabled.

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

In your code u can always override the default or what u pass through the system properties. What u set in the code will take higher precedence. To override in the code...

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

1) If you are using raw socket and SSLEngine, u can set the protocol and ciphers in the SSLEngine (sslEngine.setEnabledProtocols(..)

1) 如果您使用原始套接字和 SSLEngine,您可以在 SSLEngine (sslEngine.setEnabledProtocols(..)

2) If you are using SSLSocket, u can set the protocol and ciphers in the SSLSocket (sslSocket.setEnabledProtocols(..)

2) 如果您使用的是 SSLSocket,您可以在 SSLSocket (sslSocket.setEnabledProtocols(..)

You can also get an SSLContext with the required protocol enabled and use that for whatever SSL components you use. SSLContext.getInstance("TLSvx.x"). Note that by default it will return a context with all the protocols lesser that TLSvx.x enabled. If u have configured "jdk.tls.client.protocols", this will return a context with those protocols enabled.

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

It would not be a good idea to hard coded the protocols in the code. Quite often, we will encounter certain customers want specific version either because they use old servers or some serious vulnerabilities are encountered in some TLS versions. Either set it through the system properties or even if you are explicitly setting in sslsocket or sslengine, read that from some conf file.

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

Also refer:

另请参考:

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

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

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