Java 如何在 apache HttpClient 上设置 TLS 版本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28391798/
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 06:09:02  来源:igfitidea点击:

How to set TLS version on apache HttpClient

javasslapache-httpclient-4.x

提问by Oliveira

How can I change the supported TLS versions on my HttpClient?

如何更改 HttpClient 上支持的 TLS 版本?

I'm doing:

我正在做:

SSLContext sslContext = SSLContext.getInstance("TLSv1.1");
sslContext.init(
    keymanagers.toArray(new KeyManager[keymanagers.size()]),
    null,
    null);

SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext, new String[]{"TLSv1.1"}, null, null);
Scheme scheme = new Scheme("https", 443, socketFactory);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(scheme);
BasicClientConnectionManager cm = new BasicClientConnectionManager(schemeRegistry);
httpClient = new DefaultHttpClient(cm);

But when I check the created socket, it still says the supported protocols are TLSv1.0, TLSv1.1 and TLSv1.2.

但是当我检查创建的套接字时,它仍然说支持的协议是 TLSv1.0、TLSv1.1 和 TLSv1.2。

In reality I just want it to stop using TLSv1.2, for this specific HttpClient.

实际上,我只是希望它停止为这个特定的 HttpClient 使用 TLSv1.2。

采纳答案by Oliveira

The solution is:

解决办法是:

SSLContext sslContext = SSLContexts.custom()
    .useTLS()
    .build();

SSLConnectionSocketFactory f = new SSLConnectionSocketFactory(
    sslContext,
    new String[]{"TLSv1", "TLSv1.1"},   
    null,
    BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

httpClient = HttpClients.custom()
    .setSSLSocketFactory(f)
    .build();

This requires org.apache.httpcomponents.httpclient 4.3.x though.

不过,这需要 org.apache.httpcomponents.httpclient 4.3.x。

回答by douglaslps

This is how I got it working on httpClient 4.5 (as per Olive Tree request):

这就是我在 httpClient 4.5 上工作的方式(根据 Olive Tree 请求):

CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
        new AuthScope(AuthScope.ANY_HOST, 443),
        new UsernamePasswordCredentials(this.user, this.password));

SSLContext sslContext = SSLContexts.createDefault();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
        new String[]{"TLSv1", "TLSv1.1"},
        null,
        new NoopHostnameVerifier());

CloseableHttpClient httpclient = HttpClients.custom()
        .setDefaultCredentialsProvider(credsProvider)
        .setSSLSocketFactory(sslsf)
        .build();

return httpclient;

回答by Christopher Schultz

If you are using httpclient 4.2, then you need to write a small bit of extra code. I wanted to be able to customize both the "TLS enabled protocols" (e.g. TLSv1.1specifically, and neither TLSv1nor TLSv1.2) as well as the cipher suites.

如果您使用的是 httpclient 4.2,那么您需要编写一些额外的代码。我希望能够自定义“启用 TLS 的协议”(例如,TLSv1.1特别是,既不是TLSv1也不是TLSv1.2)以及密码套件

public class CustomizedSSLSocketFactory
    extends SSLSocketFactory
{
    private String[] _tlsProtocols;
    private String[] _tlsCipherSuites;

    public CustomizedSSLSocketFactory(SSLContext sslContext,
                                      X509HostnameVerifier hostnameVerifier,
                                      String[] tlsProtocols,
                                      String[] cipherSuites)
    {
        super(sslContext, hostnameVerifier);

        if(null != tlsProtocols)
            _tlsProtocols = tlsProtocols;
        if(null != cipherSuites)
            _tlsCipherSuites = cipherSuites;
    }

    @Override
    protected void prepareSocket(SSLSocket socket)
    {
        // Enforce client-specified protocols or cipher suites
        if(null != _tlsProtocols)
            socket.setEnabledProtocols(_tlsProtocols);

        if(null != _tlsCipherSuites)
            socket.setEnabledCipherSuites(_tlsCipherSuites);
    }
}

Then:

然后:

    SSLContext sslContext = SSLContext.getInstance("TLS");

    sslContext.init(null, getTrustManagers(), new SecureRandom());

    // NOTE: not javax.net.SSLSocketFactory
    SSLSocketFactory sf = new CustomizedSSLSocketFactory(sslContext,
                                                         null,
                                                         [TLS protocols],
                                                         [TLS cipher suites]);

    Scheme httpsScheme = new Scheme("https", 443, sf);
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(httpsScheme);

    ConnectionManager cm = new BasicClientConnectionManager(schemeRegistry);

    HttpClient client = new DefaultHttpClient(cmgr);
    ...

You may be able to do this with slightly less code, but I mostly copy/pasted from a custom component where it made sense to build-up the objects in the way shown above.

您可能可以使用稍微少一些的代码来完成此操作,但我主要是从自定义组件复制/粘贴,其中以上面显示的方式构建对象是有意义的。

回答by IgorGanapolsky

If you have a javax.net.ssl.SSLSocketclass reference in your code, you can set the enabled TLS protocols by a call to SSLSocket.setEnabledProtocols():

如果您的代码中有javax.net.ssl.SSLSocket类引用,您可以通过调用SSLSocket.setEnabledProtocols()来设置启用的 TLS 协议:

import javax.net.ssl.*;
import java.net.*; 
...
Socket socket = SSLSocketFactory.getDefault().createSocket();
...
if (socket instanceof SSLSocket) {
   // "TLSv1.0" gives IllegalArgumentException in Java 8
   String[] protos = {"TLSv1.2", "TLSv1.1"}
   ((SSLSocket)socket).setEnabledProtocols(protos);
}

