【问题标题】:Shutting down SSL without closing underlying socket?在不关闭底层套接字的情况下关闭 SSL?
【发布时间】:2011-05-20 19:44:30
【问题描述】:

如何在不关闭底层套接字的情况下优雅地关闭 Java SSL 会话?

该场景是 Java 客户端连接到(非 Java)服务器,设置 SSL 并将凭据(用户名和密码)安全地发送到服务器。服务器设置在这些凭据下运行的环境,在此环境中生成一个新进程并将套接字句柄传递给它,但问题是这个新进程不能重用现有的 SSL 连接(并且不能使用 SSL 会话resuming)...所以想法是在这个新进程产生之前关闭 SSL,然后从头开始与新进程重新协商 SSL 会话。

问题在于 Java 的 SSLSocket's close() 方法除了关闭 SSL 会话(通过 sending the close_notify alert)之外还关闭了套接字。似乎没有等同于 OpenSSL 的 SSL_shutdown() 函数,它允许让底层套接字保持打开状态。

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

  1. 第二次使用SSLSocket.startHandshake(),但这会自动尝试恢复现有的缓存 SSL 会话(失败,因为生成的服务器进程不知道此会话),并且,虽然有一种方法可以force resuming SSL sessions or die trying,没有办法使所有缓存会话无效或禁用缓存会话。

  2. 使用SSLSocketFactory.createSocket() 在我现有套接字的顶部创建SSLSocket,并将autoClose 设置为false。这并不能阻止close() 方法关闭底层套接字,而且我怀疑autoClose 参数只是防止在初始握手失败时关闭套接字。

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

我听说过SSLEngine,但我也读到过(尽管我目前不知道来源)使用它来编写一个基于 TCP/IP 的 SSL 的正确实现是一项艰巨的工作,而且看起来像当我所需要的只是拥有一个close() 不调用super.Close()SSLSocket 版本时,我就有点矫枉过正了。

但是,SSLSocket 似乎甚至没有覆盖 close()(根据 javadoc),所以我不确定当似乎不支持注册关闭时它如何挂钩 close() 方法Socket上的听众。

公司政策规定不能使用第三方加密库,因此我无法求助于诸如Bouncy Castle 的替代 SSL 实现来解决问题。

如果我们不能让当前的 1-socket 设计工作,另一种选择是重写客户端和服务器以使用 2 个单独的套接字(这在必须应对拒绝服务和人入方面变得相当混乱-the-middle 攻击,这不就是 SSL 最初应该用于的吗?)。

欢迎对解决此问题的方法提出任何意见或想法。

【问题讨论】:

  • 您是否尝试在后台使用普通的 Socket?类似:Socket s = new Socket(HOST, PORT); SSLSocket ssl = ...; ssl.connect(s.getRemoteSocketAddress()); ...? (对我来说,至少它不会关闭“底层”套接字。可能我做错了什么 :))
  • dacwe,据我所知(忽略您的 sn-p 的 ... 部分),执行 ssl.connect() 将创建与主机的第二个连接,并且套接字 @987654346 @ 根本不会用于 SSL 通信。

标签: java sockets ssl


【解决方案1】:

这不会停止 close() 方法 从关闭底层套接字

是的。根据文档。

我怀疑 autoClose 参数只阻止套接字 从最初的时候被关闭 握手失败。

没有。它可以防止关闭底层套接字。根据文档。

给我们看一些代码。

【讨论】:

  • 我的错,你是对的。 SSLSocket.close() javadoc 可能不会说任何关于关闭 SSL 通信的内容,并且 SSLSocket 的 Eclipse 大纲视图没有显示 close 方法(可能它没有显示没有源附件的已编译类的覆盖方法),但不知何故SSLSocket.close() 确实覆盖了 Socket.close() 的功能。
  • SSLSocket 是抽象的。实现类覆盖 close()。
  • 是的。我同意它应该被记录并且它发送一个 close_notify,并且 startHandshake() 或第一个 I/O 导致客户端发送一个客户端问候等。事实上,通常希望看到更多这种文档,即使对于套接字,例如Socket.connect() 启动 SYN 序列,SocketChannel.finishConnect() 告诉你是否收到了对 SYN 的 ACK,shutdownOutput() 和 close() 启动 FIN/ACK 序列。
【解决方案2】:

您应该能够使用普通的底层套接字(您的#3),因为这是使用 Clear Control Channel 的 FTP 实现所做的。

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

  1. 利用网络顽皮(ARP 中毒?)成为客户端和服务器之间的代理。
  2. 双向转发数据包,直到身份验证完成并且 SSL 会话结束。 (任何 SSL ALERT 都会发出结束信号,我们不需要解密内容)。
  3. 关闭客户端的套接字,这样它就会出错。
  4. 我自己与服务器进行 SSL 协商。
  5. 继续使用客户端的凭据进行操作。

【讨论】:

  • EJP 更好地回答了我的问题,但是 +1 指出了我没有想到的 MITM 攻击。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-18
  • 2013-10-04
  • 2019-06-10
  • 1970-01-01
  • 2019-03-21
  • 2020-01-10
  • 1970-01-01
相关资源
最近更新 更多