Tomcat Websocket 禁用主机名验证

6
我想禁用Tomcat WebSocket实现的主机名验证,但是我没有找到任何示例。
我已经成功禁用了证书验证:
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
ClientEndpointConfig config = ClientEndpointConfig.Builder.create().build();
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                .loadTrustMaterial(null, acceptingTrustStrategy)
                .build();

config.getUserProperties().put("org.apache.tomcat.websocket.SSL_CONTEXT", sslContext);

官方文档表示:
对于安全服务器端点,默认启用主机名验证。要绕过此验证(不建议),需要通过org.apache.tomcat.websocket.SSL_CONTEXT用户属性提供自定义SSLContext。自定义SSLContext必须配置具有扩展javax.net.ssl.X509ExtendedTrustManager的自定义TrustManager。然后,可以通过适当实现各个抽象方法来控制所需的验证(或缺少验证)。

https://tomcat.apache.org/tomcat-8.5-doc/web-socket-howto.html

但只要我了解,X509ExtendedTrustManager 用于证书验证,而不是主机名验证。

有人能帮助我吗?


我遇到了类似的问题。看起来这并不容易实现。在Tomcat的WsWebSocketContainer中,setEndpointIdentificationAlgorithm("HTTPS");将启用主机名验证:https://github.com/apache/tomcat/blob/7ce16dd575d34657b3f8b23c816bac9a871a0162/java/org/apache/tomcat/websocket/WsWebSocketContainer.java#L942 - Remo
我在尝试升级到Tomcat 9时遇到了同样的问题。有人找到解决方法了吗? - zoesnape
1个回答

0
我在Tomcat用户列表(https://lists.apache.org/thread/1x6x6hqzj8ggv408y0r2bkcp6k4hqz1t)上询问并确认X509ExtendedTrustManager是正确的方式。这里没有像HttpsUrlConnection的HostnameVerifier选项那样"好"/直观的覆盖方式,但以下是我成功使用的方法:
private static class BypassHostnameVerificationTrustManager extends X509ExtendedTrustManager {
  private final X509ExtendedTrustManager m_realTrustManager;

  /**
   * @param realManager Instance of a legitimate X509ExtendedTrustManager provided by JSSE to delegate real
   * security work to
   */
  public BypassHostnameVerificationTrustManager(X509ExtendedTrustManager realManager) {
    super();
    m_realTrustManager = realManager;
  }

  @Override
  public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
    try {
      m_realTrustManager.checkServerTrusted(x509Certificates, s, sslEngine);
    } catch (CertificateException e) {
      String errMsg = e.getMessage();
      if (StringUtils.equals(errMsg, "No subject alternative names present")) {
        s_log.warn("Suppressing hostname verification exception: {}", e.getMessage());
        return;
      }
      String peerHost = sslEngine.getPeerHost();
      if (InetAddresses.isInetAddress(peerHost)) {
        String expectedError = String.format("No subject alternative names matching IP address %s found", peerHost);
        if (StringUtils.equals(errMsg, expectedError)) {
          s_log.info("Suppressing hostname verification exception: {}", e.getMessage());
          return;
        }
      }
      throw e;
    }
  }

  @Override
  public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
    m_realTrustManager.checkServerTrusted(x509Certificates, s, socket);
  }

  @Override
  public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    m_realTrustManager.checkServerTrusted(x509Certificates, s);
  }

  @Override
  public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
    m_realTrustManager.checkClientTrusted(x509Certificates, s, socket);
  }

  @Override
  public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
    m_realTrustManager.checkClientTrusted(x509Certificates, s, sslEngine);
  }

  @Override
  public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    m_realTrustManager.checkClientTrusted(x509Certificates, s);
  }

  @Override
  public X509Certificate[] getAcceptedIssuers() {
    return m_realTrustManager.getAcceptedIssuers();
  }
}

如果有人有更好的方法,我会很高兴听到的...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接