【问题标题】:Extract Client Certificate information when using mTLS and WSS使用 mTLS 和 WSS 时提取客户端证书信息
【发布时间】:2020-08-13 09:35:17
【问题描述】:

我有一个在 Tomcat 中运行的 java 应用程序。我在端口 8443 上添加了 https 监听器,如下所述:

https://docs.spring.io/spring-boot/docs/2.2.x/reference/pdf/spring-boot-reference.pdf(9.3.13. 使用 Tomcat 启用多个连接器)

我另外配置了侦听器以挑战客户端提供证书(2-way-TLS / 双向 TLS)并拥有一个包含受信任条目的信任库。所有这些都有效,我可以在日志中看到整个 TLS-Handshake,以及提供的客户端证书。

我有一个 WebSocket 服务器端点 (@javax.websocket.server.ServerEndpoint),在建立安全 tls 隧道后,当客户端通过“wss://.....”连接时,它会被调用,@OnOpen 方法被调用它有 javax.websocket.Session 对象。所以 http(s) 升级到 ws 工作正常。

我的问题: 执行 TLS 握手后(由 WSS 触发:在我的情况下是 ws over https),我需要提取客户端证书 (X509) 信息(主题/发行人等)并在 @OnOpen 方法中提供它。我正在寻找一些拦截器或其他方式来访问和提取证书数据并使其可用升级到 ws 完成。有没有办法从@OnOpen web socket 处理方法访问HttpServletRequest?感谢您的帮助。

【问题讨论】:

    标签: java tomcat x509 wss


    【解决方案1】:

    再看下去我就可以做到了。

    第一步:点击 modifyHandshake(...) 方法并获取ServletRequest 对象,HttpSession 不会完成这项工作。致谢this StackOverflow answer

    package example.com;
    
    import java.lang.reflect.Field;
    import java.security.cert.X509Certificate;
    import javax.security.auth.x500.X500Principal;
    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpSession;
    import javax.websocket.HandshakeResponse;
    import javax.websocket.server.HandshakeRequest;
    import javax.websocket.server.ServerEndpointConfig;    
    
    public class InjectAttributesIntoWebSocketConfigurator extends ServerEndpointConfig.Configurator {
    
      @Override
      public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        ServletRequest servletRequest = getField(request, ServletRequest.class);
        X509Certificate[] certificates = (X509Certificate[]) servletRequest.getAttribute("javax.servlet.request.X509Certificate");
        // certificates[0] is client certificate
        // do null/error/empty array handling here
        config.getUserProperties().put("clientcert", certificates[0]);
      }
    
      private static <I, F> F getField(I instance, Class<F> fieldType) {
        try {
          for (Class<?> type = instance.getClass(); type != Object.class; type = type.getSuperclass()) {
            for (Field field : type.getDeclaredFields()) {
              if (fieldType.isAssignableFrom(field.getType())) {
                field.setAccessible(true);
                return (F) field.get(instance);
              }
            }
          }
        } catch (Exception e) {
          // Handle?
        }
         return null;
      }
    }
    

    第 2 步:配置 WS 以使用上面的配置器并从 Session 对象中读取任何参数(您必须事先在 modifyHandshake() 方法中添加这些参数)。

    @ServerEndpoint(
            value = "/some/endpoint/here",
            configurator = InjectAttributesIntoWebSocketConfigurator.class
    )
    

    第 3 步:完成:)。现在,WS 端点具有客户端证书,使用该证书建立了 TLS(在我的情况下为 2 路 TLS)底层 HTTPS 连接。

    【讨论】:

      猜你喜欢
      • 2021-10-07
      • 2021-11-01
      • 2020-11-13
      • 2021-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-07
      • 1970-01-01
      相关资源
      最近更新 更多