当前位置: 首页 > 面试题库 >

正确关闭SSLSocket

梁嘉祥
2023-03-14
问题内容

我想在Java中实现SSL代理。我基本上打开了两个套接字browser-proxyproxy- 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

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

但是,如果我想使用an SSLSocket怎么办?似乎shutdownOutput/Input那里不支持这些方法。这是我得到的例外。

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, */}

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

我的问题是:

  1. 如果shutdownInput()不受支持,我怎么会-1从那里得到答案SSLSocket
  2. 对于我的痛苦,有更好的解决方案吗?我认为没有什么理智的,因为read当另一个线程将他标记为“我完成”时,其中一个线程可能已经在阻塞方法中。我看到的将他踢出阻塞线程的唯一方法是关闭套接字并引发“ closed socket”异常

(您可能猜想使用多线程消息传递和异步数据读取的某种不合时宜的组合,以便向另一个线程发出信号,表明您的工作已经完成,但这太可怕了,我担心IntelliJ的风格警察会在我后面追随我的想法它…)

澄清:
我知道,shutdownInputshutdownOutput没有什么意义,因为你不能有一个半双工TLS连接,按规格。但是考虑到这一点,如何在不出现异常的情况下终止Java中的TLS连接?

拜托,不要告诉我shutdownIputTLS没有意义。我知道, 这不是我要的
。我在问我还能用什么才能SSLSocket正确关闭(因此标题为“正确关闭SSLSocket”。


问题答案:

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

不能关闭SSL /
TLS套接字的一半连接,以使其符合TLS协议(如关闭警报部分所述)。

客户端和服务器必须共享有关连接即将结束的知识,以避免被截断攻击。任何一方均可发起结束消息的交换。

close_notify此消息通知收件人,发件人将不再通过此连接发送任何消息。如果在没有适当的close_notify消息的情况下终止任何连接,并且会话级别等于警告,则该会话将无法恢复。

任何一方都可以通过发送close_notify警报来发起关闭。关闭警报后收到的任何数据都将被忽略。

要求各方在关闭连接的写端之前发送close_notify警报。要求另一方以自己的close_notify警报响应并立即关闭连接,并丢弃所有未决的写入。关闭的发起方不需要在关闭连接的读取端之前等待响应的close_notify警报。

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

修复非常简单:仅close()SSLSockets上使用:它不仅会像普通TCP套接字那样关闭连接,而且会根据SSL / TLS规范进行整洁的连接。

编辑附加说明

简短的答案仍然是:use close(),您可能还需要从SSL / TLS顶部的协议中找出合适的时机。

(为澄清起见,您实际上想做的是有效的“中间人”(MITM)代理。您需要将客户端配置为信任代理的证书,就像它是服务器一样证书(如果要透明地进行的话)。HTTPS连接如何通过HTTP代理工作是一个不同的问题。)

在您对其他相关问题的一些评论中,我得到的印象是,您认为不返回-1就是“破坏了TLS协议”。这实际上与协议无关,而是与API有关:将编程结构(类,方法,函数等)提供给TLS堆栈的(程序员)用户以使用TLS的方式。SSLSocketAPI只是API的一部分,以使程序员能够以类似于plain的方式使用SSL
/ TLS
Socket。TLS规范根本不涉及套接字。TLS(及其前身,SSL)的构建旨在提供这种抽象,但归根结底SSLSocket就是:抽象。符合OOP设计原则,SSLSocket继承Socket并尝试尽可能地坚持行为Socket。但是,由于SSL
/ TLS的工作方式(并且SSLSocket有效地位于normal之上Socket),因此无法精确映射所有功能。

特别是,从普通TCP套接字到SSL /
TLS套接字的过渡区域不能完全透明地建模。在设计SSLSocket类时,必须做出一些任意选择(例如,握手方式):这是在(a)不向SSLSocket用户暴露过多的TLS特殊性,(b)使TLS机制起作用和((
c)将所有这些映射到超类(Socket)提供的现有接口。这些选择不可避免地会对的功能产生一些影响SSLSocket,这就是其Javadoc
API页面包含相对大量文本的原因。
SSLSocket.close()就API而言,必须遵守的描述Socket.close()。在InputStream/
OutputStream从所获得的SSLSocket不同于底层平原的Socket那个(除非您已将其显式转换),否则可能看不到。

SSLSocket被设计为尽可能地像平原一样可用Socket,并且InputStream您获得的设计被设计为与基于文件的输入流尽可能接近。顺便说一下,这不是特定于Java的。甚至C语言中的Unix套接字也与文件描述符和一起使用read()。只要您知道它们的局限性,这些抽象就很方便并且在大多数时候都可以使用。

当涉及到时read(),即使在C语言中,的概念也EOF来自文件结尾终止,但实际上并没有处理文件。TCP套接字的问题在于,虽然您可以检测到远程方在发送时选择了关闭连接,但是FIN您却无法检测到它没有发送任何内容(如果没有发送)。从套接字读取任何内容时,无法分辨无效连接和断开连接之间的区别。因此,在进行网络编程时,只能依靠-1InputStreamif
读取,但您绝不能完全依靠它来进行读取,否则最终可能会遇到未发布的无效连接。一方正确地关闭半个TCP连接是“礼貌”的(例如,远程方读取-1InputStream或类似的内容(如Java中的有序与异常连接发行版中所述),仅处理这种情况是不够的。这就是为什么TCP之上的协议通常旨在指示何时完成发送数据的原因:例如,SMTP发送QUIT(并具有特定的终止符),HTTP 1.1使用Content- Length或分块编码。

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

通常,对于TCP,您应该在应用协议级别上知道何时停止发送和接收数据(以避免未释放的连接和/或依赖超时错误)。TLS规范中的这句话使TLS成为一个更好的要求,它是对TLS的一个更强的要求:“
要求对方以自己的close_notify警报响应并关闭连接,并立即丢弃所有未决的写入。
”必须通过应用程序协议以某种方式进行同步,否则远程方可能仍然需要写一些东西(尤其是如果您允许使用流水线式的请求/响应)。

如果您要编写的代理用于拦截HTTPS连接(作为MITM),则可以调查请求的内容。即使HTTP请求是通过管道传递的,您也可以计算目标服务器发送的响应数(并通过Content-
Length或块定界符期望它们何时结束)。

但是,您将无法拥有一个完全透明的通用TLS“拦截器”。TLS旨在确保两方之间的传输安全,并且中间没有一方。虽然大多数实现该保护的机制(避免使用MITM)都是通过服务器证书来完成的,但其他一些TLS机制也会成为障碍(关闭警报就是其中之一)。请记住,您实际上是在创建两个不同的TLS连接,考虑到TLS的目的,它们很难合并为一个事实,这不足为奇。

SSLSocket.close()是关闭的正确方法SSLSocket,但是应用程序协议也将帮助您确定何时合适。



 类似资料:
  • 我正在处理一个需要使用jFileChooser Swing窗口的项目。当单击“取消”或“打开”时,窗口不会关闭。在查看了StackOverflow上的许多文章以及JFileChooser教程和文档之后,我不知道是什么导致了这个重复的问题。 我正在与NetBeans一起使用它的swing编辑器。我也在Eclipse中尝试了该程序作为测试,并收到了相同的结果。

  • 问题内容: 我正在使用Play编写一个部署在Tomcat中的webapp。因为该应用程序不会处理大量数据,所以我在Hibernate中使用默认的H2数据库。当我想部署新版本的应用程序时,我关闭了tomcat,擦除了旧的webapp和WAR,添加了新的WAR,然后开始备份。 直到几天前,当我添加数据库组件时,它一直有效。现在,我经常无法重新部署该应用程序。当我删除旧目录时,它将使用以下结构自动重新生

  • 我错过了什么关于正确关闭生产者和消费者的事情吗?

  • 问题内容: 我有全部传播异常的方法,然后在一个地方处理,但是我意识到了一些事情。 假设我有这样的方法 我的问题是,如果doSometing()方法引发异常,该语句将不会关闭,但我不想在那里处理异常。尝试并捕获只会抛出异常并最终关闭语句的正确方法吗? 问题答案:

  • 问题内容: 在清理一些代码时,FindBugs向我介绍了一些使用Connection,CallableStatement和ResultSet对象的JDBC代码。这是该代码的一个片段: FindBugs指出这些应该在finally块内。我开始重构我的代码来做到这一点,我开始想知道如何在finally块中处理代码。 Connection对象的CallableStatement的创建可能会引发异常,而我

  • 问题内容: 其中哪一个是正确的? 问题答案: 工作正常,并正确关闭标签。最好为视障人士添加属性。