使用mTLS和WSS时提取客户端证书信息

时间:2020-08-13 09:35:17

标签: java tomcat x509 wss

我有一个在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握手以及提供的客户端证书。

我有一个WebSocket服务器端点(@javax.websocket.server.ServerEndpoint),当安全tls隧道建立后,当客户端通过“ wss:// .....”连接时,确实会调用该端点,{{1} }方法被调用,它具有javax.websocket.Session对象。因此将http(s)升级到ws即可。

我的问题: 执行TLS握手后(由WSS触发:在我的情况下为ws over https),我需要提取客户端证书(X509)信息(主题/发布者等。),并在@OnOpen方法中提供它。我正在寻找某些拦截器或其他方式来访问和提取证书数据,并在升级到ws后 使其可用。有什么方法可以通过@OnOpen Web套接字处理方法访问HttpServletRequest?感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

再看一下我就能做到了。

第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端点具有客户端证书,通过该客户端证书建立了基础HTTPS连接TLS(在我的情况下为2-way TLS)。