回答by sfxm

HttpClient-4.5,Use TLSv1.2 ,You must code like this:

HttpClient-4.5,使用 TLSv1.2 ,你必须这样编码:

 //Set the https use TLSv1.2
private static Registry<ConnectionSocketFactory> getRegistry() throws KeyManagementException, NoSuchAlgorithmException {
    SSLContext sslContext = SSLContexts.custom().build();
    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
            new String[]{"TLSv1.2"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    return RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.getSocketFactory())
            .register("https", sslConnectionSocketFactory)
            .build();
}

public static void main(String... args) {
    try {
        //Set the https use TLSv1.2
        PoolingHttpClientConnectionManager clientConnectionManager = new PoolingHttpClientConnectionManager(getRegistry());
        clientConnectionManager.setMaxTotal(100);
        clientConnectionManager.setDefaultMaxPerRoute(20);
        HttpClient client = HttpClients.custom().setConnectionManager(clientConnectionManager).build();
        //Then you can do : client.execute(HttpGet or HttpPost);
    } catch (KeyManagementException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}

回答by user2122524

For HttpClient-4.1 using TLSv1.2, code would go something like this:

对于使用 TLSv1.2 的 HttpClient-4.1,代码将如下所示:

        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, null, new SecureRandom());
        SSLSocketFactory sf = new SSLSocketFactory(sslContext);
        Scheme httpsScheme = new Scheme("https", 443, sf);
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(httpsScheme);
        ClientConnectionManager cm =  new        SingleClientConnManager(schemeRegistry);
        HttpClient client = new DefaultHttpClient(cm);

       // Use client to make the connection and get the results.

回答by Sachin Kumar

You could just specify the following property -Dhttps.protocols=TLSv1.1,TLSv1.2 at your server which configures the JVM to specify which TLS protocol version should be used during all https connections from client.

您可以在您的服务器上指定以下属性 -Dhttps.protocols=TLSv1.1,TLSv1.2,它配置 JVM 以指定在来自客户端的所有 https 连接期间应使用哪个 TLS 协议版本。

回答by jebeaudet

Using the HttpClientBuilderin HttpClient 4.5.x with a custom HttpClientConnectionManagerwith the defaults of HttpClientBuilder:

使用HttpClientBuilder中的HttpClient 4.5.x有一个自定义HttpClientConnectionManager使用缺省值HttpClientBuilder

SSLConnectionSocketFactory sslConnectionSocketFactory = 
    new SSLConnectionSocketFactory(SSLContexts.createDefault(),          
                                   new String[] { "TLSv1.2" },                                            
                                   null, 
           SSLConnectionSocketFactory.getDefaultHostnameVerifier());

PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
    new PoolingHttpClientConnectionManager(
        RegistryBuilder.<ConnectionSocketFactory> create()
                       .register("http",
                                 PlainConnectionSocketFactory.getSocketFactory())
                       .register("https",
                                 sslConnectionSocketFactory)
                       .build());

// Customize the connection pool

CloseableHttpClient httpClient = HttpClientBuilder.create()
                                                  .setConnectionManager(poolingHttpClientConnectionManager)
                                                  .build()

Without a custom HttpClientConnectionManager:

没有自定义HttpClientConnectionManager

SSLConnectionSocketFactory sslConnectionSocketFactory = 
    new SSLConnectionSocketFactory(SSLContexts.createDefault(),          
                                   new String[] { "TLSv1.2" },                                            
                                   null, 
           SSLConnectionSocketFactory.getDefaultHostnameVerifier());

CloseableHttpClient httpClient = HttpClientBuilder.create()
                                                  .setSSLSocketFactory(sslConnectionSocketFactory)
                                                  .build()

回答by duvo

Using -Dhttps.protocols=TLSv1.2 JVM argument didn't work for me. What worked is the following code

使用 -Dhttps.protocols=TLSv1.2 JVM 参数对我不起作用。有效的是以下代码

RequestConfig.Builder requestBuilder = RequestConfig.custom();
//other configuration, for example
requestBuilder = requestBuilder.setConnectTimeout(1000);

SSLContext sslContext = SSLContextBuilder.create().useProtocol("TLSv1.2").build();

HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultRequestConfig(requestBuilder.build());
builder.setProxy(new HttpHost("your.proxy.com", 3333)); //if you have proxy
builder.setSSLContext(sslContext);

HttpClient client = builder.build();

Use the following JVM argument to verify

使用以下 JVM 参数来验证

-Djavax.net.debug=all

回答by JonnyJD

Since this only came up hidden in comments, difficult to find as a solution:

由于这只是隐藏在评论中,因此很难找到解决方案:

You canuse java -Dhttps.protocols=TLSv1,TLSv1.1, but you need to use also useSystemProperties()

可以使用java -Dhttps.protocols=TLSv1,TLSv1.1,但您还需要使用useSystemProperties()

client = HttpClientBuilder.create().useSystemProperties();

We use this setup in our system now as this enables us to set this only for some usage of the code. In our case we still have some Java 7 running and one API end point disallowed TLSv1, so we use java -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2to enable current TLS versions. Thanks @jebeaudet for pointing in this direction.

我们现在在我们的系统中使用此设置,因为这使我们能够仅针对代码的某些用途进行设置。在我们的例子中,我们仍然有一些 Java 7 正在运行,并且一个 API 端点不允许使用 TLSv1,因此我们使用java -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2来启用当前的 TLS 版本。感谢@jebeaudet 指出这个方向。