打开和关闭SSLSocket没有写入任何数据时出错

简单的服务器

listen = getServer(); Logger.getAnonymousLogger().info("Listening to "+listen.toString()); SSLSocket client = (SSLSocket)listen.accept(); // adding this line fixes everything - client.write(42); client.close(); 

和一个简单的客户

 SocketFactory sockMaker = SSLSocketFactory.getDefault(); Socket server = sockMaker.createSocket("localhost", 1443); int retval = server.getInputStream().read(); assert retval == -1; server.close(); 

如果我没有向SSL Socket写入任何内容,则会在客户端抛出exception:

 Exception in thread "main" javax.net.ssl.SSLException:\ Received close_notify during handshake at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:190) 

我不明白为什么会这样。 SSL / TLS规范是否要求您将内容写入套接字?

查看完整示例 。

您不必将任何内容写入套接字,但如果您立即将其关闭,它将生成close_notify警报(尽管它被称为“警报”,它是关闭TLS / SSL套接字的正常方式的一部分) 。

此外,SSL / TLS套接字被设计为“几乎”像普通的TCP套接字一样,但由于SSL / TLS的工作方式,它们没有(也不能)。 特别是,在SSL / TLS连接开始时,会发生SSL / TLS握手,在发送任何应用程序数据之前,它会涉及来自每一方的大量读/写操作。

SSLSocket的文档说:

可以通过以下三种方式之一启动此连接上的初始握手:

  • 调用startHandshake明确地开始握手,或者
  • 任何在此套接字上读取或写入应用程序数据的尝试都会导致隐式握手,或者
  • 如果没有当前有效的会话,则调用getSession尝试建立会话,并完成隐式握手。

实际上,示例中客户端的getInputStream().read()会启动握手,这会导致服务器继续执行accept() 并在其一侧执行握手 。 但是,由于您在服务器端关闭它(通常,但是立即),您甚至不会让握手完成任何时间。 因此,在握手期间发送close_notify ,这会导致您获得exception。 如果您尝试从服务器端读取或写入,则握手至少已完成。

编辑 :关注@ EJP的评论,我应该澄清我的意思:

  • 客户端的createSocket("localhost", 1443)建立连接,服务器通过accept()接受它。
  • getInputStream().read()客户端的getInputStream().read()使它启动握手。 因此,它将ClientHello TLS消息发送到服务器。
  • 因为服务器在接受套接字后直接使用close() ,所以它会发送close_notify警报。 因为服务器还没有开始读/写,所以它没有启动握手(因此没有完成它)。

请注意, SSLServerSocket实现的ServerSocket.accept()的目的是创建一个SSLSocket ,不一定要对它做任何事情。 SSLServerSocket配置它,但继续握手超出范围。 一方面,它可能听起来像使SSLSocket像普通的TCP套接字一样更透明; 另一方面,它意味着从底层TCP流中读取,因此它会产生副作用。 我没有尝试过,但SSLServerSocket创建的SSLSocket可能仍然可以配置到客户端套接字中 。 毕竟, RFC 2246术语表说: “客户端:启动到服务器的TLS连接的应用程序实体。这可能会也可能不会暗示客户端启动了底层传输连接。” 这肯定会对透明度以及何时从API的角度进行握手产生影响。

(编写映射到普通TCP套接字API的SSL / TLS套接字的API是一项棘手的工作,而Java在这方面的工作并不算太糟糕。真正的“乐趣”始于使用SSLEngine和NIO通道的异步TLS考虑到任何一方都可以随时发起新的握手,它会变得更好:就TLS而言,对于上述级别的影响是不确定的,这可能导致尴尬的问题 。)

情况无效。 您正在尝试读取未发送的数据。 这是应用程序协议错误。 布鲁诺的答案中的所有陈述也适用。 客户正在尝试握手; 服务器正在尝试关闭连接。 可以说服务器关闭可以启动握手,如果它还没有完成,但事实并非如此。

正如您所指出的,另一种解决方法是在任一端调用startHandshake()。