我正在尝试在我当前的项目中使用SNI模式,在该项目中,客户端会启动与服务器的SSL连接。我想让我的服务器知道客户端打算连接到哪个主机/服务器名称。该服务器名称由客户端提供。我有一个ExtendedSSLSession对象,但是由于我使用的是Java 7,因此无法使用getRequestedServerNames()方法(在Java 8中引入)来实现我要寻找的确切功能。我目前的情况使我无法升级Java版本。
我想问社区,在使用Java 7时是否有变通办法来获取服务器名称?
我将不胜感激。
谢谢!
最佳答案
好吧,JDK 7和8是开源的,因此原则上您可以向后移植必要的新代码并构建自己的版本(称为7.5?)-至少在您的要求仅停留在java7 API而非官方上的情况下java7 JRE。但是,如果您谈论的是大量的运行时系统或应用程序,尤其是对业务至关重要的运行时系统或应用程序,则可能难以提供支持。
作为一种解决方法,我认为-但尚未弄清所有细节-您可以使用SSLSocketFactory.createSocket(Socket,InputStream,boolean)的等效项(也是java8中的新增项),并出于相同的原因根据文档“在底层”。具体来说,如果您使用的是“简单” SSLServerSocket
或SSLSocket(client=false)
接口:
自己接受TCP连接(显式或替代SSLServerSocket)
阅读ClientHello并解析您想要的片段,即SNI扩展
为Socket
创建一个包装器,该包装器可以记住此数据,并且还具有类似byte[]
的内容,其中包含ClientHello,并且在调用read
时,它首先返回已读取的ClientHello,然后从实际套接字读取,而和其他操作照常进行
在此包装器套接字“上方”创建服务器端write
并照常使用
当您想获取SNI时(例如在SSLSocket
回调中),而不是从X509KeyManager
查看ExtendedSSLSession
,而是将[SSL]Socket
下放到wrapper-Socket并在那里查看。 (您可以在Socket
上设置此条件,因此当您进入java8时它会自行关闭。)
如果您使用的是socket instanceof MyWrapper
界面,则已经在管理连接和I / O,因此只需将2、3A和5:进行输入,然后将第一个输入(ClientHello)馈送到SSLEngine
进行解析即可并用包装纸记住它,当您要查看包装纸时,请使用包装纸。