Java https 代理(使用 https.proxyPort 和 https.proxyHost)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16351413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:25:57  来源:igfitidea点击:

Java https proxy (using https.proxyPort and https.proxyHost)

javahttpsproxy

提问by Raekye

I was making a Java application that relies on setting http.proxyPortand http.proxyHost. There are two processes: One is the regular program, the other is the proxy. I have a simple socket listener running on http.proxyPort(which I control). It's as simple as

我正在制作一个依赖于设置http.proxyPorthttp.proxyHost. 有两个进程:一个是正则程序,一个是代理。我有一个简单的套接字侦听器正在运行http.proxyPort(由我控制)。就这么简单

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

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

So whenever "process 1" makes an http request - like

因此,每当“进程 1”发出 http 请求时 - 就像

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

It goes through the proxy. Now what if the client is using an HTTPS protocol? Like instead use https://google.ca? There's a property https.proxyPortand https.proxyHost, but I've literally been trying for months (on and off, it's not too important) without luck. I've read a bunch of threads (I will list some at the end so you know I have done something).

它通过代理。现在如果客户端使用 HTTPS 协议怎么办?喜欢改为使用https://google.ca? 有一个属性https.proxyPortand https.proxyHost,但我确实已经尝试了几个月(断断续续,这不是太重要)而没有运气。我已经阅读了一堆线程(我会在最后列出一些,所以你知道我做了一些事情)。

My closest attempt so far: 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();
}

And I get this error javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?I googled it but came up with some mail stuff instead.

我得到了这个错误,javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?我用谷歌搜索了它,但想出了一些邮件内容。

Basically, I need to create a java proxy server, that's set to the client by the https.proxyPortand https.proxyHostflags, and can send data back to the client app, which may not be modified in any way (it's just using URL connection = new URL("https://..."))

基本上,我需要创建一个 java 代理服务器,它通过https.proxyPorthttps.proxyHost标志设置给客户端,并且可以将数据发送回客户端应用程序,它可能不会以任何方式修改(它只是使用URL connection = new URL("https://...")

A few of the sites I tried...

我试过的几个网站...

回答by TheConstructor

As auntyellow commented: you don't need to do any SSL-fiddling yourself. Basically https-proxying is about forwarding binary data between two parties.

正如阿姨评论的那样:你不需要自己做任何 SSL 摆弄。基本上,https 代理是关于在两方之间转发二进制数据。

To cite draft-luotonen-web-proxy-tunneling-01.txt:

引用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 >>>
 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 >>>

So basically you need to ensure you trust your client enough to connect from your proxies firewall-position to the given host and port. Because of this common practice is to limit allowed port to 443, reject connection to localhost and from "untrusted" parties.

所以基本上你需要确保你足够信任你的客户端,从你的代理防火墙位置连接到给定的主机和端口。因为这种常见的做法是将允许的端口限制为 443,拒绝与本地主机和来自“不受信任”方的连接。

This is a "simple" server which is usable as https.proxyin Java if you are not jet convinced:

这是一个“简单”的服务器,https.proxy如果您不相信它,它可以像在 Java 中一样使用:

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

回答by MarekM

Default java SE7 implementation of URLConnection for HTTPS protocol uses parameters
https.proxyHostand https.proxyPort

HTTPS 协议的 URLConnection 的默认 java SE7 实现使用参数
https.proxyHosthttps.proxyPort

Add to Tomcat:

添加到Tomcat:

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

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