如何在apache HttpClient上设置TLS版本

时间:2015-02-08 07:50:57

标签: java ssl apache-httpclient-4.x

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

我在做:

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

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

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

10 个答案:

答案 0 :(得分:33)

解决方案是:

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

这需要org.apache.httpcomponents.httpclient 4.3.x尽管。

答案 1 :(得分:14)

这就是我如何使用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;

答案 2 :(得分:8)

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

答案 3 :(得分:5)

对于使用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.

答案 4 :(得分:4)

使用HttpClient 4.5.x中的HttpClientBuilder和自定义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()

没有自定义HttpClientConnectionManager

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

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

答案 5 :(得分:2)

如果您使用的是httpclient 4.2,那么您需要编写一些额外的代码。我希望能够自定义“支持TLS的协议”(例如TLSv1.1TLSv1TLSv1.2}以及cipher suites

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

然后:

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

您可以使用略少的代码执行此操作,但我主要通过自定义组件进行复制/粘贴,以便按上述方式构建对象。

答案 6 :(得分:2)

如果您的代码中有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);
}

答案 7 :(得分:0)

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

答案 8 :(得分:0)

使用-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();

使用以下JVM参数进行验证

-Djavax.net.debug=all

答案 9 :(得分:0)

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

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

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

我们现在在系统中使用此设置,因为这使我们只能为某些代码用法设置此设置。 在我们的情况下,我们仍然在运行Java 7,并且一个API端点不允许使用TLSv1, 因此我们使用java -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2启用当前的TLS版本。 感谢@jebeaudet指出了这个方向。