如何通过HTTP代理连接SSL套接字?

Eri*_*ric 13 java ssl android

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

我想通过HTTP代理中继SSL连接,但我遇到了很多问题.现在我使用的场景以及我的浏览器似乎与squid代理一起使用的场景如下

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

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

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);
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是createSocket NEVER RETURNS.通过代理机器的wireshark转储,我可以看到代理和服务器之间发生了tcp握手.通过Web会话转储,我可以看到客户端通常会在此时启动SSL握手,这在我的方案中不会发生.

这对于信任管理器来说不是问题,因为证书永远不会回复给我并且永远不会被验证.

编辑:

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

我试图调试的代码的原始版本抛出
java.net.ConnectException: failed to connect to /192.168.1.100 (port 443): connect failed: ETIMEDOUT (Connection timed out)

顺序如下(再次简化):

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]
Run Code Online (Sandbox Code Playgroud)

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

    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 )
    {
    }
Run Code Online (Sandbox Code Playgroud)

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

use*_*421 15

java.net.Proxy或者https.proxyHost/proxyPort属性,仅HttpURLConnection,通过a来支持HTTP代理Socket.

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

编辑发送CONNECT命令时,当然不能关闭套接字; 在阅读回复时,你不能使用BufferedReader,否则会丢失数据; 要么手工阅读,要么DataInputStream.readLine(),尽管不赞成使用.您还需要完全遵循RFC 2616.

  • 当您使用接受代理的 Socket 构造函数时,Java 8 支持 HTTP 隧道代理:`new Socket(new Proxy(Proxy.Type.HTTP, socketAddress))` (2认同)

小智 6

你必须使用javax.net lib.您可以使用javax.net.ssl.*归档到目标.

我想你可以使用oracle docs获得解决方案.这是链接.

SSLSocketClientWithTunneling

  • 该链接出现轻微错误.其中一个请求行以`\n结尾,``应该是`\ r \n (2认同)