【发布时间】: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