关闭SSL而不关闭底层套接字?

5
如何在不关闭底层套接字的情况下优雅地关闭Java SSL会话?
场景是Java客户端连接到(非Java)服务器,建立SSL并安全地向服务器发送凭据(用户名和密码)。服务器使用这些凭据设置在此环境下运行的环境,生成此环境中的新进程并将套接字句柄传递给它,但问题在于此新进程无法重用现有的SSL连接(也无法使用SSL会话恢复)...因此,想法是在生成此新进程之前关闭SSL,然后从头开始重新协商与新进程的SSL会话。
问题在于Java的SSLSocket'sclose()方法除了关闭SSL会话(通过发送close_notify警报)外,还关闭套接字。似乎没有OpenSSL的SSL_shutdown()函数的等效函数,该函数允许保持底层套接字处于打开状态。

我已经尝试了几种方法来解决这个问题:

  1. 使用 SSLSocket.startHandshake() 第二次,但它会自动尝试恢复现有的缓存 SSL 会话(由于生成的服务器进程不知道此会话,因此失败),虽然有一种方法可以 强制恢复 SSL 会话或死亡尝试,但没有一种方法可以使所有缓存的会话无效或禁用使用缓存的会话。

  2. 使用 SSLSocketFactory.createSocket() 在现有套接字上创建一个 SSLSocket,并将 autoClose 设置为 false。这不会阻止 close() 方法关闭基础套接字,我怀疑 autoClose 参数仅在初始握手失败时防止套接字关闭。

  3. 在现有套接字上创建一个 SSLSocket(如上所述),然后创建第二个 SSLSocket 与生成的进程进行 SSL 握手(来自新的 SSLContext)。这会失败,因为当服务器发送 close_notify 警报(在生成子进程之前),Java 关闭套接字。

我听说过SSLEngine,但我也读过(虽然当前的来源逃脱了我)使用它编写正确的SSL/TCP/IP实现是非常痛苦的,而且当我只需要一个不调用super.Close()SSLSocket版本时,它似乎过度了。
但是,根据javadoc的描述,SSLSocket甚至没有覆盖close(),因此我不确定它如何钩住close()方法,因为似乎没有支持在Socket上注册关闭监听器的任何支持。
公司政策规定不能使用第三方加密库,因此无法求助于类似Bouncy Castle这样的替代SSL实现来解决问题。
如果我们无法让当前的单套接字设计正常工作,那么替代方案是重写客户端和服务器以使用两个独立的套接字(这会在应对拒绝服务和中间人攻击方面变得非常混乱,这难道不是SSL在第一时间要解决的问题吗?)。
欢迎提供任何解决此问题的意见或想法。

你尝试过在后台使用普通的Socket吗?像这样:Socket s = new Socket(HOST, PORT); SSLSocket ssl = ...; ssl.connect(s.getRemoteSocketAddress()); ...?(至少对我来说,它不会关闭“底层”套接字.. 可能是我做错了什么 :) ) - dacwe
据我所知(忽略你代码片段中的...部分),执行ssl.connect()将会创建第二个连接到主机,并且套接字s将不会被用于SSL通信。 - Caspar
2个回答

2

这并不会阻止close()方法关闭底层套接字。

实际上是会的,根据文档。

我怀疑autoClose参数只有在初始握手失败时才能防止套接字关闭。

不是的。它可以防止底层套接字关闭,根据文档。

给我们展示一些代码吧。


我的错误,你是正确的。SSLSocket.close() javadoc 可能没有提到关闭 SSL 通信的任何内容,而且 SSLSocket 在 Eclipse 的轮廓视图中没有显示出 close 方法(可能对于没有源代码附件的编译类不会显示覆盖的方法),但是不知怎么的 SSLSocket.close() 确实重写了 Socket.close() 的功能。 - Caspar
1
SSLSocket是抽象的。实现类重写了close()方法。 - user207421
是的,我同意应该对此进行文档化,并且它会发送close_notify,startHandshake()或第一个I/O会导致客户端发送客户端hello等。事实上,我希望能够看到更多这种类型的文档,即使是对于Sockets,例如Socket.connect()启动SYN序列,SocketChannel.finishConnect()告诉您是否已收到SYN的ACK,shutdownOutput()和close()启动FIN/ACK序列。 - user207421

2

您应该能够使用纯基础套接字(您的#3),因为这是使用Clear Control Channel实现FTP的内容。

请记住,您正在尝试执行的操作会在两个SSL会话之间留下中间人攻击的漏洞,除非您在两者之间携带一些秘密来表示身份验证仍然有效。

  1. 使用网络不良行为(ARP毒化?)成为客户端和服务器之间的代理。
  2. 将数据包双向转发,直到身份验证完成并且SSL会话结束。(任何SSL ALERT都将表示结束,我们不需要解密内容)。
  3. 关闭与客户端的套接字,以便其产生错误。
  4. 使用服务器进行自己的SSL协商。
  5. 继续使用客户端的凭据进行操作。

EJP回答了我的问题,但是因为指出了我没有想到的中间人攻击,所以还是点个赞。 - Caspar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接