正确关闭SSLSocket [英] Properly closing SSLSocket

查看:1808
本文介绍了正确关闭SSLSocket的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Java中实现SSL代理。我基本上打开两个套接字 browser-proxy proxy-server ,然后运行两个写入<$​​ c的线程$ c> proxy-server 他们从 browser-proxy 中读取的内容,反之亦然。每个线程如下所示:

I want to implement an SSL proxy in Java. I basically open two sockets browser-proxy,proxy-server, and run two threads which would write to proxy-server what they read from browser-proxy, and vice versa. Each thread looks like this:

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

每个线程只关闭输入套接字,所以最终两个套接字都关闭了。

Each thread will only close the input socket, so that eventually both sockets are closed.

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

But what do I do if I want to use an SSLSocket? It seems that the shutdownOutput/Input methods are not supported there. Here's the exception I get.

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

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

I must catch and ignore end of socket exception every time my socket ends.

我的问题是:


  1. 如果 shutdownInput()不受支持,我怎么得到 -1 SSLSocket 回答。

  2. 对我的痛苦有更好的解决方案吗?我不认为有任何理智,因为当另一个线程标记他我已经完成时,其中一个线程可能已经在阻塞读取方法中。而且我看到将他踢出阻塞线程的唯一方法是关闭套接字并引发关闭套接字异常。

  1. If shutdownInput() is unsupported, how come I'm getting a -1 answer from an SSLSocket.
  2. Is there a better solution for my agony? I don't think there's anything sane, since one of the thread might be already in the blocking read method when the other thread marks him "I'm done". And the only way I see to kick him out of the blocking thread is by closing the socket and raising a "closed socket" exception.

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

(You could I guess use some unholy combination of multithread message passing and asynchronous data reading in order to signal the other thread your work is done, but this is so horrible that I'm afraid IntelliJ's style cop will come after me just for thinking about it...)

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

Clarification: I know that shutdownInput and shutdownOutput doesn't make sense, since you cannot have a half-duplex TLS connection, per spec. But given that, how can I end a TLS connection in Java without getting an exception?

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

