【问题标题】:Java, Apache HttpClient, TLSv1.2 & OpenJDK 7Java、Apache HttpClient、TLSv1.2 和 OpenJDK 7
【发布时间】:2017-05-04 01:30:22
【问题描述】:

我们有一小群运行 OpenJDK v1.7.0_111 的 Tomcat 服务器。我们计划在今年夏天升级和迁移它们,但我们发现我们与之交互的客户端 API 在短期内将需要 TLSv1.2。我的最终愿望是找到一种配置更改来实现这一点。

托管在那里的应用程序以非常直接的方式创建它的 SSL 上下文:

SSLContext sslContext = SSLContexts.createDefault()
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);

SSLContexts 来自 Apache 的 httpclient 库(版本 4.4.1),而且它创建 SSL 上下文的方式也非常简单:

public static SSLContext createDefault() throws SSLInitializationException {
    try {
        SSLContext ex = SSLContext.getInstance("TLS");
        ex.init((KeyManager[])null, (TrustManager[])null, (SecureRandom)null);
        return ex;
    } catch (NoSuchAlgorithmException var1) {
        throw new SSLInitializationException(var1.getMessage(), var1);
    } catch (KeyManagementException var2) {
        throw new SSLInitializationException(var2.getMessage(), var2);
    }
}

深入研究SSLConnectionSocketFactory 类,它似乎只是使用SSLSocket.getEnabledProtocols() 方法来确定哪些协议可供使用。请注意,在我的情况下,this.supportedProtocols 为 null。

public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) throws IOException {
        SSLSocket sslsock = (SSLSocket)this.socketfactory.createSocket(socket, target, port, true);
        if(this.supportedProtocols != null) {
            sslsock.setEnabledProtocols(this.supportedProtocols);
        } else {
            String[] allProtocols = sslsock.getEnabledProtocols();
            ArrayList enabledProtocols = new ArrayList(allProtocols.length);
            String[] arr$ = allProtocols;
            int len$ = allProtocols.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                String protocol = arr$[i$];
                if(!protocol.startsWith("SSL")) {
                    enabledProtocols.add(protocol);
                }
            }

            if(!enabledProtocols.isEmpty()) {
                sslsock.setEnabledProtocols((String[])enabledProtocols.toArray(new String[enabledProtocols.size()]));
            }
        }

我遇到的问题是,在运行一些初步测试时,我无法让这些客户端连接到需要 TLSv1.2 的 API。

在下面的示例中,我可以通过包含 -Dhttps.protocols=TLSv1.2 参数来完成 URLConnection 代码,但我无法获得要连接的 Apache 连接。

public static void main(String[] args) throws Exception{
        String testURL = "https://testapi.com";

        SSLContext sslcontext = SSLContext.getInstance("TLS");
        sslcontext.init(null, null, null);

        try {
            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext);
            CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();

            HttpGet httpget = new HttpGet(testURL);

            CloseableHttpResponse response = client.execute(httpget);
            System.out.println("Response Code (Apache): " + response.getStatusLine().getStatusCode());
        }
        catch (Exception e){
            System.err.println("Apache HTTP Client Failed");
            e.printStackTrace();
        }

        try {
            HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(testURL).openConnection();
            urlConnection.setSSLSocketFactory(sslcontext.getSocketFactory());
            urlConnection.connect();
            System.out.println("Response Code (URLConnection): " + urlConnection.getResponseCode());
        }
        catch (Exception e){
            System.err.println("HttpsURLConnection Failed");
            e.printStackTrace();
        }

    }

除了-Dhttps.protocols=TLSv1.2,我还尝试了-Djdk.tls.client.protocols=TLSv1.2-Ddeployment.security.TLSv1.2=true JVM 参数,但没有任何运气。

是否有人想过如何在此配置中启用 TLSv1.2,而无需升级到 v8 或更改应用程序以专门请求 TLSv1.2 实例?

【问题讨论】:

  • 您是否尝试过此处显示的参数和测试blogs.oracle.com/java-platform-group/…
  • 另外,您可能希望包含一个简短的 minimal reproducible example,以便 JDK 7 上的其他用户可以重现您正在尝试的内容。
  • 您不应启用所有支持的协议,因为它们包括不安全的匿名密码套件。
  • @pvg - 现在我有了。我已经列出了我在问题中尝试过的所有 JVM 参数。我还提供了一个简短的示例。
  • 为了完整起见,在您的示例中添加的最后一件事可能是简单的 URLConnection 测试,因此您知道这不是(或现在)特定于 httpclient 的问题。

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


【解决方案1】:

jdk.tls.client.protocols 仅适用于您未使用的 Java 8(可能是 9)。

https.protocols 默认只在 httpclient 不使用的HttpsURLConnection 中有效。

deployment.* 仅适用于您未使用的 JNLP 和小程序(如果任何浏览器仍允许小程序)。

如前所述,至少对于 4.5,假设您使用 HttpClientBuilderHttpClients(您没有说)的 Q 的答案是使用 .useSystemProperties() 或 @ 987654330@,分别;这些确实使用与*URLConnection 相同的系统属性——或者至少其中许多包括https.protocols。您应该检查此集合中包含的其他属性均未配置为执行您不想要的操作。这确实需要更改应用程序,但不需要将它们更改为“专门请求 ... TLSv1.2”。

除此之外,您还可以配置 SSLConnectionSocketFactory 以指定允许的确切协议,如 the Q linked by @pvgSSLContexts.custom().useProtocol(String).build() 以指定上限 - 这对于您的情况来说已经足够了,因为提供的范围 'up to 1.2 ' 到需要 1.2 的服务器将选择 1.2。

【讨论】:

  • 如果我正确理解this OpenJDK enhancement requestjdk.tls.client.protocols 实际上应该在 OpenJDK 7u91 及更高版本和 6u115 及更高版本中可用。
【解决方案2】:

这是配置 Apache HttpClient 4.x 以使用特定 TLS/SSL 版本的推荐方法

CloseableHttpClient client = HttpClientBuilder.create()
    .setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContext.getDefault(), new String[] { "TLSv1.2" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
    .build();

投票支持 dave_thompson_085 的回答

【讨论】:

    猜你喜欢
    • 2016-05-03
    • 2015-04-09
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    相关资源
    最近更新 更多