gpt4 book ai didi

java - 正确关闭 SSLSocket

转载 作者:太空宇宙 更新时间:2023-11-03 12:42:01 32 4
gpt4 key购买 nike

我想用 Java 实现一个 SSL 代理。我基本上开了两个socket browser-proxy , proxy-server ,并运行两个线程来写入 proxy-server他们阅读的内容 browser-proxy ,反之亦然。每个线程看起来像这样:

while (true) {
nr = in.read(buffer);
if (nr == -1) System.out.println(sockin.toString()+" EOF "+nr);
if (nr == -1) break;
out.write(buffer, 0, nr);
}
sockin.shutdownInput();
sockout.shutdownOutput(); // now the second thread will receive -1 on read

每个线程只会关闭输入套接字,因此最终两个套接字都关闭。

但是如果我想使用 SSLSocket 怎么办? ?看来 shutdownOutput/Input那里不支持方法。这是我得到的异常(exception)。
Exception in thread "Thread-35" java.lang.UnsupportedOperationException: \
The method shutdownInput() is not supported in SSLSocket
at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.shutdownInput(Unknown Source)

我想出的是:
try {
while (true) {
nr = in.read(buffer);
if (nr == -1) System.out.println(sockin.toString()+" EOF "+nr);
if (nr == -1) break;
out.write(buffer, 0, nr);
}
// possible race condition if by some mysterious way both threads would get EOF
if (!sockin.isClosed()) sockin.close();
if (!sockout.isClosed()) sockout.close();
} catch (SocketException e) {/*ignore expected "socket closed" exception, */}

每次我的套接字结束时,我都必须捕获并忽略套接字结束异常。