Please, don't tell me shutdownIput doesn't make sense for TLS. I know that, this is not what I'm asking. I'm asking what else can I use, in order to close SSLSocket properly (hence the title, "Properly closing SSLSocket".

推荐答案

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

无法关闭SSL / TLS套接字的一半连接以符合TLS协议,如关闭提醒部分。

It's not possible to close half of the connection for SSL/TLS sockets to comply with the TLS protocol, as specified in the section on closure alerts.


客户端和服务器必须
分享知道连接是
结束以避免截断
攻击。任何一方都可以发起
交换结束消息。

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 此消息通知收件人发件人不会
发送更多信息在这个连接上。如果在没有正确等级为警告的close_notify消息的情况下终止任何连接,会话将变得不可恢复。

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.

任何一方都可以通过发送close_notify警报来启动关闭

关闭
提醒后收到的任何数据都会被忽略。

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

每一方都需要在关闭$ b之前发送
close_notify提醒$ b连接的写入端。
是必需的,另一方
以自己的
的close_notify警报响应并立即关闭
连接,丢弃任何
挂起的写入。在关闭
连接的读取端之前,响应close_notify
警报等待
的发起人不需要

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 数据包,否则,它将被视为截断攻击。

Although you may want to close only half of the connection (input or output via shutdownInput()/shutdownOutput()), the underlying TLS layer still needs to send this close_notify packet before really shutting down the communication, otherwise, it will be considered as a truncation attack.

修复非常简单:仅在<$上使用 close() c $ c> SSLSocket s:它不仅像普通的TCP套接字那样关闭连接,而是根据SSL / TLS规范干净地完成连接。

The fix is quite simple: only use close() on SSLSockets: it doesn't just close the connection as it would for normal TCP sockets, but it does it cleanly according to the SSL/TLS specification.

编辑其他说明

简短的回答仍然是:使用 close(),您可能还需要从专业人员那里了解何时适合这样做在SSL / TLS之上的协议。

The short answer is still: use close(), you may also need to find out when it's appropriate to do so from the protocol on top of SSL/TLS.

(只是为了澄清,你要做的是实际上是中间人(MITM)代理。您需要将客户端配置为信任代理的证书,如果这是透明的,则可能就像服务器证书一样。 HTTPS连接如何通过HTTP代理工作是不同的问题。)

(Just to clarify, what you're trying to do is effectively a "Man-In-The-Middle" (MITM) proxy. You would need the client to be configured to trust the proxy's certificate, possibly as if it was the server certificate if this is meant to happen transparently. How HTTPS connections work through an HTTP proxy is a different question.)

您对其他相关问题,我的印象是你认为没有返回 -1 是打破TLS协议。这不是关于协议,而是关于API:编程结构(类,方法,函数,...)的方式被提供给TLS堆栈的(程序员)用户以便能够使用TLS。 SSLSocket 只是API的一部分,为程序员提供了以类似于普通 Socket 。 TLS规范根本不讨论套接字。
虽然构建TLS(及其前身SSL)的目的是为了能够提供这种抽象,但在一天结束时 SSLSocket 是就是这样:抽象。
根据OOP设计原则, SSLSocket 继承 Socket 并尝试尽可能贴近 Socket 的行为。但是,由于SSL / TLS的工作方式(并且因为 SSLSocket 实际上位于正常的 Socket 之上) ,没有所有功能的精确映射。

In some of your comments to your other related question, I got the impression that you thought that not returning -1 was "breaking the TLS protocol". This isn't really about the protocol, but about the API: the way programming structures (classes, methods, functions, ...) are provided to (programmer) user of the TLS stack to be able to use of TLS. SSLSocket is merely part of an API to provide programmers with the ability to use SSL/TLS in a way similar to plain Socket. The TLS specification doesn't talk about sockets at all. While TLS (and its precessor, SSL) were built with the aim to make it possible to provide this sort of abstraction, at the end of the day SSLSocket is just that: an abstraction. In line with the OOP design principles, SSLSocket inherits Socket and tries to stick as closely as possible to the behaviour of Socket. However, because of the way SSL/TLS works (and because an SSLSocket effectively sits on top of a normal Socket), there cannot be an exact mapping of all features.

特别是,从普通TCP套接字到SSL / TLS套接字的转换区域无法完全建模透明。
在设计 SSLSocket 类时,必须做出一些任意选择(例如如何进行握手):
它是否是(a)之间的妥协暴露了太多TLS对 SSLSocket 用户的特殊性,(b)使TLS机制起作用,(c)将所有这些映射到超类提供的现有接口( 插槽)。
这些选择不可避免地对 SSLSocket 的功能产生一些影响,这就是为什么它的 Javadoc API页面具有相对大量的文本。
SSLSocket.close(),就API而言,必须遵守 Socket.close()
SSLSocket InputStream / OutputStream >与基础普通 Socket 中的那个不同,(除非你明确地转换了它)你可能看不到。

In particular, the area of transition from a normal TCP socket to an SSL/TLS socket can't quite be modelled transparently. Some arbitrary choices (e.g. how the handshake takes place) had to be made when designing the SSLSocket class: it's a compromise between not (a) exposing too much of the specificity of TLS to the SSLSocket user, (b) making the TLS mechanism work and (c) mapping all this to the existing interface provided by the superclass (Socket). These choices inevitably have some impact on the functionality of the SSLSocket, and that's why its Javadoc API page has a relatively large amount of text. SSLSocket.close(), in terms of API, has to abide by the description of Socket.close(). The InputStream/OutputStream obtained from the SSLSocket are not the same as the one from the underlying plain Socket,which (unless you've converted it explicitly) you might not see.

SSLSocket 旨在尽可能接近,就像它是一个简单的 Socket ,以及<$ c你得到的$ c> InputStream 的设计与基于文件的输入流的行为尽可能接近。
顺便说一下,这不是特定于Java的。即使C语言中的Unix套接字也与文件描述符一起使用, read()
这些抽象很方便,大部分时间都可以工作,只要你知道它们的局限性。

SSLSocket is designed to be usable as closely as possibly as if it was a plain Socket, and the InputStream you get is designed to behave as closely as possibly to a file-based input stream. This isn't specific to Java, by the way. Even Unix sockets in C are used with a file descriptor and read(). These abstractions are convenient and work most of the time, so long as you know what their limitations are.

