Search code examples
javassljettykeystoresni

Jetty Server has multiple server certificates


I want to put more than one server certificates in one Java Keystore(All have different CN). How can I config SSL so that when client specifies a hostname, the certificate with matching CN is returned, but if it's not specified, a desired default certificate is always returned.

I know that I can write my own key manager, but is there a simpler way? what is certAlias in Jetty, is it going to solve my problem?

I've seen other posts saying that the first certificate in the keystore is returned if no SNI matched. But in my case, it seems rather random, not to do with the order of certificates.

Thanks!


Solution

  • Your entire question is answered by simply using TLS + SNI properly.

    SNI (Server Name Indication) is very old, and very mature. It was first defined back in June 2003 as part of the TLS Extensions Spec RFC 3546 and has had multiple updates since then. (RFC 4366, RFC 6066, and then RFC 9325)

    All modern HTTP Clients (be it a browser, or an embedded HTTP client library) will use SNI when talking https to a server (technically, the TLS layer is handling this part). Even the venerable Java java.net.HttpURLConnection supports SNI (in reality, the Java TLS layer is doing this, the client library doesn't have to do anything extra to support SNI over TLS).

    SNI has multiple configurations in Jetty.

    org.eclipse.jetty.util.ssl.SslContextFactory.Server

    • .setSniRequired(boolean) - this is the JVM TLS layer behavior for SNI, if the client doesn't provide SNI, or the SNI has no match on the server side, then the TLS layer will respond accordingly.
    • .setSNISelector(SniX509ExtendedKeyManager.SniSelector) - this is the JVM TLS layer behavior control to select the appropriate certificate alias for the incoming SNI information. The default behavior is found in org.eclipse.jetty.util.ssl.SslContextFactory.Server.sniSelect(String keyType, Principal[] issuers, SSLSession session, String sniHost, Collection<X509> certificates)

    org.eclipse.jetty.server.SecureRequestCustomizer

    • .setSniHostCheck(boolean) the this is post TLS layer, after the HTTP request has been parsed, ensuring that the client TLS SNI information AND client HTTP request authority (Host header in HTTP/1, :authority pseudo header in HTTP/2 and HTTP/3) AND the returned SNI in the server level TLS layer all match.
    • .setSniRequired(boolean) this ensures that the SNI is used by the client.

    I would strongly encourage you to NOT replace the KeyManager with your own implementation unless you are intimately aware of the entirety of SNI and TLS both from a spec point of view, and the nuances of each of the major browsers behavior with regards to how they treat SNI (eg: local names, localhost, ip literals, non-routables, reserved hosts, protected hosts, etc).

    Start with the default behavior in Jetty (which just uses the built-in JVM techniques for working with TLS + SNI as a server), then if you have more needs in terms of how to select a certificate, look at providing your own SniX509ExtendedKeyManager.SniSelector (but make sure to keep it up to date! you'll want to follow the code of the default implementation periodically to catch changes that happen due to various factors: major browser vendor changes in behavior, JVM changes in behavior, Java crypto roadmap changes, etc)