2013-05-03 12 views
11

Tôi đã tạo một ứng dụng Java dựa trên cài đặt http.proxyPorthttp.proxyHost. Có hai quy trình: Một là chương trình thông thường, chương trình kia là proxy. Tôi có một bộ lắng nghe socket đơn giản chạy trên http.proxyPort (mà tôi kiểm soát). Nó đơn giản nhưProxy https Java (sử dụng https.proxyPort và https.proxyHost)

while (true) { 
    try { 
    Socket connection = server.accept(); 

    Handler handler = new Handler(connection); 
    handler.start(); 
    } catch (Exception ex) { 
     ex.printStackTrace(); 
    } 
} 

Vì vậy, bất cứ khi nào "quá trình 1" làm cho một http request - như

URL yahoo = new URL("http://www.google.ca/"); 
URLConnection yc = yahoo.openConnection(); 
System.out.println(yc.getClass().getName()); 
BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream())); 

Nó đi qua proxy. Bây giờ nếu khách hàng đang sử dụng giao thức HTTPS thì sao? Thay vào đó, hãy sử dụng https://google.ca? Có một tài sản https.proxyPorthttps.proxyHost, nhưng tôi đã cố gắng trong nhiều tháng (trên và tắt, nó không quá quan trọng) mà không có may mắn. Tôi đã đọc một loạt các chủ đề (tôi sẽ liệt kê một số ở cuối để bạn biết tôi đã làm một cái gì đó).

nỗ lực của tôi gần gũi nhất cho đến nay: server