我的问题是:
  • shutdownInput()不受支持,我怎么会收到 -1来自 SSLSocket 的回答.
  • 我的痛苦有更好的解决方案吗?我不认为有任何理智,因为其中一个线程可能已经在阻塞中 read当另一个线程标记他“我完成了”时的方法。我看到将他踢出阻塞线程的唯一方法是关闭套接字并引发“关闭的套接字”异常。

    (我猜你可以使用多线程消息传递和异步数据读取的一些邪恶的组合来向另一个线程发出你的工作完成的信号,但这太可怕了,我担心 IntelliJ 的风格警察会来找我只是为了思考它...)

  • 澄清:我知道 shutdownInputshutdownOutput没有意义,因为根据规范,您不能拥有半双工 TLS 连接。但鉴于此,我如何才能在没有异常的情况下结束 Java 中的 TLS 连接?

    请不要告诉我 shutdownIput对 TLS 没有意义。我知道, 这不是我要问的 .我在问我还能用什么来关闭 SSLSocket正确(因此标题为“正确关闭 SSLSocket”。

    最佳答案

    这是我最初的(暂时删除的)答案,但它显然不够好,因为它很快就被-2 了......我想我应该添加更多的解释。

    section on closure alerts 中指定的,无法关闭 SSL/TLS 套接字的一半连接以符合 TLS 协议(protocol)。 .

    The client and the server must share knowledge that the connection is ending in order to avoid a truncation attack. Either party may initiate the exchange of closing messages.

    close_notify This message notifies the recipient that the sender will not send any more messages on this connection. The session becomes unresumable if any connection is terminated without proper close_notify messages with level equal to warning.

    Either party may initiate a close by sending a close_notify alert. Any data received after a closure alert is ignored.

    Each party is required to send a close_notify alert before closing the write side of the connection. It is required that the other party respond with a close_notify alert of its own and close down the connection immediately, discarding any pending writes. It is not required for the initiator of the close to wait for the responding close_notify alert before closing the read side of the connection.



    尽管您可能只想关闭一半的连接(通过 shutdownInput()/ shutdownOutput() 输入或输出),底层 TLS 层仍然需要发送这个 close_notify在真正关闭通信之前关闭数据包,否则将被视为截断攻击。

    修复很简单:只使用 close()SSLSocket s:它不只是像普通 TCP 套接字那样关闭连接,而是根据 SSL/TLS 规范干净利落地做到这一点。

    编辑 : 补充说明。

    简短的回答仍然是:使用 close() ,您可能还需要从 SSL/TLS 之上的协议(protocol)中找出何时适合这样做。

    (只是为了澄清,您想要做的实际上是一个“中间人”(MITM)代理。您需要将客户端配置为信任代理的证书,可能就像它是服务器一样证书,如果这意味着透明地发生。HTTPS 连接如何通过 HTTP 代理工作是 different question。)

    在您对您的 other related question 的一些评论中,我的印象是你以为不回 -1是“破坏了 TLS 协议(protocol)”。这实际上不是关于协议(protocol),而是关于 API:向 TLS 堆栈的(程序员)用户提供编程结构(类、方法、函数等)以便能够使用 TLS 的方式。 SSLSocket只是 API 的一部分,为程序员提供以类似于普通的方式使用 SSL/TLS 的能力 Socket . TLS 规范根本不讨论套接字。
    虽然构建 TLS(及其前导 SSL)的目的是为了能够提供这种抽象,但归根结底 SSLSocket就是这样:一个抽象。
    符合OOP设计原则, SSLSocket继承 Socket并尽可能地坚持 Socket 的行为.但是,由于 SSL/TLS 的工作方式(并且因为 SSLSocket 实际上位于普通 Socket 之上),不可能有所有功能的精确映射。

    特别是,从普通 TCP 套接字到 SSL/TLS 套接字的转换区域不能完全透明地建模。
    在设计 SSLSocket 时必须做出一些任意选择(例如握手的方式)。类(class):
    这是不 (a) 向 SSLSocket 暴露过多 TLS 的特殊性之间的妥协。用户,(b) 使 TLS 机制工作,(c) 将所有这些映射到父类(super class) ( Socket) 提供的现有接口(interface)。
    这些选择不可避免地会对 SSLSocket 的功能产生一些影响。 ,这就是为什么它的 Javadoc API page有相对大量的文字。 SSLSocket.close() ,在API方面,必须遵守 Socket.close() 的说明. InputStream/ OutputStreamSSLSocket 获得与来自底层平原的不一样 Socket ,您可能看不到(除非 you've converted it explicitly )。
    SSLSocket旨在尽可能接近地使用,就好像它是普通的一样 Socket ,以及 InputStream你得到的设计目的是尽可能接近基于文件的输入流。
    顺便说一下,这不是 Java 特有的。甚至 C 中的 Unix 套接字也与文件描述符和 read() 一起使用。 .
    这些抽象很方便,并且大部分时间都可以工作,只要您知道它们的局限性。

    说到 read() ,即使在 C 中, EOF 的概念来自文件尾终止,但您实际上并未处理文件。
    TCP 套接字的问题在于,虽然您可以检测到远程方在发送 FIN 时选择关闭连接的时间。 ,如果它不发送任何内容,您将无法检测到它没有。
    从套接字读取任何内容时,无法区分非 Activity 连接和断开连接之间的区别。
    所以说到网络编程,靠阅读 -1来自 InputStream如果没问题,但你永远不要仅仅依赖它,否则你最终可能会得到未释放的死连接。
    虽然一方正确关闭半 TCP 连接是“礼貌的”(例如远程方在其 -1 上读取 InputStream 或等效的东西,如 Orderly Versus Abortive Connection Release in Java 中所述),但处理这种情况不是'足够了。
    这就是为什么 TCP 之上的协议(protocol)通常是 designed to give an indication as to when they're done sending data : 例如,
    SMTP 发送 QUIT (并有特定的终止符),HTTP 1.1 使用 Content-Length或分块编码。

    (顺便说一句,如果您对 SSLSocket 提供的抽象不满意,请尝试直接使用 SSLEngine。它可能会更难,并且不会改变 TLS 规范关于发送 close_notify 的说明。 .)

    一般来说,对于 TCP,您应该知道,在应用程序协议(protocol)级别,何时停止发送和接收数据(以避免未释放的连接和/或依赖超时错误)。
    当涉及到 TLS 时,TLS 规范中的这句话使这种通常良好的实践成为更强的要求:
    “要求对方用自己的 close_notify 警报进行响应,并立即关闭连接并丢弃任何挂起的写入。”
    您将不得不通过应用程序协议(protocol)以某种方式进行同步,否则远程方可能仍有一些东西要写(特别是如果您允许流水线请求/响应)。

    如果您编写的代理用于拦截 HTTPS 连接(作为 MITM),您可以查看请求的内容。即使 HTTP 请求是流水线化的,您也可以计算由
    目标服务器(并期望它们何时结束,通过 Content-Length 或块分隔符)。

    但是,您将无法拥有纯粹透明的通用 TLS“拦截器”。
    TLS 旨在保护两方之间的传输,而不是中间有一个。
    虽然实现该保护(避免 MITM)的大部分机制是通过服务器证书完成的,但其他一些 TLS 机制也会受到阻碍(关闭警报就是其中之一)。
    请记住,您正在有效地创建两个不同的 TLS 连接,考虑到 TLS 的目的,它们很难合并为一个这一事实应该不足为奇。
    SSLSocket.close()是关闭 SSLSocket 的正确方法,但应用程序协议(protocol)也将帮助您确定何时适合这样做。

    关于java - 正确关闭 SSLSocket,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6424998/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com