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

Solutions Collecting From Web of "Как подключить SSL-сокет через HTTP-прокси?"

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

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

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

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

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

SSLSocketClientWithTunneling

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

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