19

It is very easy to set up an embedded Jetty server that requests client authentication: One just needs to add the statement SslContextFactory.setNeedClientAuth(true); to the ssl context when configuring the server. Any client that has its certificate in the server's truststore will be able to establish a TLS connection to the server.

However I need to know which client of all the possible trusted clients is currently making a request; in other words I need to know the client certificate used in this connection, in particular in the handler. Does anyone know how to access this certificate or if it is even possible?

Brian Reinhold
  • 2,313
  • 3
  • 27
  • 46

2 Answers2

21

Updated Aug 2019: for Jetty 9.4.20.v20190813 release.

The certificates are added to the Request objects (such as HttpServletRequest), by a HttpConfiguration Customizer.

Specifically, the SecureRequestCustomizer.

Your code to use this would be as follows (scroll down)...

Server server = new Server();

// === HTTP Configuration ===
HttpConfiguration http_config = new HttpConfiguration();
http_config.setSecureScheme("https");
http_config.setSecurePort(8443);
http_config.setOutputBufferSize(32768);
http_config.setRequestHeaderSize(8192);
http_config.setResponseHeaderSize(8192);
http_config.setSendServerVersion(true);
http_config.setSendDateHeader(false);

// === Add HTTP Connector ===
ServerConnector http = new ServerConnector(server,
    new HttpConnectionFactory(http_config));
http.setPort(8080);
http.setIdleTimeout(30000);
server.addConnector(http);

// === Configure SSL KeyStore, TrustStore, and Ciphers ===
SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath("/path/to/keystore");
sslContextFactory.setKeyStorePassword("changeme");
sslContextFactory.setKeyManagerPassword("changeme");
sslContextFactory.setTrustStorePath("/path/to/truststore");
sslContextFactory.setTrustStorePassword("changeme");
// OPTIONAL - for client certificate auth (both are not needed)
// sslContextFactory.getWantClientAuth(true)
// sslContextFactory.setNeedClientAuth(true)

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

// == Add SSL Connector ===
ServerConnector sslConnector = new ServerConnector(server,
    new SslConnectionFactory(sslContextFactory,"http/1.1"),
    new HttpConnectionFactory(https_config));
sslConnector.setPort(8443);
server.addConnector(sslConnector);

With this SecureRequestCustomizer in place you can access various pieces about the SSL connection from the HttpServletRequest.getAttribute(String) calls using the following attribute names.

javax.servlet.request.X509Certificate

an array of java.security.cert.X509Certificate[]

javax.servlet.request.cipher_suite

the String name of the cipher suite. (same as what is returned from javax.net.ssl.SSLSession.getCipherSuite())

javax.servlet.request.key_size

Integer of the key length in use

javax.servlet.request.ssl_session_id

String representation (hexified) of the active SSL Session ID

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • Does the 'javax.servlet.request.X509Certificate' returns a list of the X509 certificates in the truststore or the certificate chain for this connection? If the former, how do I find the certificate used by this connection? If the latter, how is the chain listed? Is element [0] the certificate in the truststore and the remaining element represent the chain to the CA? – Brian Reinhold Nov 18 '13 at 21:29
  • It worked with a small caveat. The X509Certificate import needs to be java.security.cert.X509Certificate and NOT javax.security.cert.X509Certificate even though the string passed in the attribute is "javax". If I import the javax certificate I get a cast exception. It also appears to be (from the comment below) that the first certificate in the list is the one for the current session. Hard to tell at test at the moment since I have only one certificate in my truststore. – Brian Reinhold Nov 18 '13 at 22:01
  • the `"javax.servlet.*"` is merely an attribute name for something that the servlet spec dictates, that name has no correlation on the class you should cast to/use. – Joakim Erdfelt Nov 18 '13 at 22:37
  • @JoakimErdfelt how to use the HttpServletRequest? Any pointer would be helpful. – devd Feb 01 '21 at 08:01
8

There's a standard servlet request property: javax.servlet.request.X509Certificate

It returns an array of X509Certificates.

We use this to get the name and look up the DN from the cert:

x509Cert[0].getSubjectX500Principal().getName()
Will Hartung
  • 115,893
  • 19
  • 128
  • 203