Подтвердить что ты не робот

Как подключить SSL-сокет через HTTP-прокси?

Я пытаюсь использовать Java (Android) для подключения к серверу с помощью сокета SSL. Обратите внимание, что это не HTTP-данные. Это проприетарный протокол с сочетанием текстовых и двоичных данных.

Я хочу передать это SSL-соединение через HTTP-прокси, но перед этим сталкиваюсь с множеством проблем. Прямо сейчас сценарий, который я использую и что мой браузер, похоже, использует с прокси-сервером squid, как следует

[client] → [http connection] → [proxy] → [ssl connection] → [server]

Это работает для браузера, потому что после того, как прокси делает соединение 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. С дампом wirehark от прокси-машины я вижу, что между прокси-сервером и сервером происходит рукопожатие tcp. С дампами веб-сессий я вижу, что клиент обычно инициирует 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]

Затем прочитайте ответ со следующим кодом:

    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, а не просто создает новый "на лету", как в моем первоначальном примере.

4b9b3361

Ответ 1

java.net.Proxy или свойства https.proxyHost/proxyPort поддерживают только HTTP-проксирование через HttpURLConnection, не через Socket.

Чтобы сделать эту работу для своего SSLSocket, все, что вам нужно, это создать сокет plaintext, выпустить на нем команду HTTP CONNECT, проверить ответ на 200 и затем обернуть его в SSLSocket.

EDIT При отправке команды CONNECT, вы не должны закрывать сокет, конечно; и при чтении ответа вы не должны использовать BufferedReader,, иначе вы потеряете данные; либо прочитайте строку вручную, либо используйте DataInputStream.readLine(),, несмотря на ее устаревание. Вам также необходимо полностью выполнить RFC 2616.

Ответ 2

Вы должны использовать javax.net lib. вы можете архивировать свою цель с помощью javax.net.ssl. *.

Я думаю, что вы можете получить решение, используя документы oracle. Вот ссылка для этого.

SSLSocketClientWithTunneling

Ответ 3

У меня нет времени тестировать/записывать целевое решение, однако Обновление сокета до SSLSocket с помощью STARTTLS: отказ recv, похоже, оспаривает основную проблему.

В вашем случае вам нужно подключиться к прокси, выпустить прокси-соединение, а затем обновить соединение - по сути, вы CONNECT заменяете STARTTLS в указанном вопросе, и проверка на "670" не требуется.