如何在Java中阅读SNI扩展?

时间:2016-01-27 10:38:27

标签: java ssl squid tls1.2

我想从TLS Client Hello Message找到主机名。我希望在java完成对透明ssl proxy的握手之前找到主机名。

有没有办法在不编写整个SNI extension逻辑的情况下找到ssl handshake值?

Java是否支持ssl handshake初始内存缓冲区? 我的想法是:

  1. 读取TLS客户端问候消息,解析并找到SNI值
  2. 调用sslSocket.startHandshake(initialBuffer)初始缓冲区将包含TLS客户端hello数据包数据。所以Java可以做握手。
  3. 第二个想法是使用SSLEngine class。但它似乎比需求更多的实现。我假设SSLEngine在我不需要的情况下使用大部分异步。

    第三个想法是实施完整的TLS protocol

    哪种想法更好?

2 个答案:

答案 0 :(得分:1)

SSLSocket和SSLEngine都缺乏(非常难以理解)对服务器连接的正确SNI支持。

我自己遇到了同样的问题,最后写了一个库:TLS Channel。它不仅如此,它实际上是SSLEngine的完全抽象,暴露为ByteChannel。关于SNI,库在创建SSLEngine之前解析第一个字节。然后,用户可以向服务器通道提供功能,根据收到的域名选择SSLContexts。

答案 1 :(得分:1)

该问题的公认答案有些陈旧,并不能真正提供该问题的答案。

初始化KeyManager时应使用自定义SSLContext,诀窍是使用javax.net.ssl.X509ExtendedKeyManager(请注意扩展术语)。这将提供在密钥别名选择过程中公开SSLEngine而不是普通(无用)套接字的可能性。

在SSL握手过程中将调用方法chooseEngineServerAlias(...),以便从KeyStore中选择适当的别名(证书/密钥)。并且SNI信息已已填充,并在SSLEngine中可用。此处的技巧是将SSLSession转换为ExtendedSSLSession(请注意扩展术语),然后转换为getRequestedServerNames()

KeyManager[] km = new KeyManager[]
{
    new X509ExtendedKeyManager()
    {
        public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
        {
            if( engine.getHandshakeSession() instanceof ExtendedSSLSession )
            {
               List<SNIServerName> sni = ((ExtendedSSLSession)engine.getHandshakeSession()).getRequestedServerNames();

                // select the proper certificate alias based on SNI here
            }
        }
    }
}

SSLContext context = SSLContext.getInstance("TLS");
context.init(km, null, null);