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

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

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

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

编辑:

经过讨论,这是我正在尝试运行的代码的更完整版本。 上面的这个版本使用简单的(新的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] 

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

  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函数的套接字,而不是像我原来的例子一样动态创建一个新套接字。

java.net.Proxyhttps.proxyHost/proxyPort属性仅支持通过HttpURLConnection, HTTP代理HttpURLConnection,而不是通过Socket.

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

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

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

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

SSLSocketClientWithTunneling

我现在没有时间测试/编写目标解决方案,但是使用STARTTLS:recv failed将套接字升级到SSLSocket似乎涵盖了基本问题。

在您的情况下,您需要连接到代理,发出代理连接,然后升级连接 – 基本上您在引用的问题中取代STARTECT取代STARTTLS,并且不需要检查“670”。