当谈到 read(),即使在C中, EOF 的概念来自文件结束终止,但你实际上并没有处理文件。
TCP套接字的问题在于,虽然您可以检测到远程方在发送 FIN 时选择关闭连接的时间,但您无法检测到它没有,如果它没有发送任何东西。
当从套接字中读取任何内容时,无法区分非活动连接和断开连接之间的区别。
因此,当谈到网络编程时,依赖于从 InputStream 中读取 -1 但是你永远不会完全依赖它,否则你可能最终得到未发布的,死的连接。
虽然对于一方正确关闭半TCP连接是礼貌的(例如远程方在其<$ c $上读取 -1 ) c> InputStream 或等效的东西,如有序与Java中的流量连接释放),处理这种情况是不够的。
这就是为什么TCP之上的协议通常是旨在说明他们何时完成发送数据:例如,
SMTP发送 QUIT (和有特定的终止符),HTTP 1.1使用 Content-Length 或分块编码。

When it comes to read(), even in C, the notion of EOF comes from an end-of-file termination, but you're not actually dealing with files. The problem with TCP sockets is that, while you can detect when the remote party has chosen to close the connection when it sends FIN, you can't detect it hasn't, if it doesn't send anything. When reading nothing from a socket, there is no way to tell the difference between an inactive and a broken connection. As such, when it comes to network programming, relying on reading -1 from the InputStream if fine, but you never rely solely on that, otherwise you may end up with unreleased, dead connections. While it's "polite" for a party to close the half TCP connection properly (in a way such as the remote party reads -1 on its InputStream or something equivalent, as described in Orderly Versus Abortive Connection Release in Java), handling this case isn't sufficient. This is why protocols on top of TCP are generally designed to give an indication as to when they're done sending data: for example, SMTP sends QUIT (and has specific terminators), HTTP 1.1 uses Content-Length or chunked encoding.

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

(By the way, if you're not satisfied with the abstraction provided by SSLSocket, try to use SSLEngine directly. It's likely to be harder, and it won't change what the TLS specification says about sending close_notify.)

一般情况下,对于TCP,你应该知道,在应用程序协议级别,何时停止发送和接收数据(以避免未发布的连接和/或依赖于超时错误)。
TLS规范中的这句话使得这种通常良好的做法在TLS方面有更强的要求:
需要另一方以自己的close_notify警报响应并关闭连接立即丢弃任何挂起的写入。
您将不得不通过应用程序协议以某种方式进行同步,或者远程方可能仍然需要编写内容(特别是如果您允许流水线请求/响应)。

In general with TCP, you should know, at the application protocol level, when to stop sending and receiving data (to avoid unreleased connections and/or relying on timeout errors). This sentence in the TLS specification makes this generally good practice a stronger requirement when it comes to TLS: "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." You will have to synchronize somehow, via the application protocol, or the remote party may still have something to write (especially if you're allowing pipelined requests/responses).

如果您编写的代理是用于拦截HTTPS连接(作为MITM),您可以查看请求的内容。即使HTTP请求是流水线的,您也可以计算目标服务器
发送的响应数(当它们结束时,通过Content-Length或chunks delimiters计算)。

If the proxy you're writing is for intercepting HTTPS connections (as a MITM), you can look into the content of the requests. Even if the HTTP requests are pipelined, you can count the number of responses sent by the target server (and expect when they end, via Content-Length or chunks delimiters).

但是,你将无法拥有纯粹透明的通用TLS拦截器。
TLS旨在确保双方之间的运输,而不是中间的一方。
虽然实现这种保护(避免使用MITM)的大多数机制都是通过服务器证书完成的,但其他一些TLS机制会受到阻碍(关闭警报就是其中之一)。
请记住,您正在有效地创建两个不同的TLS连接,考虑到TLS的目的,它们很难合并,因为一个人应该不会感到惊讶。

However, you won't be able to have a purely transparent, generic TLS "interceptor". TLS is designed to secure the transport between two parties and not to have one in the middle. While most of the mechanism to achieve that protection (avoiding a MITM) is done via the server certificate, some other TLS mechanisms will get in the way (closure alert is one of them). Remember that you're effectively creating two distinct TLS connections, the fact that they're hard to merge as one shouldn't be surprising, considering the purpose of TLS.

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

SSLSocket.close() is the right way to close an SSLSocket, but the application protocol will also help you determine when it's appropriate to do so.

这篇关于正确关闭SSLSocket的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