Java、Apache HttpClient、TLSv1.2 和 OpenJDK 7
Java, Apache HttpClient, TLSv1.2 & OpenJDK 7
我们有一小组 Tomcat 服务器 运行 OpenJDK v1.7.0_111。我们计划在今年夏天对它们进行升级和迁移,但我们发现与我们互动的客户 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
class 挖掘,它似乎只是使用 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 参数但没有任何运气。
有没有人想过如何在不升级到 v8 或更改应用程序以专门请求 TLSv1.2 实例的情况下在此配置中启用 TLSv1.2?
jdk.tls.client.protocols
仅适用于您未使用的 Java 8(大概是 9)。
https.protocols
仅在 httpclient 不使用的 HttpsURLConnection
默认情况下有效。
deployment.*
仅适用于您未使用的 JNLP 和小程序(如果任何浏览器仍允许小程序)。
你的问题的答案至少是 4.5,假设 你使用 HttpClientBuilder
或 HttpClients
(你没有说) ,就是分别使用.useSystemProperties()
或者.createSystem()
;这些 do 使用与 *URLConnection
相同的系统属性——或者至少其中许多包括 https.protocols
。您应该检查此集合中包含的其他属性的 none 是否配置为执行您不想要的操作。 确实需要更改应用程序,但不需要更改它们 'to specifically request ... TLSv1.2'。
除此之外,您可以配置 SSLConnectionSocketFactory
以指定在 中允许的确切协议,或配置 SSLContexts.custom().useProtocol(String).build()
以指定上限 - 这足以满足您的情况因为向需要 1.2 的服务器提供范围 'up to 1.2' 将 select 1.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 的回答
我们有一小组 Tomcat 服务器 运行 OpenJDK v1.7.0_111。我们计划在今年夏天对它们进行升级和迁移,但我们发现与我们互动的客户 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
class 挖掘,它似乎只是使用 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 参数但没有任何运气。
有没有人想过如何在不升级到 v8 或更改应用程序以专门请求 TLSv1.2 实例的情况下在此配置中启用 TLSv1.2?
jdk.tls.client.protocols
仅适用于您未使用的 Java 8(大概是 9)。
https.protocols
仅在 httpclient 不使用的 HttpsURLConnection
默认情况下有效。
deployment.*
仅适用于您未使用的 JNLP 和小程序(如果任何浏览器仍允许小程序)。
你的问题的答案至少是 4.5,假设 你使用 HttpClientBuilder
或 HttpClients
(你没有说) ,就是分别使用.useSystemProperties()
或者.createSystem()
;这些 do 使用与 *URLConnection
相同的系统属性——或者至少其中许多包括 https.protocols
。您应该检查此集合中包含的其他属性的 none 是否配置为执行您不想要的操作。 确实需要更改应用程序,但不需要更改它们 'to specifically request ... TLSv1.2'。
除此之外,您可以配置 SSLConnectionSocketFactory
以指定在 SSLContexts.custom().useProtocol(String).build()
以指定上限 - 这足以满足您的情况因为向需要 1.2 的服务器提供范围 'up to 1.2' 将 select 1.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 的回答