如何强制 Commons HTTPClient 3.1 仅对 HTTPS 使用 TLS 1.2?
How to force Commons HTTPClient 3.1 to use TLS 1.2 only for HTTPS?
我想强制Apache Commons HTTP-Client(版本3.1)使用TLS 1.2 作为 HTTPS 的 仅 协议。
这是因为服务器应该升级到 TLS 1.2 并且不再接受任何旧协议(导致返回 'Connection Reset')。
对于进一步的上下文,可能无关紧要,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);
非常感谢您的帮助!
可惜没人回答;我能做到,首先你写一个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));
}
}
您的代码中需要一个套接字引用。然后你可以像这样设置启用的协议:
if (socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
}
这取决于您编写客户端的方式以及您使用的 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")。请注意,默认情况下,它将 return 一个上下文,其中包含比 TLSvx.x 启用的所有协议更少的协议。如果您已配置 "jdk.tls.client.protocols",这将 return 启用这些协议的上下文。
在代码中硬编码协议并不是一个好主意。很多时候,我们会遇到某些客户想要特定版本,因为他们使用旧服务器或在某些 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
我想强制Apache Commons HTTP-Client(版本3.1)使用TLS 1.2 作为 HTTPS 的 仅 协议。
这是因为服务器应该升级到 TLS 1.2 并且不再接受任何旧协议(导致返回 'Connection Reset')。
对于进一步的上下文,可能无关紧要,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);
非常感谢您的帮助!
可惜没人回答;我能做到,首先你写一个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));
}
}
您的代码中需要一个套接字引用。然后你可以像这样设置启用的协议:
if (socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
}
这取决于您编写客户端的方式以及您使用的 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")。请注意,默认情况下,它将 return 一个上下文,其中包含比 TLSvx.x 启用的所有协议更少的协议。如果您已配置 "jdk.tls.client.protocols",这将 return 启用这些协议的上下文。
在代码中硬编码协议并不是一个好主意。很多时候,我们会遇到某些客户想要特定版本,因为他们使用旧服务器或在某些 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