8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

尝试从 SSLSocket 读取时出现“javax.net.ssl.SSLException:不支持或无法识别的 SSL 消息”

Sirat Ben jemaa 1月前

40 0

我正在编写一个简单的 Java 代理,它打印客户端的请求,然后将其转发到目标服务器。它必须支持 SSL 连接,因为大多数网页都使用 HTTPS,而不是 ...

我正在编写一个简单的 Java 代理,它打印客户端的请求,然后将其转发到目标服务器。它必须支持 SSL 连接,因为大多数网页都使用 HTTPS 而不是纯 HTTP。所以我使用 SSLSockets 而不是普通的 Sockets。

但是,当我将浏览器配置为通过代理连接并发出请求时,连接被拒绝,并且我收到 SSLException:不支持或无法识别的 SSL 消息

我想我忘记了一些有关 SSL 配置的信息。此外,我正在使用 renatoathaydes / rawhttp 库来轻松管理请求。

这是主要方法,无限循环监听新连接:

public static void main(String... args) {
        RequestHandler handler = new RequestHandler();
        try {
            SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
            SSLServerSocket sslServerSocket = (SSLServerSocket) factory.createServerSocket(8080);
            while (true) {
                SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
                Runnable action = () -> handler.handleRequest(sslSocket);
                Thread thread = new Thread(action);
                thread.start();
            }
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
        }
    }

RequestHandler 类负责读取客户端请求、打印到记录器并将其转发到服务器:

@Slf4j
@NoArgsConstructor
public class RequestHandler {

