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

标签 java android ssl

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

我想通过 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 永远不会返回。通过代理机器的 wireshark 转储,我可以看到代理和服务器之间发生了一次 tcp 握手。通过 Web session 的转储,我可以看到客户端通常会在此时启动 SSL 握手,这在我的场景中不会发生。

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

编辑:

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

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

顺序如下(再次简化):

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 代理, 不通过 套接字。

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

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

关于java - 如何通过 HTTP 代理连接 SSL 套接字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27979752/

相关文章:

ssl - 如何在 Zeppelin 中设置 SSL

c# - WebRequest\HttpClient C# 中的客户端证书身份验证失败

java - 无法使用 JAVA 创建表并将表列表到远程 HBase

java - Gradle Zip 任务做多个子树?

Android - 更改 ListView 特定项目的背景颜色

java - 将联系人保存到 android 的问题 - 代码附加

java - 如何在 Java 中获取唯一的计算机标识符(如磁盘 ID 或主板 ID)?

java - Servlet 方法不重定向页面

android - android 中的谷歌地图 v2 中的多色折线

ssl - Jenkins “Publish over FTP plugin” 返回 “534 Policy requires SSL” 用于文件上传