try { 
    System.setProperty("javax.net.ssl.keyStore", "test.jks"); 
    System.setProperty("javax.net.ssl.keyStorePassword", "2520xe"); 

    SSLServerSocketFactory sslserversocketfactory = 
      (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 
    SSLServerSocket sslserversocket = 
      (SSLServerSocket) sslserversocketfactory.createServerSocket(9999); 
    System.out.println("Ready"); 
    SSLSocket sslsocket = (SSLSocket) sslserversocket.accept(); 

    InputStream inputstream = sslsocket.getInputStream(); 
    InputStreamReader inputstreamreader = new InputStreamReader(inputstream); 
    BufferedReader bufferedreader = new BufferedReader(inputstreamreader); 

    OutputStream toClient = sslsocket.getOutputStream(); 
    toClient.write(("HTTP/1.0 200 Connection established\n" + 
      "Content-Length: " + "Shut down!".getBytes().length 
            + "\r\n").getBytes("utf-8")); 
    toClient.write("Shut down!".getBytes("utf-8")); 
    toClient.close(); 
} catch (Exception exception) { 
    exception.printStackTrace(); 
} 

Khách hàng

try { 
    System.setProperty("https.proxyHost", "127.0.0.1"); 
    System.setProperty("https.proxyPort", "9999"); 
    URL yahoo = new URL("https://www.google.ca/"); 
    URLConnection yc = yahoo.openConnection(); 
    System.out.println(yc.getClass().getName()); 
    BufferedReader in = new BufferedReader(
       new InputStreamReader(
       yc.getInputStream())); 
    String inputLine; 

    while ((inputLine = in.readLine()) != null) 
    System.out.println(inputLine); 
    in.close(); 
} catch (Exception ex) { 
    ex.printStackTrace(); 
} 

Và tôi nhận được lỗi này javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? Tôi googled nó, nhưng đã đưa ra một số nội dung email thay thế.

Về cơ bản, tôi cần phải tạo một máy chủ java proxy, đó là thiết lập cho khách hàng bởi các https.proxyPorthttps.proxyHost cờ, và có thể gửi dữ liệu trở lại để ứng dụng client, mà có thể không được sửa đổi trong bất kỳ cách nào (nó chỉ sử dụng URL connection = new URL("https://..."))

Một vài trong số các trang web mà tôi đã cố gắng ...

  • creating a Java Proxy Server that accepts HTTPS
  • http://stilius.net/java/java_ssl.php
  • Có một cái gì đó khác về việc Java chấp nhận tất cả các chứng chỉ, nhưng tôi không thể tìm thấy bất kỳ liên kết nào. Tôi có mã, nhưng tôi gặp phải nhiều lỗi hơn những gì tôi đang làm ngay bây giờ, nhưng tôi có thể bao gồm nó nếu nó giúp (tôi không ban đầu vì đây là một câu hỏi dài)
+0

Hiện nay tôi đang còn phải vật lộn với việc thiết lập hệ thống sở hữu https.proxyHost. Bằng cách đính kèm một trình gỡ lỗi vào quá trình VM, có vẻ như máy ảo không nhận được thuộc tính https.proxyHost chút nào. –

+0

Ok, chỉ sử dụng proxyHost và proxyPort mà không có http. hoặc https. tiền tố bây giờ làm việc cho tôi. Nó cũng có vẻ như hành vi đó (một lần nữa) được biên dịch từ Java 6 đến Java 7. Tuyên bố trên appleis cho Java 7. –

+0

Khi yêu cầu URL https qua proxy, máy khách phải yêu cầu 'CONNECT www.google.ca:443 HTTP/1.0' và phản hồi của máy chủ proxy 'HTTP/1.0 200 Kết nối được thiết lập', cả hai đều ở dạng plain. Sau đó, khách hàng bắt đầu một cái bắt tay SSL. – auntyellow

Trả lời

8

Như auntyellow đã nhận xét: bạn không cần phải tự mình thực hiện bất kỳ lỗi SSL nào. Về cơ bản https-proxy là về chuyển tiếp dữ liệu nhị phân giữa hai bên.

Để trích dẫn draft-luotonen-web-proxy-tunneling-01.txt:

CLIENT -> SERVER      SERVER -> CLIENT 
-------------------------------------- ----------------------------------- 
CONNECT home.netscape.com:443 HTTP/1.0 
User-agent: Mozilla/4.0 
<<< empty line >>> 
             HTTP/1.0 200 Connection established 
             Proxy-agent: Netscape-Proxy/1.1 
             <<< empty line >>> 
       <<< data tunneling to both directions begins >>> 

Vì vậy, về cơ bản bạn cần phải đảm bảo bạn tin tưởng của khách hàng của bạn đủ để kết nối từ proxy firewall vị trí của bạn đến máy chủ nhất định và cảng. Vì thực tế phổ biến này là giới hạn cổng được phép cho 443, từ chối kết nối với máy chủ cục bộ và từ các bên "không đáng tin cậy".

Đây là một "đơn giản" máy chủ đó là có thể sử dụng như https.proxy trong Java nếu bạn không phải là máy bay phản lực thuyết phục:

import java.io.*; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

/** 
* Created for http://stackoverflow.com/q/16351413/1266906. 
*/ 
public class Server extends Thread { 

    public static void main(String[] args) { 
     (new Server()).run(); 
    } 

    public Server() { 
     super("Server Thread"); 
    } 

    @Override 
    public void run() { 
     try (ServerSocket serverSocket = new ServerSocket(9999)) { 
      Socket socket; 
      try { 
       while ((socket = serverSocket.accept()) != null) { 
        (new Handler(socket)).start(); 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); // TODO: implement catch 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); // TODO: implement catch 
      return; 
     } 
    } 

    public static class Handler extends Thread { 
     public static final Pattern CONNECT_PATTERN = Pattern.compile("CONNECT (.+):(.+) HTTP/(1\\.[01])", 
                     Pattern.CASE_INSENSITIVE); 
     private final Socket clientSocket; 
     private boolean previousWasR = false; 

     public Handler(Socket clientSocket) { 
      this.clientSocket = clientSocket; 
     } 

     @Override 
     public void run() { 
      try { 
       String request = readLine(clientSocket); 
       System.out.println(request); 
       Matcher matcher = CONNECT_PATTERN.matcher(request); 
       if (matcher.matches()) { 
        String header; 
        do { 
         header = readLine(clientSocket); 
        } while (!"".equals(header)); 
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(clientSocket.getOutputStream(), 
                        "ISO-8859-1"); 

        final Socket forwardSocket; 
        try { 
         forwardSocket = new Socket(matcher.group(1), Integer.parseInt(matcher.group(2))); 
         System.out.println(forwardSocket); 
        } catch (IOException | NumberFormatException e) { 
         e.printStackTrace(); // TODO: implement catch 
         outputStreamWriter.write("HTTP/" + matcher.group(3) + " 502 Bad Gateway\r\n"); 
         outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n"); 
         outputStreamWriter.write("\r\n"); 
         outputStreamWriter.flush(); 
         return; 
        } 
        try { 
         outputStreamWriter.write("HTTP/" + matcher.group(3) + " 200 Connection established\r\n"); 
         outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n"); 
         outputStreamWriter.write("\r\n"); 
         outputStreamWriter.flush(); 

         Thread remoteToClient = new Thread() { 
          @Override 
          public void run() { 
           forwardData(forwardSocket, clientSocket); 
          } 
         }; 
         remoteToClient.start(); 
         try { 
          if (previousWasR) { 
           int read = clientSocket.getInputStream().read(); 
           if (read != -1) { 
            if (read != '\n') { 
             forwardSocket.getOutputStream().write(read); 
            } 
            forwardData(clientSocket, forwardSocket); 
           } else { 
            if (!forwardSocket.isOutputShutdown()) { 
             forwardSocket.shutdownOutput(); 
            } 
            if (!clientSocket.isInputShutdown()) { 
             clientSocket.shutdownInput(); 
            } 
           } 
          } else { 
           forwardData(clientSocket, forwardSocket); 
          } 
         } finally { 
          try { 
           remoteToClient.join(); 
          } catch (InterruptedException e) { 
           e.printStackTrace(); // TODO: implement catch 
          } 
         } 
        } finally { 
         forwardSocket.close(); 
        } 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); // TODO: implement catch 
      } finally { 
       try { 
        clientSocket.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); // TODO: implement catch 
       } 
      } 
     } 

     private static void forwardData(Socket inputSocket, Socket outputSocket) { 
      try { 
       InputStream inputStream = inputSocket.getInputStream(); 
       try { 
        OutputStream outputStream = outputSocket.getOutputStream(); 
        try { 
         byte[] buffer = new byte[4096]; 
         int read; 
         do { 
          read = inputStream.read(buffer); 
          if (read > 0) { 
           outputStream.write(buffer, 0, read); 
           if (inputStream.available() < 1) { 
            outputStream.flush(); 
           } 
          } 
         } while (read >= 0); 
        } finally { 
         if (!outputSocket.isOutputShutdown()) { 
          outputSocket.shutdownOutput(); 
         } 
        } 
       } finally { 
        if (!inputSocket.isInputShutdown()) { 
         inputSocket.shutdownInput(); 
        } 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); // TODO: implement catch 
      } 
     } 

     private String readLine(Socket socket) throws IOException { 
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
      int next; 
      readerLoop: 
      while ((next = socket.getInputStream().read()) != -1) { 
       if (previousWasR && next == '\n') { 
        previousWasR = false; 
        continue; 
       } 
       previousWasR = false; 
       switch (next) { 
        case '\r': 
         previousWasR = true; 
         break readerLoop; 
        case '\n': 
         break readerLoop; 
        default: 
         byteArrayOutputStream.write(next); 
         break; 
       } 
      } 
      return byteArrayOutputStream.toString("ISO-8859-1"); 
     } 
    } 
} 
+0

Không tệ. Có một nếp nhăn. Khi proxy đọc EOS từ một ổ cắm, nó sẽ tắt ổ cắm khác cho đầu ra và thoát khỏi luồng đó. Khi nó đã tắt cả hai ổ cắm, nó có thể đóng cả hai và thoát khỏi luồng thứ hai. Điều này giúp trong các tình huống lưu giữ HTTP. – EJP

+0

@EJP Cảm ơn bạn đã phản hồi! Tôi đã chuyển sang [gọi phương thức tắt máy] (http://stackoverflow.com/a/3626450/1266906) và thêm vào cuối cùng là 'forwardSocket'. Tôi đã bỏ lỡ điều gì khác? – TheConstructor

1

Mặc định java SE7 thi hành URLConnection cho giao thức HTTPS sử dụng các thông số
https.proxyHosthttps.proxyPort

Thêm vào Tomcat:

-Dhttps.proxyHost = "192.168.121.31" -Dhttps.proxyPort = "3128"