1

I have a multi-tenant webservice which I want to use mutual SSL/TLS authentication as well as user authentication. This means that I need to resolve the user and the user's allowed certs, which can only occur after the SSL connection has been established. I will then use PKIXCertPathBuilderResult to valid the trust chain using the client certs passed in the request.

In Tomcat with the openssl connector, it's possible to use optional_no_ca mode, which requests a client cert but does not validate it.

With Jetty 9.x, I've tried configuring the following SslContextFactory options to no avail:

  • ValidateCerts=false
  • ValidatePeerCerts=false
  • TrustAll=true

How can this be achieved in Jetty 9.x?

Edit 2019: The requirement was to demand an SSL certificate from all client devices accessing the system. The validation of the certificate chain and other certificate attributes would then be performed by the application, which also has the ability to lookup missing cert roots from external sources. This is in contrast to the norm - typically, application servers would perform cert-chain validation during the SSL connection setup using a pre-configured static list of known trusted CAs. If trust can not be found, the SSL connection is rejected.

Alastair McCormack
  • 26,573
  • 8
  • 77
  • 100

2 Answers2

1

While TrustAll seems to be the likely solution, it only works if no TrustStore and KeyStore is given. Then you can't connect using a regular client as the server has no certificate to give during the handshake.

To get a sensible trustAll mode, the only options seems to be to extend SslContextFactory:

package media.alu.jetty;
/**
 * SslContextFactoryRelaxed is used to configure SSL connectors
 * as well as HttpClient. It holds all SSL parameters and
 * creates SSL context based on these parameters to be
 * used by the SSL connectors.
 *
 * TrustAll really means trustAll!
 */
@ManagedObject
public class SslContextFactoryRelaxed extends SslContextFactory
{
    private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM;
    private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM;

    @Override
    protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls) throws Exception
    {
        TrustManager[] managers = null;
        if (trustStore != null)
        {
            if (isTrustAll()) {
                managers = TRUST_ALL_CERTS;
            }

            // Revocation checking is only supported for PKIX algorithm
            else if (isValidatePeerCerts() && "PKIX".equalsIgnoreCase(getTrustManagerFactoryAlgorithm()))
            {
                PKIXBuilderParameters pbParams = newPKIXBuilderParameters(trustStore, crls);

                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
                trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams));

                managers = trustManagerFactory.getTrustManagers();
            }
            else
            {
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
                trustManagerFactory.init(trustStore);

                managers = trustManagerFactory.getTrustManagers();
            }
        }

        return managers;
    }

}

To use:

  1. Follow Jetty documentation to configure SSL/TLS with client authentication
  2. Compile code above against Jetty 9.x
  3. Install jar in `$jetty.home/lib/ext'
  4. Edit $jetty.home/etc/jetty-ssl-context.xml

    i. Change:

    <Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
    

    to:

    <Configure id="sslContextFactory" class="media.alu.jetty.SslContextFactoryRelaxed">
    

    ii. Add <Set name="TrustAll">TRUE</Set> as child of <Configure id="sslContextFactory">

Alastair McCormack
  • 26,573
  • 8
  • 77
  • 100
-2

Why? JSSE already validates it. All you need to to is check the authorization of that user. By the time you get access to the certificate, it is already validated for integrity, non-expiry, and trust-anchoring, so you can believe that its SubjectDN refers to who it says it refers to, so all you have to do is decide what roles that SubjectDN has, if any.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thanks, you made me think for a second, but I don't have a CA that's common to all users - each set of users will be signed by a different CA. I would then have to maintain a JKS with all known CAs in it. – Alastair McCormack Oct 18 '17 at 20:45
  • Doesn't matter. You're conflating authentication with authorisation. You should use the same truststore for all clients, and only *authorize* afterwards. – user207421 Oct 18 '17 at 20:53
  • I'm not conflating authentication and authorisation. The user provides credential data to authenticate and authorise themselves to the system. Once the user is authenticated and authorised, I then need to authorise their device for use with the system. Maintaining a changing JKS file on a filesystem in a large clustered globally distributed environment is a PITA so it does matter. Further, there's nothing magic in JSSE that defines this - It's the SSL Connector bespoke to each implementation that happens to perform it. – Alastair McCormack Oct 18 '17 at 21:05
  • 1. You are. TLS authenticates; the application authorizes. 2. If the CAs are al trusted by the default cacerts there is zero maintenance, and if they aren't you need to maintain only for the changes in the CAs, which should only happen very infrequently: CA certifictEs run for decades. 3. There most certainly is. JSSE does all this. The connector only uses JSSE. JSSE is where all the trust management and PKIX path building is located. – user207421 Oct 18 '17 at 21:11
  • 1. The user credentials (username/password) authenticates the user, device authentication and authorisation happens later within the application. The most important thing to me is the user a + a. 2. Read comment #1 - "I don't have a CA that's common to all users". These are private CAs so won't be in cacerts (Does Verisign et al even sign client certs?!) . 3. The connector defaults to using SunJSSE which has some default mechanisms for establishing trust. That's just one implementation. So who says device authentication and authorisation has to happen at the connector level? – Alastair McCormack Oct 18 '17 at 21:25