如何通过 HTTP 代理连接 SSL 套接字? [英] How to connect a SSL socket through a HTTP proxy?

查看:56
本文介绍了如何通过 HTTP 代理连接 SSL 套接字?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Java (Android) 通过 SSL 套接字连接到服务器.请注意,这不是 HTTP 数据.这是混合了文本和二进制数据的专有协议.

I'm trying to use Java (Android) to connect to a server with a SSL socket. Please note that this is not HTTP data. This is proprietary protocol with a mix of text and binary data.

我想通过 HTTP 代理中继该 SSL 连接,但我面临着很多问题.现在我使用的场景以及我的浏览器似乎与鱿鱼代理一起使用的场景如下

I want to relay that SSL connection through a HTTP proxy, but I am facing a lot of problems with that. Right now the scenario that I use and that my browser seems to use with a squid proxy is as follow

[客户端]->[http 连接]->[代理]->[ssl 连接]->[服务器]

[client]->[http connection]->[proxy]->[ssl connection]->[server]

这适用于浏览器,因为在代理建立 ssl 连接后,TLS 协商立即发生.但是我的代码似乎没有这样做.

This works for the browser, because after the proxy makes the ssl connection, a TLS negotiation takes place immediately. However my code does not seem to do that.

final TrustManager[] trustManager = new TrustManager[] { new MyX509TrustManager() };
final SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManager, null);
SSLSocketFactory factory = context.getSocketFactory();
Socket s = factory.createSocket(new Socket(proxy_ip, 3128), hostName, port, true);

我遇到的问题是 createSocket 永远不会返回.使用来自代理机器的 wireshark 转储,我可以看到代理和服务器之间发生了 tcp 握手.通过 Web 会话的转储,我可以看到客户端通常会在此时发起 SSL 握手,这在我的场景中不会发生.

The problem that I have is that createSocket NEVER RETURNS. With a wireshark dump from the proxy machine, I can see that a tcp handshake takes place between the proxy and the server. With dumps from web sessions, I can see that the client usually initiate a SSL handshake at this point, which does not happen in my scenario.

这不是信任管理器的问题,因为证书永远不会返回给我,也永远不会被验证.

This is not a problem with the trust manager, because the certificate never gets back to me and it is never validated.

经过讨论,这是我尝试运行的代码的更完整版本.上面这个以简单的 (new Socket(...)) 作为参数的版本是我后来尝试过的.

After discussion, this is the more complete version of the code I'm trying to run. This version above with the simple (new Socket(...)) as parameter is something I've tried later on.

我试图调试的原始版本代码抛出
java.net.ConnectException:无法连接到/192.168.1.100(端口 443):连接失败:ETIMEDOUT(连接超时)

The original version of the code I'm trying to debug throws
java.net.ConnectException: failed to connect to /192.168.1.100 (port 443): connect failed: ETIMEDOUT (Connection timed out)

顺序如下(再次简化了一点):

The sequence is as follow (a bit simplified again) :

final Socket proxySocket = new Socket();
proxySocket.connect(proxyAddress, 2000); // 2 seconds as the connection timeout for connecting to the proxy server 
[Start a thread and write to outputStream=socket.getOutputStream()]
final String proxyRequest = String.format("CONNECT %s:%d HTTP/1.1\r\nProxy-Connection: keep-alive\r\nConnection: keep-alive\r\nHost: %s:%d\r\n\r\n", hostName, port, hostName, port);
outputStream.close(); // Closing or not doesn't change anything
[Stop using that thread and let it exit by reaching the end of its main function]

然后使用以下代码读取响应:

Then read the response with the following code :

    final InputStreamReader isr = new InputStreamReader(proxySocket.getInputStream());
    final BufferedReader br = new BufferedReader(isr);
    final String statusLine = br.readLine();

    boolean proxyConnectSuccess = false;
    // readLine consumed the CRLF
    final Pattern statusLinePattern = Pattern.compile("^HTTP/\\d+\\.\\d+ (\\d\\d\\d) .*");
    final Matcher statusLineMatcher = statusLinePattern.matcher(statusLine);
    if (statusLineMatcher.matches())
    {
        final String statusCode = statusLineMatcher.group(1);
        if (null != statusCode && 0 < statusCode.length() && '2' == statusCode.charAt(0))
        {
            proxyConnectSuccess = true;
        }
    }

    // Consume rest of proxy response
    String line;
    while ( "".equals((line = br.readLine())) == false )
    {
    }

我可以说这段代码有效,因为它在没有 SSL 的情况下有效.此处创建的套接字 proxySocket 是传递给 createSocket 函数的套接字,而不是像我原来的示例那样动态创建一个新套接字.

I can say that this code works because it works without SSL. The socket created here, proxySocket is the one that is passed to the createSocket function instead of just creating a new one on the fly like in my original example.

推荐答案

java.net.Proxy,或者 https.proxyHost/proxyPort 属性,只支持 HTTP 代理通过 HttpURLConnection, 而不是通过 Socket.

java.net.Proxy, or the https.proxyHost/proxyPort properties, only support HTTP proxying via HttpURLConnection, not via a Socket.

要使其适用于您自己的 SSLSocket,您只需创建一个纯文本套接字,在其上发出 HTTP CONNECT 命令,检查响应为 200,然后将其包装在 SSLSocket.

To make that work for an SSLSocket of your own, all you need to to is create a plaintext socket, issue an HTTP CONNECT command on it, check the response for 200, and then wrap it in an SSLSocket.

EDIT 发送CONNECT命令时,当然不能关闭socket;并且在阅读它的回复时你不能使用 BufferedReader, 否则你会丢失数据;要么手动读取该行,要么使用 DataInputStream.readLine(), 尽管它已被弃用.您还需要完全遵循 RFC 2616.

EDIT When sending the CONNECT command, you must not close the socket, of course; and when reading its reply you must not use a BufferedReader, otherwise you will lose data; either read the line by hand or use DataInputStream.readLine(), despite its deprecation. You also need to follow RFC 2616 entirely.

这篇关于如何通过 HTTP 代理连接 SSL 套接字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