如何使用具有 480 位密钥长度的 Diffie-Hellman 获取 SSL 页面?
How can I fetch an SSL page using Diffie-Hellman with a 480-bit key length?
我正在尝试获取使用 480 位密钥长度的 DH 的页面,但我无法使用 Apache HTTP 客户端执行此操作。如果我将异步 HTTP 客户端与 JDK 提供程序一起使用,我可以成功获取页面。
我知道 JDK 对最大大小有限制,但这个问题似乎是相反的。密钥大小太小,不是 64 的倍数。
我写了一个 SSCCE 来说明这种行为,经过两天的寻找,我已经无计可施了。
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.junit.Assert;
import org.junit.Test;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.providers.jdk.JDKAsyncHttpProvider;
public class DH480Test extends Assert {
private final String URL = "https://dh480.badssl.com/";
@Test
public void testNingHTTPClientWithJDKProvider() {
AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build();
AsyncHttpClient client = new AsyncHttpClient(new JDKAsyncHttpProvider(config), config);
try {
client.prepareGet(URL).execute().get();
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
@Test
public void testApacheHTTClient() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, null, null);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", new SSLConnectionSocketFactory(sslContext))
.register("http", new PlainConnectionSocketFactory())
.build();
HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(ccm);
HttpClient client = builder.build();
try {
HttpGet get = new HttpGet(URL);
HttpResponse r = client.execute(get);
EntityUtils.consume(r.getEntity());
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
}
这是失败测试的堆栈跟踪。
javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1906)
at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1889)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1410)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
at org.apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.java:318)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
at DH480Test.testApacheHTTClient(DH480Test.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.RuntimeException: Could not generate DH keypair
at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:142)
at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:114)
at sun.security.ssl.ClientHandshaker.serverKeyExchange(ClientHandshaker.java:708)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:268)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
... 41 more
Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 2048 (inclusive)
at com.sun.crypto.provider.DHKeyPairGenerator.initialize(DHKeyPairGenerator.java:120)
at java.security.KeyPairGenerator$Delegate.initialize(KeyPairGenerator.java:674)
at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:128)
... 49 more
java.lang.AssertionError
...
这是另一个独立测试,展示了我能够使用默认 JDK 网络库获取页面。这是异步 http 客户端使用的底层网络实现。这是我的 JVM 详细信息
➜ ~ java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
@Test
public void testHTTPURLConnection() throws IOException {
try {
java.net.URL url = new URL(URL);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setHostnameVerifier((s, sslSession) -> true);
connection.setRequestMethod("GET");
connection.setReadTimeout(15 * 1000);
connection.connect();
System.out.println(IOUtils.toString(connection.getInputStream()));
} catch (Exception e) {
fail();
}
}
我不是专家,也从未在 java 中使用过 DH,但是在查看了您的日志并对 DHKeyPairGenerator Grepcode 中的代码进行了一些探索之后,initialize
函数,如果 Prime size < 512
它总是抛出异常,因为它是用代码编写的,
83 if ((keysize < 512) || (keysize > 1024) || (keysize % 64 != 0)) {
84 throw new InvalidParameterException("Keysize must be multiple "
85 + "of 64, and can only range "
86 + "from 512 to 1024 "
87 + "(inclusive)");
88 }
和
118 if ((pSize < 512) || (pSize > 1024) ||
119 (pSize % 64 != 0)) {
120 throw new InvalidAlgorithmParameterException
121 ("Prime size must be multiple of 64, and can only range "
122 + "from 512 to 1024 (inclusive)");
123 }
因此可以肯定的是,您不能将这些标准库用于 480 位密钥长度。
它可能在您需要探索的其他一些库中可用,我们使用 bouncycastle 库来做一些可能具有这些功能的加密内容。
我几乎没有研究并在 bouncycastle 包中发现了相同的 DHKeyPairGenerator class,没有约束和检查密钥大小。
免责声明:我是 AsyncHttpClient 的维护者(请不要称它为 Ning)。
有趣的事实:当 运行 JDK8 时,这确实适用于 AHC 的 JDK 提供程序,但不适用于 JDK7!
现在:AHC JDK 提供程序(它只是一个玩具,在 AHC2 中即将消失,您不应该使用它)归结为 Http(s)UrlConnection,因此它有望获得相同的结果结果。
然后,基于 NIO 的客户端(例如其他 AHC 提供程序)和 Apache HttpComponents 无法使用 HttpsUrlConnection 使用的底层 HTTPS 组件,因为它们不是 public!
所以他们不得不直接使用javax.net.ssl.SSLEngine
。但是,此 API 适用于通用 TLS,不适用于构建在其之上的协议,例如 HTTPS。
例如,在 JDK7 之前,这会对所有基于 Java NIO 的 HTTP 客户端造成巨大的安全问题,因为它们无法正确执行主机名验证。主机名验证特定于 HTTPS,因此您可以将 HostnameVerifier 传递给 HttpsUrlConnection(特定于 HTTPS),但不能传递给 SSLEngine(通用 TLS)。
现在,自 JDK7 以来,我们终于可以在 SSLEngine 上配置协议(HTTPS 或 LDAP),以便它处理 HTTPS 的特定行为。但是,与使用 HttpsUrlConnection 相比,您的控制权仍然较少。
在这里,您似乎发现了一个 JDK 错误,并且启用了 HTTPS 协议的 HttpsUrlConnection 和 SSLEngine 的行为方式不同。
例如,无论我做什么(无论我启用什么密码套件),我都无法获得 BadSSL.com 服务器选择 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
,这是 HttpsUrlConnection 选择的一个。它总是选择 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
代替(没有椭圆曲线)。启用 TLS 调试时请参阅 ServerHello (-Djavax.net.debug=all
)。
我正在尝试获取使用 480 位密钥长度的 DH 的页面,但我无法使用 Apache HTTP 客户端执行此操作。如果我将异步 HTTP 客户端与 JDK 提供程序一起使用,我可以成功获取页面。
我知道 JDK 对最大大小有限制,但这个问题似乎是相反的。密钥大小太小,不是 64 的倍数。
我写了一个 SSCCE 来说明这种行为,经过两天的寻找,我已经无计可施了。
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.junit.Assert;
import org.junit.Test;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.providers.jdk.JDKAsyncHttpProvider;
public class DH480Test extends Assert {
private final String URL = "https://dh480.badssl.com/";
@Test
public void testNingHTTPClientWithJDKProvider() {
AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build();
AsyncHttpClient client = new AsyncHttpClient(new JDKAsyncHttpProvider(config), config);
try {
client.prepareGet(URL).execute().get();
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
@Test
public void testApacheHTTClient() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, null, null);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", new SSLConnectionSocketFactory(sslContext))
.register("http", new PlainConnectionSocketFactory())
.build();
HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(ccm);
HttpClient client = builder.build();
try {
HttpGet get = new HttpGet(URL);
HttpResponse r = client.execute(get);
EntityUtils.consume(r.getEntity());
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
}
这是失败测试的堆栈跟踪。
javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1906)
at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1889)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1410)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
at org.apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.java:318)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
at DH480Test.testApacheHTTClient(DH480Test.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.RuntimeException: Could not generate DH keypair
at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:142)
at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:114)
at sun.security.ssl.ClientHandshaker.serverKeyExchange(ClientHandshaker.java:708)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:268)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
... 41 more
Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 2048 (inclusive)
at com.sun.crypto.provider.DHKeyPairGenerator.initialize(DHKeyPairGenerator.java:120)
at java.security.KeyPairGenerator$Delegate.initialize(KeyPairGenerator.java:674)
at sun.security.ssl.DHCrypt.<init>(DHCrypt.java:128)
... 49 more
java.lang.AssertionError
...
这是另一个独立测试,展示了我能够使用默认 JDK 网络库获取页面。这是异步 http 客户端使用的底层网络实现。这是我的 JVM 详细信息
➜ ~ java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
@Test
public void testHTTPURLConnection() throws IOException {
try {
java.net.URL url = new URL(URL);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setHostnameVerifier((s, sslSession) -> true);
connection.setRequestMethod("GET");
connection.setReadTimeout(15 * 1000);
connection.connect();
System.out.println(IOUtils.toString(connection.getInputStream()));
} catch (Exception e) {
fail();
}
}
我不是专家,也从未在 java 中使用过 DH,但是在查看了您的日志并对 DHKeyPairGenerator Grepcode 中的代码进行了一些探索之后,initialize
函数,如果 Prime size < 512
它总是抛出异常,因为它是用代码编写的,
83 if ((keysize < 512) || (keysize > 1024) || (keysize % 64 != 0)) {
84 throw new InvalidParameterException("Keysize must be multiple "
85 + "of 64, and can only range "
86 + "from 512 to 1024 "
87 + "(inclusive)");
88 }
和
118 if ((pSize < 512) || (pSize > 1024) ||
119 (pSize % 64 != 0)) {
120 throw new InvalidAlgorithmParameterException
121 ("Prime size must be multiple of 64, and can only range "
122 + "from 512 to 1024 (inclusive)");
123 }
因此可以肯定的是,您不能将这些标准库用于 480 位密钥长度。
它可能在您需要探索的其他一些库中可用,我们使用 bouncycastle 库来做一些可能具有这些功能的加密内容。
我几乎没有研究并在 bouncycastle 包中发现了相同的 DHKeyPairGenerator class,没有约束和检查密钥大小。
免责声明:我是 AsyncHttpClient 的维护者(请不要称它为 Ning)。
有趣的事实:当 运行 JDK8 时,这确实适用于 AHC 的 JDK 提供程序,但不适用于 JDK7!
现在:AHC JDK 提供程序(它只是一个玩具,在 AHC2 中即将消失,您不应该使用它)归结为 Http(s)UrlConnection,因此它有望获得相同的结果结果。
然后,基于 NIO 的客户端(例如其他 AHC 提供程序)和 Apache HttpComponents 无法使用 HttpsUrlConnection 使用的底层 HTTPS 组件,因为它们不是 public!
所以他们不得不直接使用javax.net.ssl.SSLEngine
。但是,此 API 适用于通用 TLS,不适用于构建在其之上的协议,例如 HTTPS。
例如,在 JDK7 之前,这会对所有基于 Java NIO 的 HTTP 客户端造成巨大的安全问题,因为它们无法正确执行主机名验证。主机名验证特定于 HTTPS,因此您可以将 HostnameVerifier 传递给 HttpsUrlConnection(特定于 HTTPS),但不能传递给 SSLEngine(通用 TLS)。
现在,自 JDK7 以来,我们终于可以在 SSLEngine 上配置协议(HTTPS 或 LDAP),以便它处理 HTTPS 的特定行为。但是,与使用 HttpsUrlConnection 相比,您的控制权仍然较少。
在这里,您似乎发现了一个 JDK 错误,并且启用了 HTTPS 协议的 HttpsUrlConnection 和 SSLEngine 的行为方式不同。
例如,无论我做什么(无论我启用什么密码套件),我都无法获得 BadSSL.com 服务器选择 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
,这是 HttpsUrlConnection 选择的一个。它总是选择 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
代替(没有椭圆曲线)。启用 TLS 调试时请参阅 ServerHello (-Djavax.net.debug=all
)。