【发布时间】: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() 函数,它允许让底层套接字保持打开状态。
我已经尝试了一些方法来解决这个问题:
第二次使用SSLSocket.startHandshake(),但这会自动尝试恢复现有的缓存 SSL 会话(失败,因为生成的服务器进程不知道此会话),并且,虽然有一种方法可以force resuming SSL sessions or die trying,没有办法使所有缓存会话无效或禁用缓存会话。
使用SSLSocketFactory.createSocket() 在我现有套接字的顶部创建
SSLSocket,并将autoClose设置为false。这并不能阻止close()方法关闭底层套接字,而且我怀疑autoClose参数只是防止在初始握手失败时关闭套接字。在现有套接字上创建
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 通信。