java - 우회 - HTTP 프록시를 통해 SSL 소켓을 연결하는 방법은 무엇입니까?




안드로이드 trust anchor for certification path not found (2)

SSL 소켓이있는 서버에 Java (Android)를 사용하여 연결하려고합니다. 이것은 HTTP 데이터가 아닙니다. 이것은 텍스트와 바이너리 데이터가 혼합 된 독점적 인 프로토콜입니다.

나는 HTTP 프록시를 통해 SSL 연결을 중계하고 싶지만, 나는 그것에 많은 문제가있다. 지금 내가 사용하는 시나리오와 브라우저가 오징어 프록시와 함께 사용하는 것처럼 보이는 시나리오는 다음과 같습니다.

[클라이언트] -> [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 핸드 셰이크가 프록시와 서버간에 발생한다는 것을 알 수 있습니다. 웹 세션의 덤프를 사용하면 클라이언트가 대개이 시점에서 SSL 핸드 셰이크를 시작한다는 것을 알 수 있습니다. 이는 내 시나리오에서는 발생하지 않습니다.

이것은 인증서가 결코 나에게 돌아 오지 않으며 결코 검증되지 않기 때문에 트러스트 매니저에 문제가되지 않습니다.

편집하다 :

토론 후에,이 코드는 내가 실행하려고하는 코드의보다 완전한 버전입니다. 매개 변수로 간단한 (새로운 소켓 (...)) 위의이 버전은 내가 나중에 시도한 것입니다.

디버깅하려는 코드의 원본 버전이 throw됩니다.
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 함수에 전달되는 소켓입니다.


지금은 타겟 솔루션을 테스트 / 작성할 시간이 없지만 STARTTLS : recv를 사용하여 소켓을 SSLSocket으로 업 그레 이드하는 것은 기본 문제를 해결하는 것으로 보입니다.

귀하의 경우에는 프록시에 연결하고 프록시 연결을 실행 한 다음 연결을 업그레이드해야합니다. 본질적으로 CONNECT는 참조 된 질문에서 STARTTLS 대신 사용되며 "670"에 대한 확인은 필요하지 않습니다.


java.net.Proxy 또는 https.proxyHost/proxyPort 속성은 Socket. 아닌 HttpURLConnection, 통한 HTTP 프록시 만 지원합니다 Socket.

그 자신의 SSLSocket 에 대해 작업하려면, 일반 텍스트 소켓을 작성하고 HTTP CONNECT 명령을 실행 한 다음 200 응답을 확인한 다음 SSLSocket. 랩핑하면됩니다 SSLSocket.

편집 CONNECT 명령을 보낼 때 물론 소켓을 닫아서는 안됩니다. 답장을 읽을 때 BufferedReader, 사용하면 안됩니다 BufferedReader, 그렇지 않으면 데이터가 손실됩니다. 그 행을 수동으로 읽거나 사용되지 않는 DataInputStream.readLine(), 사용하십시오. 또한 RFC 2616을 완전히 따라야합니다.







ssl