4

基于 JSSE 示例,我试图在服务器端获取 TLS 参数“服务器名称指示”(SNI)的值 - 没有成功。我确定该值是由客户端发送的,因为我使用了显示该值的网络嗅探器(Wireshark)。

但是当我使用以下代码片段时,服务器名称参数列表为空(同时显示了“协议”):

public void connectionAccepted(Socket socket) 
{ 
  System.out.println("Connection accepted!"); 
  try { 
    /* get SNI parameter */ 
    SSLSocket sslSocket = (SSLSocket)socket; 
    SSLParameters sslParams = sslSocket.getSSLParameters(); 

    List<SNIServerName> serverNames = sslParams.getServerNames(); 
    for(SNIServerName item : serverNames){ 
      System.out.println("SNI: " + item.toString()); 
    } 

    String[] protocols = sslParams.getProtocols(); 
    for(String item : protocols) { 
      System.out.println("Protocols: " + item.toString()); 
    } 
  } catch (Exception e) { 
    e.printStackTrace(); 
  } 
} 
4

3 回答 3

2

无论出于何种原因,Java SNI 实现显然没有在服务器端提供实际的主机名。相反,文档 [1](在“基于 SSLSocket 的虚拟服务器调度程序”下)建议手动解析初始 SSL 数据包并从中提取服务器名称。

您还可以使用 SNIMatcher [2] 来要求客户端提供特定名称;如果匹配器不匹配,则客户端在 SSL 层收到错误。

[1] http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SNIExamples [2] http://docs.oracle.com/javase/8/docs /api/javax/net/ssl/SNIMatcher.html

更新:我编写了一个自定义 SNI 解析器,它的工作原理如下:客户端发送 SNI 作为 SSL ClientHello 消息的一部分,这是作为设置 SSL 连接的一部分发送的第一条消息。我的实现首先对其进行解析,然后使用与请求的主机名匹配的正确服务器端证书设置 SSLEngine。

于 2015-01-01T21:08:54.240 回答
1

该功能(非常莫名其妙)不能通过 SSLSocket(也不是较新的 SSLEngine)开箱即用。

我自己也遇到了同样的问题,最后写了一个开源库:TLS Channel。该库的范围实际上更大:它是 SSLEngine 的完整抽象(很难直接使用),将其公开为 ByteChannel。

关于 SNI,该库在创建 SSLEngine 之前解析第一个字节。然后,用户可以向服务器通道提供一个函数,以根据接收到的域名选择 SSLContexts。

于 2017-07-22T14:27:31.213 回答
0

我会尝试实现我自己的 SNIMatcher (扩展抽象的)并在他们访问 mymatcher.matches(...) 方法时拦截并保存 sniservername (另一个抽象)。

如果sniservername instanceof snihostname,则snihostname.getAsciiName()。否则,您将不得不研究如何读取 sniservername.getEncoded() byte[]。

一旦退出握手和连接,我会检查我的 snimatcher 上保留的主机名。当您手头有 sniservername 对象时,您可能必须取出字符串主机名,因为谁知道(您会!),它可能在 snimatcher.matches(...) 调用后立即变为无效。

于 2016-02-06T20:55:55.563 回答