是否可以删除 Java 中 SSL 握手的扩展部分?

Is it possible to remove the extension part of SSL handshakes in Java?

我正尝试使用 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)

是否可以让Java发送数据包的"extension"部分?

如果有人想知道答案,您需要为 https.protocols 系统 属性 使用 SSLv2Hello。

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 client hello 的扩展部分是否被删除作为将 client 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 中得到的:

如果我注释掉 SSLv2Client 部分,我会得到:

很明显,这实现了我正在寻找的目标:删除 SSL 握手的扩展部分。也许这不是最好的方法,正如@EJP 提到的那样引入了很多安全漏洞(我不关心这里,因为我的 CISCO IPS 服务器是本地的,我只是想连接到它,安全与否),但我不能找到任何其他方法来做到这一点。