    public void handleRequest(SSLSocket clientSocket) {
        try {
            RawHttpRequest request = readClientRequest(clientSocket);
            SSLSocket serverSocket = writeRequestToServer(request);

            log.info(request.toString().replace("\\n", ""));

            RawHttpRequest serverResponse = readResponseFromServer(serverSocket);
            writeResponseToClient(clientSocket, serverResponse);

            serverSocket.close();
            clientSocket.close();
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    private RawHttpRequest readClientRequest(SSLSocket clientSocket) throws IOException {
        RawHttp http = new RawHttp();
        return http.parseRequest(clientSocket.getInputStream());
    }

    private void writeResponseToClient(SSLSocket clientSocket, RawHttpRequest response) throws IOException {
        response.writeTo(clientSocket.getOutputStream());
    }

    private RawHttpRequest readResponseFromServer(SSLSocket serverSocket) throws IOException {
        RawHttp http = new RawHttp();
        return http.parseRequest(serverSocket.getInputStream());
    }

    private SSLSocket writeRequestToServer(RawHttpRequest httpRequest) throws IOException {
        String host = httpRequest.getUri().getHost();
        int port = httpRequest.getUri().getPort();

        SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        SSLSocket sslSocket = (SSLSocket) factory.createSocket(host, port);

        httpRequest.writeTo(sslSocket.getOutputStream());
        return (sslSocket);
    }

}

我尝试了 SSL 和普通 HTTP 连接,将浏览器配置为通过 localhost 和指定端口 (8080) 进行连接。我将端口更改为 443,这是我所看到的 SSL 端口,但没有效果。

也尝试过不使用 RawHttp 库,但问题是一样的。

帖子版权声明 1、本帖标题:尝试从 SSLSocket 读取时出现“javax.net.ssl.SSLException:不支持或无法识别的 SSL 消息”
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Sirat Ben jemaa在本站《http》版块原创发布, 转载请注明出处!
最新回复 (0)
  • “我以为 OP 想要的是允许所有证书”——我不确定是什么让您这么想,因为 OP 根本没有提到证书,而且错误与证书无关。

  • 我以为 OP 想要的是允许所有证书。我并不质疑他们的意图是好是坏,也许 OP 想尝试不同的用例。而这段代码只是为了解除阻止编写与 SSL 配合使用的代理的目的。而这段代码并不是一个确切的实现,只是一个信任所有证书的建议。你不需要是超人就能知道这一点。

  • 不清楚这个答案如何解决 OP 的问题。这个答案主要是代码转储,没有解释。唯一的小解释是关于信任所有证书 - 这是 a) 一个非常糟糕的想法,并且 b) 没有解决 OP 的问题(即没有证书验证失败)。

  • 那么像这样的事情怎么样:

    import javax.net.ssl.*;
    import java.io.IOException;
    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    
    public class SSLProxy {
    
        public static void main(String... args) {
            RequestHandler handler = new RequestHandler();
            try {
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
    
                    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    }
    
                    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    }
                }}, new java.security.SecureRandom());
    
                SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
                SSLServerSocket sslServerSocket = (SSLServerSocket) factory.createServerSocket(8080);
                while (true) {
                    SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
                    Runnable action = () -> handler.handleRequest(sslSocket);
                    Thread thread = new Thread(action);
                    thread.start();
                }
            } catch (IOException | NoSuchAlgorithmException | KeyManagementException ex) {
                ex.printStackTrace();
            }
        }
    }
    

    然后

    import lombok.NoArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import rawhttp.core.RawHttp;
    import rawhttp.core.RawHttpRequest;
    
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.SSLSocketFactory;
    import java.io.IOException;
    
    @Slf4j
    @NoArgsConstructor
    public class RequestHandler {
    
        public void handleRequest(SSLSocket clientSocket) {
            try {
                RawHttpRequest request = readClientRequest(clientSocket);
                SSLSocket serverSocket = writeRequestToServer(request);
    
                log.info(request.toString().replace("\\n", ""));
    
                RawHttpRequest serverResponse = readResponseFromServer(serverSocket);
                writeResponseToClient(clientSocket, serverResponse);
    
                serverSocket.close();
                clientSocket.close();
            } catch (IOException ex) {
                log.error(ex.getMessage(), ex);
            }
        }
    
        private RawHttpRequest readClientRequest(SSLSocket clientSocket) throws IOException {
            RawHttp http = new RawHttp();
            return http.parseRequest(clientSocket.getInputStream());
        }
    
        private void writeResponseToClient(SSLSocket clientSocket, RawHttpRequest response) throws IOException {
            response.writeTo(clientSocket.getOutputStream());
        }
    
        private RawHttpRequest readResponseFromServer(SSLSocket serverSocket) throws IOException {
            RawHttp http = new RawHttp();
            return http.parseRequest(serverSocket.getInputStream());
        }
    
        private SSLSocket writeRequestToServer(RawHttpRequest httpRequest) throws IOException {
            String host = httpRequest.getUri().getHost();
            int port = httpRequest.getUri().getPort() == -1 ? 443 : httpRequest.getUri().getPort();
    
            SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket sslSocket = (SSLSocket) factory.createSocket(host, port);
    
            httpRequest.writeTo(sslSocket.getOutputStream());
            return sslSocket;
        }
    }
    

    通常情况下,此实现应该信任所有证书。

  • 查看您的代码,似乎存在与 HTTPS 代理工作方式相关的缺陷。当浏览器配置为使用 HTTPS 代理时,它会向代理发送 CONNECT 请求以建立到目标服务器的隧道。这允许浏览器和目标服务器直接处理 SSL/TLS 握手,而无需代理干扰。

    在您的实现中,代理尝试处理 SSL / TLS 连接,这不是 HTTPS 代理通常的工作方式,所以在我看来这里的关键问题是:

    • 处理 CONNECT 方法:代理必须处理来自浏览器的 CONNECT 请求以建立隧道。它不应尝试解析或修改 SSL 数据;相反,它应该在隧道建立后简单地在客户端和服务器之间传递数据。
    • 原始 SSL 处理:代理不应使用 SSLSocket 进行客户端连接,而应仅使用常规 Socket。SSL/TLS 握手应在浏览器和目标服务器之间进行处理,而不是由代理进行处理。

    我尝试使用下面的代码并为我工作,并进行了一些小的更改(与我的特定环境有关)

    public static void main(String... args) {
        RequestHandler handler = new RequestHandler();
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            while (true) {
                Socket clientSocket = serverSocket.accept();
                Runnable action = () -> handler.handleRequest(clientSocket);
                Thread thread = new Thread(action);
                thread.start();
            }
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
        }
    }
    
    @Slf4j
    @NoArgsConstructor
    public class RequestHandler {
    
        public void handleRequest(Socket clientSocket) {
            try {
                InputStream input = clientSocket.getInputStream();
                OutputStream output = clientSocket.getOutputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(input));
                String line = reader.readLine();
    
                if (line == null || !line.startsWith("CONNECT")) {
                    log.error("Only CONNECT method is supported by this proxy.");
                    clientSocket.close();
                    return;
                }
    
                // Extract host and port from the CONNECT request
                String[] parts = line.split(" ");
                String[] hostPort = parts[1].split(":");
                String host = hostPort[0];
                int port = Integer.parseInt(hostPort[1]);
    
                // Establish a connection to the target server
                try (Socket serverSocket = new Socket(host, port)) {
                    output.write("HTTP/1.1 200 Connection Established\r\n\r\n".getBytes());
                    output.flush();
    
                    // Tunnel data between the client and the server
                    tunnelData(clientSocket, serverSocket);
                }
    
                clientSocket.close();
            } catch (IOException ex) {
                log.error(ex.getMessage(), ex);
            }
        }
    
        private void tunnelData(Socket clientSocket, Socket serverSocket) throws IOException {
            Thread clientToServer = new Thread(() -> forwardData(clientSocket, serverSocket));
            Thread serverToClient = new Thread(() -> forwardData(serverSocket, clientSocket));
    
            clientToServer.start();
            serverToClient.start();
    
            try {
                clientToServer.join();
                serverToClient.join();
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
            }
        }
    
        private void forwardData(Socket inputSocket, Socket outputSocket) {
            try (InputStream input = inputSocket.getInputStream();
                 OutputStream output = outputSocket.getOutputStream()) {
    
                byte[] buffer = new byte[4096];
                int bytesRead;
    
                while ((bytesRead = input.read(buffer)) != -1) {
                    output.write(buffer, 0, bytesRead);
                    output.flush();
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
    }
    

    我做了一些修复以使其正常工作:

    • 处理 CONNECT 请求:代理现在可以正确处理浏览器用于为 HTTPS 流量建立隧道的 CONNECT 方法。
    • 数据隧道:客户端和服务器之间的数据无需修改就通过隧道传输,从而允许在浏览器和目标服务器之间直接进行 SSL/TLS 握手。
    • 使用普通套接字:此实现使用普通套接字对象进行客户端连接并按原样传输数据,而无需尝试解析或修改 SSL 流量。
  • - 您的(未记录的)代码片段让我感到困惑。而且不清楚您如何配置浏览器以通过代理进行连接。但我看不到任何尝试在这里实现 HTTP 代理(需要 HTTPS 的 CONNECT 请求)或 SOCKS 代理。因此,您可能对浏览器和代理如何相互通信存在根本性的误解。请注意,这与浏览器和 HTTPS 服务器的通信方式不同。

返回
作者最近主题: