2013-05-03 23 views
11

Tworzyłem aplikację Java, która bazuje na ustawieniach http.proxyPort i http.proxyHost. Istnieją dwa procesy: jeden to zwykły program, drugi to proxy. Mam prosty detektor nasłuchujący działający pod numerem http.proxyPort (który kontroluję). To tak proste, jakJava https proxy (przy użyciu https.proxyPort i https.proxyHost)

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

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

Więc kiedy "Proces 1" sprawia, że ​​żądanie http - jak

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

to przechodzi przez pełnomocnika. Co się stanie, jeśli klient używa protokołu HTTPS? Jak zamiast tego użyj https://google.ca? Istnieje własność https.proxyPort i https.proxyHost, ale dosłownie próbowałem przez wiele miesięcy (bez względu na to, czy to nie jest ważne) bez szczęścia. Przeczytałem kilka wątków (na koniec wymienię niektóre, więc wiesz, że coś zrobiłem).

Moja najbliższa próba tak daleko: 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(); 
} 

Client

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

I ten błąd javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? google go jednak wymyślił jakiegoś poczty zamiast rzeczy.

Zasadniczo, muszę stworzyć serwer java proxy, który jest nastawiony na klienta przez https.proxyPort i https.proxyHost flagi i może przesyłać dane z powrotem do aplikacji klienckiej, która nie może być w żaden sposób modyfikowane (nie jest to tylko przy użyciu URL connection = new URL("https://..."))

kilka stron próbowałem ...

+0

Obecnie jestem również zmaga się z ustawienie właściwości systemu https.proxyHost. Po podłączeniu debuggera do procesu VM wydaje się, że maszyna wirtualna w ogóle nie pobiera właściwości https.proxyHost. –

+0

Ok, tylko za pomocą proxyHost i proxyPort bez http. lub https. Prefiks teraz działa dla mnie. Wygląda również na to, że zachowanie zostało (ponownie) przeniesione z Java 6 do Java 7. Instrukcja powyżej appleis do Javy 7. –

+0

Gdy żądanie https URL przez proxy, klient musi zażądać 'CONNECT www.google.ca:443 HTTP/Odpowiedzi 1.0' i serwera proxy 'HTTP/1.0 200 Connection established', oba w trybie zwykłym. Następnie klient rozpoczyna uzgadnianie protokołu SSL. – auntyellow

Odpowiedz

8

Jako ciocia skomentował: nie musisz sam robić nic z SSL-fiddlingiem. Zasadniczo https-proxying polega na przekazywaniu danych binarnych między dwiema stronami.

Przytoczyć 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 >>> 

Więc w zasadzie trzeba upewnić się ufać swojemu klientowi wystarczy połączyć z serwerami proxy zapory pozycji do danego hosta i portu. Z tego powodu powszechną praktyką jest ograniczenie dozwolonego portu do 443, odrzucenie połączenia z localhostem i od "niezaufanych" stron.

Jest to „proste” serwer, który jest użyteczny jako https.proxy w Javie, jeśli nie jesteś przekonany jet:

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

Nieźle. Jest zmarszczka. Gdy proxy odczyta EOS z jednego gniazda, powinno zamknąć drugie gniazdo dla wyjścia i wyjść z tego wątku. Po zamknięciu obu gniazd może je zamknąć i wyjść z drugiego wątku. Pomaga to w sytuacjach utrzymywania aktywności HTTP. – EJP

+0

@EJP Dziękujemy za informację zwrotną! Przełączyłem się na [wywołanie metody zamykania] (http://stackoverflow.com/a/3626450/1266906) i dodałem w końcu bliższe 'forwardSocket'. Czy tęskniłem za czymś innym? – TheConstructor

1

Domyślna implementacja Javy SE7 z URLConnection dla protokołu HTTPS wykorzystuje parametry
https.proxyHost i https.proxyPort

Dodaj do Tomcat:

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

Powiązane problemy