【问题标题】:SSLProtocolException: handshake alert: unrecognized_name: client-side code workaround?SSLProtocolException:握手警报:unrecognized_name:客户端代码解决方法?
【发布时间】:2014-05-26 13:07:53
【问题描述】:

在检索 XML 数据的自定义 HTTP 客户端中,我收到了 unrecognized_name 错误。不幸的是,类似问题的答案对我不起作用:

  • 我无法通过系统属性关闭 SNI 支持,因为我的代码在我无法控制的应用程序服务器中运行
  • 我无法修复 Web 服务服务器配置(添加 ServerName)

现在,我的代码正在使用 commons httpclient 3.1,但我对替代方案持开放态度。这是我现在正在使用的精简代码:

HttpClient client = new HttpClient();
PostMethod method = new PostMethod(getEndpointUrl());
NameValuePair[] data = {
    new NameValuePair("username", getUsername()),
    new NameValuePair("password", getPassword()),
    new NameValuePair("email", email)
};
method.setRequestBody(data);
client.executeMethod(method);
return method.getResponseBodyAsStream();

我查看了 HttpClient,似乎可以实现类似于https://stackoverflow.com/a/14884941/3676401 中提到的解决方法,但似乎有很多潜在的安全相关代码。

我非常不愿意将大量代码转储到一个简单的客户端中。还有其他建议吗?

【问题讨论】:

    标签: java apache-httpclient-4.x sni


    【解决方案1】:

    您正在连接到 SNI 配置错误的服务器。如果您无法控制该服务器并且无法修复它,那么您将不得不禁用 SNI。

    1) 如果您希望服务器在某个时候得到修复,请使用运行时标志暂时禁用 SNI,直到服务器修复为止。这样,您可以稍后删除此标志,甚至无需在服务器修复后重新编译。

    java -Djsse.enableSNIExtension=false
    

    2) 如果您不希望服务器得到修复,请实施永久性解决方法,例如在您的应用程序中禁用 SNI:

    System.setProperty("jsse.enableSNIExtension", "false");
    

    您链接的答案提到的解决方法最终会以更麻烦的方式禁用 SNI,因此除非您在应用程序的其他地方需要 SNI,否则我只会使用上述选项之一。

    【讨论】:

    • 正如我在问题中所说,我无法设置系统属性,因为我的代码在我无法控制的应用服务器中运行;在我的代码中设置属性似乎没有帮助,因为 SSLSocket 代码似乎在我的代码运行时已经初始化。我链接到的解决方法使用代码使 Oracles SSLSocket 实现在每个案例的基础上忽略 SNI,所以我认为这就是我想要的。
    【解决方案2】:

    所以这是我对该代码的尝试。我已经将它隐藏在我们可以在部署时调整的配置属性后面,所以希望我们可以在 Web 服务服务器配置修复后尽快摆脱这个缺陷。

    我没有检查过这段代码的任何安全隐患。我只是用我的应用程序测试过它,它可能不适合你。其他 JVM 版本的行为可能有所不同。如果您决定使用此代码,请不要追究我的责任!

    2014-09-01 更新:不幸的是,此解决方法仅在您通过 HTTPS 代理连接到目标时才有效。如果你直接连接,你会得到有点不伦不类的错误。

    Caused by: java.net.SocketException: Unconnected sockets not implemented
      at javax.net.SocketFactory.createSocket(SocketFactory.java:125) ~[na:1.7.0_65]
    

    由于我们的目标服务器的配置已修复,我不再需要解决方法。添加工作存根 createSocket() 方法要复杂得多,因为它需要在 HTTPClients 的 SSLSocketFactory 中完成。

    import javax.net.ssl.SSLSocketFactory;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**
     * When a TLS client sends the Server Name Indication extension in the Client Hello, the server might reply with an
     * alert that it does not know this particular server name.  Usually, this means that the server is misconfigured,
     * or that you're using the wrong hostname.  But as long as the server does get you to the correct web service or
     * site, it's harmless.
     * <p/>
     * Oracle in it's wisdom has decided that the warning should be treated as an error.  This cannot be changed.  This
     * class overrides all createSocket calls so that the hostname (for TLS purposes) is blanked-out.  In the
     * implementation in 1.7.0_51-b13, this makes SSL not issue the SNI extension in the hello,
     * thereby not triggering the problematic response.
     * <p/>
     * To use this with Apache HttpComponents' HttpClient, you need to create a ConnectionManager that uses a
     * SchemeManager, which in turn uses a custom https SchemeHandler using this socket factory.
     *
     * <pre>
     *   {@code
     *   SchemeRegistry schemeRegistry = new SchemeRegistry();
     *   schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
     *   if (disableServerNameIndication) {
     *       schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(new NoSNISSLSocketFactory
     *           (sslcontext.getSocketFactory()),
     *           SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)));
     *   } else {
     *       schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(sslcontext)));
     *   }
     *   defaultHttpClient = new DefaultHttpClient(new PoolingClientConnectionManager(schemeRegistry));}
     * </pre>
     */
    public class NoSNISSLSocketFactory extends SSLSocketFactory {
    
      private final SSLSocketFactory sslSocketFactory;
    
      protected NoSNISSLSocketFactory(SSLSocketFactory socketFactory) {
        this.sslSocketFactory = socketFactory;
      }
    
      @Override
      public String[] getDefaultCipherSuites() {
        return sslSocketFactory.getDefaultCipherSuites();
      }
    
      @Override
      public String[] getSupportedCipherSuites() {
        return sslSocketFactory.getSupportedCipherSuites();
      }
    
      @Override
      public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return sslSocketFactory.createSocket(socket, "", port, autoClose);
      }
    
      @Override
      public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return createSocket(new Socket(host, port), host, port, true);
      }
    
      @Override
      public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException,
          UnknownHostException {
        return createSocket(new Socket(host, port, localHost, localPort), host, port, true);
      }
    
      @Override
      public Socket createSocket(InetAddress host, int port) throws IOException {
        return sslSocketFactory.createSocket(host, port);
      }
    
      @Override
      public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
        return createSocket(new Socket(host, port, localHost, localPort), host.getHostName(), port, true);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      • 2011-11-28
      相关资源
      最近更新 更多