2015-01-16 24 views
12

Tôi đang cố gắng sử dụng Java (Android) để kết nối với máy chủ có ổ cắm SSL. Xin lưu ý rằng đây không phải là dữ liệu HTTP. Đây là giao thức độc quyền với kết hợp văn bản và dữ liệu nhị phân.Làm cách nào để kết nối ổ cắm SSL thông qua proxy HTTP?

Tôi muốn chuyển tiếp kết nối SSL đó thông qua proxy HTTP, nhưng tôi đang gặp rất nhiều vấn đề với điều đó. Hiện tại, kịch bản mà tôi sử dụng và trình duyệt của tôi dường như sử dụng với proxy mực như sau:

[khách hàng] -> [kết nối http] -> [proxy] -> [kết nối ssl] -> [máy chủ]

Thao tác này cho trình duyệt, vì sau khi proxy thực hiện kết nối ssl, một cuộc đàm phán TLS diễn ra ngay lập tức. Tuy nhiên mã của tôi dường như không làm điều đó.

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); 

Vấn đề mà tôi có là createSocket KHÔNG BAO GIỜ TRẢ LẠI. Với một bãi chứa wireshark từ máy proxy, tôi có thể thấy rằng bắt tay tcp diễn ra giữa proxy và máy chủ. Với các bãi từ các phiên web, tôi có thể thấy rằng khách hàng thường khởi tạo một bắt tay SSL vào thời điểm này, điều này không xảy ra trong kịch bản của tôi.

Đây không phải là vấn đề với người quản lý tin cậy, bởi vì chứng chỉ không bao giờ lấy lại cho tôi và chứng chỉ không bao giờ được xác thực.

EDIT:

Sau khi thảo luận, đây là phiên bản hoàn chỉnh hơn của mã tôi đang cố gắng chạy. Phiên bản này ở trên với tham số đơn giản (mới Socket (...)) là thứ tôi đã thử sau này.

Phiên bản gốc của mã Tôi đang cố gắng để gỡ lỗi ném
java.net.ConnectException: failed to connect to /192.168.1.100 (port 443): connect failed: ETIMEDOUT (Connection timed out)

Trình tự như sau (một chút đơn giản hóa một lần nữa):

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] 

Sau đó, đọc những phản ứng với những điều sau mã:

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) 
    { 
    } 

Tôi có thể nói rằng mã này hoạt động vì nó hoạt động mà không có SSL. Ổ cắm được tạo ra ở đây, proxySocket là ổ cắm được chuyển tới hàm createSocket thay vì chỉ tạo một cái mới giống như trong ví dụ ban đầu của tôi.

+0

java.net.Proxy không hoạt động trong HTTP. Có vẻ như yêu cầu proxy SOCKS. Tôi đã tìm thấy một bản vá cho hệ điều hành mặt trời nhưng không có gì cho android. Điều này rất quan trọng vì nó hoạt động cho HTTP vì Android không thể cung cấp hỗ trợ HTTPS/SOCKS nếu không có ứng dụng của bên thứ 3 (cài đặt mặc định không cung cấp đủ cấu hình @ January 2015). – Eric

+0

Nếu bạn đang kết nối với một ổ cắm SSL thông qua một proxy HTTP, tôi khuyên bạn nên xem xét websockets nếu có thể - đặc biệt là kể từ khi thông báo lỗi "java.net.ConnectException: không kết nối với /192.168.1.100 (cổng 443)" cho thấy một nỗ lực để kết nối với cổng HTTPS tiêu chuẩn. Tất nhiên, nó phụ thuộc vào trường hợp sử dụng của bạn .... –

Trả lời

13

java.net.Proxy, hoặc https.proxyHost/proxyPort tài sản, chỉ hỗ trợ HTTP proxy qua HttpURLConnection, không thông qua một Socket.

Để làm cho điều đó làm việc cho một SSLSocket của riêng bạn, tất cả các bạn cần phải đến là tạo một ổ cắm rõ, vấn đề một HTTP CONNECT trên đó, kiểm tra câu trả lời cho 200, và sau đó quấn nó trong một số SSLSocket.

EDIT Khi gửi lệnh CONNECT, bạn không được đóng ổ cắm, tất nhiên; và khi đọc câu trả lời của nó, bạn không được sử dụng BufferedReader, nếu không bạn sẽ mất dữ liệu; hoặc đọc dòng bằng tay hoặc sử dụng DataInputStream.readLine(), mặc dù không dùng nữa. Bạn cũng cần phải làm theo RFC 2616 hoàn toàn.

1

Tôi không có thời gian để kiểm tra/viết một giải pháp nhắm mục tiêu ngay bây giờ, tuy nhiên Upgrading socket to SSLSocket with STARTTLS: recv failed dường như đề cập đến vấn đề cơ bản.Trong trường hợp của bạn, bạn cần kết nối với proxy, cấp proxy kết nối, sau đó nâng cấp kết nối - về cơ bản bạn CONNECT thay thế STARTTLS trong câu hỏi được tham chiếu và kiểm tra "670" là không cần thiết .

+1

Nhưng thay vào đó hãy kiểm tra HTTP '200'. – EJP

6

Bạn phải sử dụng javax.net lib. bạn có thể lưu trữ mục tiêu của mình bằng cách sử dụng javax.net.ssl. *.

Tôi nghĩ bạn có thể nhận được giải pháp bằng tài liệu oracle. Đây là liên kết cho điều đó.

SSLSocketClientWithTunneling

+2

Có lỗi nhỏ trong liên kết đó. Một trong các dòng yêu cầu được kết thúc bằng '\ n,' phải là '\ r \ n.' – EJP

Các vấn đề liên quan