如何从 Java 中的休息请求中检索客户端证书

How to retrieve client certificate from rest request in Java

我在 Java 中将 Jersey 用作 REST 服务器,将 Jetty 用作 Web 服务器。我有 self signed 个证书。我想从收到的 HTTP 请求中获取客户端证书详细信息。如何获取HttpServletRequest的信息?

一种方法:

X509Certificate certs[] = (X509Certificate[])httpRequest.getAttribute("javax.servlet.request.X509Certificate");

这样对吗?这导致异常,

Error [Ljava.lang.Object; cannot be cast to [Ljava.security.cert.X509Certificate 

我应该添加任何额外的 JAR 吗?或者有什么办法可以获取客户端证书的详细信息?

我也遇到了,

httpRequest.getHeader("ssl_client_cert");

这两种方式似乎都不适合我。如何获取详情?

首先,确保处理 SSL/TLS 的 ServerConnector。 接下来,该 ServerConnector 应该有一个 SslConnectionFactory,其中配置了一个 HttpConfiguration 对象。 该 HttpConfiguration 对象应添加 SecureRequestCustomizer。

用嵌入式码头的说法,它看起来像这样...

        // SSL Context Factory
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStorePath("/path/to/keystore");
        sslContextFactory.setKeyStorePassword("password");
        sslContextFactory.setKeyManagerPassword("secret");
        sslContextFactory.setTrustStorePath("/path/to/keystore");
        sslContextFactory.setTrustStorePassword("password");


        // SSL HTTP Configuration
        HttpConfiguration https_config = new HttpConfiguration(http_config);
        https_config.addCustomizer(new SecureRequestCustomizer()); // <-- THIS LINE

        // SSL Connector
        ServerConnector sslConnector = new ServerConnector(server,
            new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()),
            new HttpConnectionFactory(https_config));
        sslConnector.setPort(8443);
        server.addConnector(sslConnector);

如果您在独立 Jetty 上使用 ${jetty.home}${jetty.base} 拆分,那么您需要检查配置中是否存在 jetty-ssl.xml ...

$ cd /path/to/my-jetty-base
$ java -jar /path/to/jetty-home/start.jar --list-config

Java Environment:
-----------------
 java.home = /Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/jre (null)
 java.vm.vendor = Oracle Corporation (null)
 java.vm.version = 25.202-b08 (null)
 java.vm.name = Java HotSpot(TM) 64-Bit Server VM (null)
 java.vm.info = mixed mode (null)
 java.runtime.name = Java(TM) SE Runtime Environment (null)
 java.runtime.version = 1.8.0_202-b08 (null)
 java.io.tmpdir = /var/folders/w5/mmnzpk0n369dntp4nszlc8h40000gn/T/ (null)
 user.dir = /path/to/my-jetty-base (null)
 user.language = en (null)
 user.country = US (null)

Jetty Environment:
-----------------
 jetty.version = 9.4.15.v20190215
 jetty.tag.version = master
 jetty.home = /path/to/jetty-home
 jetty.base = /path/to/my-jetty-base

...(snip lots of output)...

Jetty Active XMLs:
------------------
 ${jetty.home}/etc/jetty-threadpool.xml
 ${jetty.home}/etc/jetty.xml
 ${jetty.home}/etc/jetty-webapp.xml
 ${jetty.home}/etc/jetty-plus.xml
 ${jetty.home}/etc/jetty-annotations.xml
 ${jetty.home}/etc/jetty-deploy.xml
 ${jetty.home}/etc/jetty-http.xml
 ${jetty.home}/etc/jetty-ssl.xml      <-- THIS LINE
 ${jetty.home}/etc/jetty-ssl-context.xml
 ${jetty.home}/etc/jetty-https.xml
 ${jetty.home}/etc/jetty-jaas.xml
 ${jetty.home}/etc/jetty-rewrite.xml
 ${jetty.base}/etc/demo-rewrite-rules.xml
 ${jetty.base}/etc/test-realm.xml

一旦你验证了这个基本级别的配置,你就可以继续使用这些属性了。

接下来,当您向该安全连接器发出请求时,各种过滤器和 servlet 将可以访问许多可能对您有用的请求属性。

这些是 Servlet 规范定义的属性,SecureRequestCustomizer 添加到传入的 HttpServletRequest。

  • javax.servlet.request.X509Certificate 包含 java.security.cert.X509Certificate 个对象的数组。
  • javax.servlet.request.cipher_suite 将协商的密码套件作为 String 对象保存。
  • javax.servlet.request.key_size 将您的密钥大小保存为 Integer 对象。
  • javax.servlet.request.ssl_session_id 将您的 SSL 会话 ID 作为 String 对象保存。

这些是 SecureRequestCustomizer 添加到您传入的 HttpServletRequests 的 Jetty 自定义属性。

  • org.eclipse.jetty.servlet.request.ssl_session 持有此连接的活动 java.net.ssl.SSLSession 对象。

由于您在尝试使用该属性时看到了通用 Object[],也许您应该调试并查看这些对象的实际内容。

考虑到 Jetty 无法控制的某些东西可能已经替换了它们,或者在您尝试访问它们之前使 Jetty 无法以 Servlet 规范形式使用它们。

  • 已知一些第 3 方安全库过去会更改这些属性。
  • 或者你没有终止 Jetty 的 TLS/SSL 连接,例如 firewall/router/load 平衡器(haproxy、nginx、apache httpd 或各种硬件)(这意味着 Jetty 看不到证书).
  • 您使用的不是普通的 ServerConnector(例如 UnixSocketConnector、LocalConnector 或自定义连接器)
  • 或者您的 Java 实施中有第 3 方安全层。
Object certs[] = httpRequest.getAttribute("javax.servlet.request.X509Certificate");
for(Object cert: certs) {
  System.out.printf("DEBUG: Cert[%s] = %s%n", cert.getClass().getName(), cert);
}