Search code examples
javassljava-8sni

TLS extension "Server Name Indication" (SNI): value not available on server side


Based on the JSSE examples, I'm trying to get the value of the TLS parameter "Server Name Indication" (SNI) on the server side - without success. I'm sure that the value is sent by the client since I used a network sniffer (Wireshark) that shows the value.

But when I use the following code fragment, the list of server name parameters is empty (while the "protocols" are shown):

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(); 
  } 
} 

Solution

  • For whatever reason, the Java SNI implementation apparently does not provide the actual host name on the server side. Instead, the documentation [1] (under "Virtual Server Dispatcher Based on SSLSocket") recommends manually parsing the initial SSL packet and extracting the server name from there.

    You could also use an SNIMatcher [2] to require that the client provides a certain name; if the matcher does not match, then the client gets an error at the SSL layer.

    [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

    Update: I've written a custom SNI parser and it works like this: the client sends the SNI as part of the SSL ClientHello message, which is the first message sent as part of setting up an SSL connection. My implementation first parses that, and then sets up an SSLEngine with the right server-side certificate matching the requested host name.