我尝试使用 https 连接到一个旧版 Cisco IPS 服务器。问题是该服务器仅接受特定条件下的握手:
版本必须为 TLSv1.0,密码套件必须为 SSL_RSA_WITH_RC4_128_MD5 或 SSL_RSA_WITH_RC4_128_SHA,且不得有任何扩展名。
我实现了一个手工制作的“ClientHello”,它将以下信息作为握手发送(wireshark 输出):
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 45
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 41
Version: TLS 1.0 (0x0301)
Random
Session ID Length: 0
Cipher Suites Length: 2
Cipher Suites (1 suite)
Compression Methods Length: 1
Compression Methods (1 method)
服务器发回ServerHello消息。
现在我想使用 Java 的 SSL 实现来发送“完全相同”的 ClientHello。代码如下:
System.setProperty("https.protocols", "TLSv1");
System.setProperty("javax.net.debug", "ssl:handshake");
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) factory.createSocket("ips-server", 443);
socket.setEnabledProtocols(new String[] {"TLSv1"});
socket.setEnabledCipherSuites(new String[] {"SSL_RSA_WITH_RC4_128_MD5"});
socket.startHandshake();
产生以下握手:
Secure Sockets Layer
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 52
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 48
Version: TLS 1.0 (0x0301)
Random
Session ID Length: 0
Cipher Suites Length: 2
Cipher Suites (1 suite)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 5
Extension: renegotiation_info
Type: renegotiation_info (0xff01)
Length: 1
Renegotiation Info extension
这会导致服务器发回以下数据包:
TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Handshake Failure)
是否可以让Javanot
发送数据包的“扩展”部分?
System.setProperty("https.protocols", "TLSv1,SSLv2Hello");
我使用以下代码连接到服务器:
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
} }, new SecureRandom());
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
});
更多解释 -
@EJP 提到这段代码没有回答标题中的问题。我编写了一个小测试来显示 SSL 客户端 hello 的扩展部分是否被删除,作为将客户端 hello 更改为 SSLv2 的副作用。
public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, MalformedURLException, IOException {
System.setProperty("https.protocols", "TLSv1,SSLv2Hello");
String url = "https://www.google.com";
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}}, new SecureRandom());
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
});
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
Stream<String> lines = br.lines();
lines.forEach(l -> { System.out.println(l);});
}
如果我运行这个程序,这就是我在wireshark中得到的结果:
很明显,这实现了我一直在寻找的东西:删除 SSL 握手的扩展部分。也许这不是最好的方法,正如 @EJP 提到的,引入了很多安全漏洞(这里不是我关心的,因为我的 CISCO IPS 服务器是本地的,我只想连接到它,无论安全与否),但我不能找到任何其他方法来做到这一点。
自从
“配置默认扩展”了解更多详细信息。