From 7f867bff0958513c89e1e9782adcbc92e8c84abe Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 15 Feb 2021 22:14:39 +0100 Subject: [PATCH] Fixes #5973 - Proxy client TLS authentication example. Examples, in form of test cases for a proxy that uses TLS client authentication both towards the remote client and towards the server. Signed-off-by: Simone Bordet --- .../io/ssl/SslClientConnectionFactory.java | 27 +- .../jetty/proxy/ClientAuthProxyTest.java | 551 ++++++++++++++++++ .../resources/client_auth/client_keystore.p12 | Bin 0 -> 8051 bytes .../resources/client_auth/proxy_keystore.p12 | Bin 0 -> 10391 bytes .../resources/client_auth/server_keystore.p12 | Bin 0 -> 2575 bytes 5 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java create mode 100644 jetty-proxy/src/test/resources/client_auth/client_keystore.p12 create mode 100644 jetty-proxy/src/test/resources/client_auth/proxy_keystore.p12 create mode 100644 jetty-proxy/src/test/resources/client_auth/server_keystore.p12 diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java index 588794d2da96..ea7dd5f3094f 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java @@ -34,6 +34,9 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; +/** + *

A ClientConnectionFactory that creates client-side {@link SslConnection} instances.

+ */ public class SslClientConnectionFactory implements ClientConnectionFactory { public static final String SSL_CONTEXT_FACTORY_CONTEXT_KEY = "ssl.context.factory"; @@ -120,7 +123,10 @@ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) return ClientConnectionFactory.super.customize(connection, context); } + /** + *

A factory for {@link SSLEngine} objects.

+ *

Typically implemented by {@link SslContextFactory.Client} + * to support more flexible creation of SSLEngine instances.

+ */ + public interface SslEngineFactory + { + /** + *

Creates a new {@link SSLEngine} instance for the given peer host and port, + * and with the given context to help the creation of the SSLEngine.

+ * + * @param host the peer host + * @param port the peer port + * @param context the {@link ClientConnectionFactory} context + * @return a new SSLEngine instance + */ + public SSLEngine newSslEngine(String host, int port, Map context); + } + private class HTTPSHandshakeListener implements SslHandshakeListener { private final Map context; diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java new file mode 100644 index 000000000000..c27504edab29 --- /dev/null +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java @@ -0,0 +1,551 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.proxy; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.Principal; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSession; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.security.auth.x500.X500Principal; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpClientTransport; +import org.eclipse.jetty.client.HttpDestination; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.ClientConnectionFactory; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + *

Tests for client and proxy authentication using certificates.

+ *

There are 3 KeyStores:

+ *
+ *
{@code client_keystore.p12}
+ *
{@code proxy} -> the proxy domain certificate with CN=proxy
+ *
{@code user1_client} -> the client certificate for user1, signed with the server certificate
+ *
{@code user2_client} -> the client certificate for user2, signed with the server certificate
+ *
+ *
+ *
{@code proxy_keystore.p12}
+ *
{@code proxy} -> the proxy domain private key and certificate with CN=proxy
+ *
{@code server} -> the server domain certificate with CN=server
+ *
{@code user1_proxy} -> the proxy client certificate for user1, signed with the server certificate
+ *
{@code user2_proxy} -> the proxy client certificate for user2, signed with the server certificate
+ *
+ *
+ *
{@code server_keystore.p12}
+ *
{@code server} -> the server domain private key and certificate with CN=server, + * with extension ca:true to sign client and proxy certificates.
+ *
+ * + *

In this way, a remote client can connect to the proxy and be authenticated, + * and the proxy can connect to the server on behalf of that remote client, since + * the proxy has a certificate correspondent to the one of the remote client.

+ *

The main problem is to make sure that the {@code HttpClient} in the proxy uses different connections + * to connect to the same server, and that those connections are authenticated via TLS client certificate + * with the correct certificate, avoiding that requests made by {@code user2} are sent over connections + * that are authenticated with {@code user1} certificates.

+ */ +public class ClientAuthProxyTest +{ + private Server server; + private ServerConnector serverConnector; + private Server proxy; + private ServerConnector proxyConnector; + private HttpClient client; + + private void startServer(Handler handler) throws Exception + { + QueuedThreadPool serverThreads = new QueuedThreadPool(); + serverThreads.setName("server"); + server = new Server(serverThreads); + + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.addCustomizer(new SecureRequestCustomizer()); + HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); + + SslContextFactory.Server serverTLS = new SslContextFactory.Server(); + serverTLS.setNeedClientAuth(true); + // The KeyStore is also a TrustStore. + serverTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/server_keystore.p12").getAbsolutePath()); + serverTLS.setKeyStorePassword("storepwd"); + serverTLS.setKeyStoreType("PKCS12"); + + SslConnectionFactory ssl = new SslConnectionFactory(serverTLS, http.getProtocol()); + + serverConnector = new ServerConnector(server, 1, 1, ssl, http); + server.addConnector(serverConnector); + + server.setHandler(handler); + + server.start(); + System.err.println("SERVER = localhost:" + serverConnector.getLocalPort()); + } + + private void startServer() throws Exception + { + startServer(new EmptyServerHandler() + { + @Override + protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException + { + X509Certificate[] certificates = (X509Certificate[])request.getAttribute(SecureRequestCustomizer.JAVAX_SERVLET_REQUEST_X_509_CERTIFICATE); + Assertions.assertNotNull(certificates); + X509Certificate certificate = certificates[0]; + X500Principal principal = certificate.getSubjectX500Principal(); + ServletOutputStream output = response.getOutputStream(); + output.println(principal.toString()); + output.println(request.getRemotePort()); + } + }); + } + + private void startProxy(AbstractProxyServlet servlet) throws Exception + { + QueuedThreadPool proxyThreads = new QueuedThreadPool(); + proxyThreads.setName("proxy"); + proxy = new Server(); + + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.addCustomizer(new SecureRequestCustomizer()); + HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); + + SslContextFactory.Server proxyTLS = new SslContextFactory.Server(); + proxyTLS.setNeedClientAuth(true); + // The KeyStore is also a TrustStore. + proxyTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + proxyTLS.setKeyStorePassword("storepwd"); + proxyTLS.setKeyStoreType("PKCS12"); + + SslConnectionFactory ssl = new SslConnectionFactory(proxyTLS, http.getProtocol()); + + proxyConnector = new ServerConnector(proxy, 1, 1, ssl, http); + proxy.addConnector(proxyConnector); + + ServletContextHandler context = new ServletContextHandler(proxy, "/"); + context.addServlet(new ServletHolder(servlet), "/*"); + proxy.setHandler(context); + + proxy.start(); + System.err.println("PROXY = localhost:" + proxyConnector.getLocalPort()); + } + + private void startClient() throws Exception + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client(); + // Disable TLS-level hostname verification. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/client_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + client = new HttpClient(clientTLS); + QueuedThreadPool clientThreads = new QueuedThreadPool(); + clientThreads.setName("client"); + client.setExecutor(clientThreads); + client.start(); + } + + @AfterEach + public void dispose() throws Exception + { + LifeCycle.stop(client); + LifeCycle.stop(proxy); + LifeCycle.stop(server); + } + + private static String retrieveUser(HttpServletRequest request) + { + X509Certificate[] certificates = (X509Certificate[])request.getAttribute(SecureRequestCustomizer.JAVAX_SERVLET_REQUEST_X_509_CERTIFICATE); + String clientName = certificates[0].getSubjectX500Principal().getName(); + Matcher matcher = Pattern.compile("CN=([^,]+)").matcher(clientName); + if (matcher.find()) + { + // Retain only "userN". + return matcher.group(1).split("_")[0]; + } + return null; + } + + @Test + public void testClientAuthProxyingWithMultipleHttpClients() throws Exception + { + // Using a different HttpClient (with a different SslContextFactory.Client) + // per user works, but there is a lot of duplicated state in the HttpClients: + // Executors and Schedulers (although they can be shared), but also CookieManagers + // ProtocolHandlers, etc. + // The proxy has different SslContextFactory.Client statically configured + // for different users. + + startServer(); + startProxy(new AsyncProxyServlet() + { + private final Map httpClients = new ConcurrentHashMap<>(); + + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + HttpClient httpClient = getOrCreateHttpClient(user); + Request proxyRequest = httpClient.newRequest(rewrittenTarget) + .method(request.getMethod()) + .attribute(CLIENT_REQUEST_ATTRIBUTE, request); + // Send the request to the server. + proxyRequest.port(serverConnector.getLocalPort()); + // No need to tag the request when using different HttpClients. + return proxyRequest; + } + + private HttpClient getOrCreateHttpClient(String user) + { + if (user == null) + return getHttpClient(); + return httpClients.computeIfAbsent(user, key -> + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client(); + // Disable TLS-level hostname verification for this test. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + clientTLS.setCertAlias(key + "_proxy"); + // TODO: httpClients should share Executor and Scheduler at least. + HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1), clientTLS); + LifeCycle.start(httpClient); + return httpClient; + }); + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + @Test + public void testClientAuthProxyingWithMultipleServerSubDomains() throws Exception + { + // Another idea is to use multiple subdomains for the server, + // such as user1.server.com, user2.server.com, with the server + // providing a *.server.com certificate. + // The proxy must pick the right alias dynamically based on the + // remote client request. + // For this test we use 127.0.0.N addresses. + + startServer(); + startProxy(new AsyncProxyServlet() + { + private final AtomicInteger userIds = new AtomicInteger(); + private final Map subDomains = new ConcurrentHashMap<>(); + + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + // Obviously not fool proof, but for the 2 users in this test it does the job. + String subDomain = subDomains.computeIfAbsent(user, key -> "127.0.0." + userIds.incrementAndGet()); + Request proxyRequest = super.newProxyRequest(request, rewrittenTarget); + proxyRequest.host(subDomain).port(serverConnector.getLocalPort()); + // Tag the request. + proxyRequest.tag(new AliasTLSTag(user)); + return proxyRequest; + } + + @Override + protected HttpClient newHttpClient() + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client() + { + @Override + protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception + { + KeyManager[] keyManagers = super.getKeyManagers(keyStore); + for (int i = 0; i < keyManagers.length; i++) + { + keyManagers[i] = new ProxyAliasX509ExtendedKeyManager(keyManagers[i]); + } + return keyManagers; + } + }; + // Disable TLS-level hostname verification for this test. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + return new HttpClient(new HttpClientTransportOverHTTP(1), clientTLS); + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + @Test + public void testClientAuthProxyingWithSSLSessionResumptionDisabled() throws Exception + { + // To user the same HttpClient and server hostName, we need to disable + // SSLSession caching, which is only possible by creating SSLEngine without + // peer host information. + // This is more CPU intensive because TLS sessions can never be resumed. + + startServer(); + startProxy(new AsyncProxyServlet() + { + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + Request proxyRequest = super.newProxyRequest(request, rewrittenTarget); + proxyRequest.port(serverConnector.getLocalPort()); + // Tag the request. + proxyRequest.tag(user); + return proxyRequest; + } + + @Override + protected HttpClient newHttpClient() + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client() + { + @Override + public SSLEngine newSSLEngine(String host, int port) + { + // This disable TLS session resumption and requires + // endpointIdentificationAlgorithm=null because the TLS implementation + // does not have the peer host to verify the server certificate. + return newSSLEngine(); + } + + @Override + protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception + { + KeyManager[] keyManagers = super.getKeyManagers(keyStore); + for (int i = 0; i < keyManagers.length; i++) + { + keyManagers[i] = new ProxyAliasX509ExtendedKeyManager(keyManagers[i]); + } + return keyManagers; + } + }; + // Disable hostname verification is required. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(1); + return new HttpClient(transport, clientTLS); + + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + @Test + public void testClientAuthProxyingWithCompositeSslContextFactory() throws Exception + { + // The idea here is to have a composite SslContextFactory that holds one for each user. + // It requires a change in SslClientConnectionFactory to "sniff" for the composite. + + startServer(); + startProxy(new AsyncProxyServlet() + { + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + Request proxyRequest = super.newProxyRequest(request, rewrittenTarget); + proxyRequest.port(serverConnector.getLocalPort()); + proxyRequest.tag(user); + return proxyRequest; + } + + @Override + protected HttpClient newHttpClient() + { + ProxyAliasClientSslContextFactory clientTLS = configure(new ProxyAliasClientSslContextFactory(), null); + // Statically add SslContextFactory.Client instances per each user. + clientTLS.factories.put("user1", configure(new SslContextFactory.Client(), "user1")); + clientTLS.factories.put("user2", configure(new SslContextFactory.Client(), "user2")); + return new HttpClient(new HttpClientTransportOverHTTP(1), clientTLS); + } + + private T configure(T tls, String user) + { + // Disable TLS-level hostname verification for this test. + tls.setEndpointIdentificationAlgorithm(null); + tls.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + tls.setKeyStorePassword("storepwd"); + tls.setKeyStoreType("PKCS12"); + if (user != null) + { + tls.setCertAlias(user + "_proxy"); + LifeCycle.start(tls); + } + return tls; + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + private void testRequestsFromRemoteClients() throws Exception + { + // User1 makes a request to the proxy using its own certificate. + SslContextFactory clientTLS = client.getSslContextFactory(); + clientTLS.reload(ssl -> ssl.setCertAlias("user1_client")); + ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .tag("user1") + .send(); + Assertions.assertEquals(HttpStatus.OK_200, response.getStatus()); + String[] parts = response.getContentAsString().split("\n"); + String proxyClientSubject1 = parts[0]; + String proxyClientPort1 = parts[1]; + + // User2 makes a request to the proxy using its own certificate. + clientTLS.reload(ssl -> ssl.setCertAlias("user2_client")); + response = client.newRequest("localhost", proxyConnector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .tag("user2") + .send(); + Assertions.assertEquals(HttpStatus.OK_200, response.getStatus()); + parts = response.getContentAsString().split("\n"); + String proxyClientSubject2 = parts[0]; + String proxyClientPort2 = parts[1]; + + Assertions.assertNotEquals(proxyClientSubject1, proxyClientSubject2); + Assertions.assertNotEquals(proxyClientPort1, proxyClientPort2); + } + + private static class AliasTLSTag implements ClientConnectionFactory.Decorator + { + private final String user; + + private AliasTLSTag(String user) + { + this.user = user; + } + + @Override + public ClientConnectionFactory apply(ClientConnectionFactory factory) + { + return (endPoint, context) -> + { + Connection connection = factory.newConnection(endPoint, context); + SSLEngine sslEngine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY); + sslEngine.getSession().putValue("user", user); + return connection; + }; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + AliasTLSTag that = (AliasTLSTag)obj; + return user.equals(that.user); + } + + @Override + public int hashCode() + { + return Objects.hash(user); + } + } + + private static class ProxyAliasX509ExtendedKeyManager extends SslContextFactory.X509ExtendedKeyManagerWrapper + { + private ProxyAliasX509ExtendedKeyManager(KeyManager keyManager) + { + super((X509ExtendedKeyManager)keyManager); + } + + @Override + public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) + { + for (String keyType : keyTypes) + { + String[] aliases = getClientAliases(keyType, issuers); + if (aliases != null) + { + SSLSession sslSession = engine.getSession(); + String user = (String)sslSession.getValue("user"); + String alias = user + "_proxy"; + if (Arrays.asList(aliases).contains(alias)) + return alias; + } + } + return super.chooseEngineClientAlias(keyTypes, issuers, engine); + } + } + + private static class ProxyAliasClientSslContextFactory extends SslContextFactory.Client implements SslClientConnectionFactory.SslEngineFactory + { + private final Map factories = new ConcurrentHashMap<>(); + + @Override + public SSLEngine newSslEngine(String host, int port, Map context) + { + HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); + String user = (String)destination.getOrigin().getTag(); + return factories.compute(user, (key, value) -> value != null ? value : this).newSSLEngine(host, port); + } + } +} diff --git a/jetty-proxy/src/test/resources/client_auth/client_keystore.p12 b/jetty-proxy/src/test/resources/client_auth/client_keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..91a29daba16cca53aa2dec7e5b124221e6badde9 GIT binary patch literal 8051 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 literal 0 HcmV?d00001 diff --git a/jetty-proxy/src/test/resources/client_auth/proxy_keystore.p12 b/jetty-proxy/src/test/resources/client_auth/proxy_keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..46a44fae095bb62aa3b3506d3e0d625f8d011090 GIT binary patch literal 10391 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 UFmy=v+}o}GLsS9+3LKjE9|Mn%2><{9 literal 0 HcmV?d00001 diff --git a/jetty-proxy/src/test/resources/client_auth/server_keystore.p12 b/jetty-proxy/src/test/resources/client_auth/server_keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..d69a31386a4262f6ca5cd60fa808c0b3785ea35c GIT binary patch literal 2575 zcmY+EX*d)L7sqEa7`uxww~RS|MB^AID||6pv3UX z^DLNG!9skWPbHBvT15&326Pxe2#{W00?W_BR5?P@hOrhnbb+fzeE&*5%>et@6`gL$ z7KNB{JGmJ0I*ti5;a0TGsj3kNKg)<$H8+{FjFhnDk#8e$uSF#lv8&ZoKLjIwkE75W z>uxAg*#7Qu&u-d@&}?xm7Sd4U6@M&@^X0!iQp)v}3>%#dtDiR^?cC8liD~g(HLsYw z6E%W&=0!EPWb{>Cx z;Ao}Jn^d?hHA{x_-q6J0mCBq|y*5u+-+oRRlrrbFcsMm;QxZru3tVj)Xb|O{(0+Gt z$rh1NZTd;c<>JtErT)~fDcE&Mh9PJ9XWB@+ODwx;G_d2TNZDtq?yMbnKJ;rd-%>Pp zPuiN){%;+#IQ$s-(R|+zl!LhSX~bu9L!z?*4$*eiWx^U#77+5x#YghPN2jIVrz_U> z4V|a!O+^;j_IZM68Z3e0!H;SU%}d@;(ud>;iHK`eYR?BeW%|6`*XULCYa|@%a#|G) z0170oRnhQ4-5Y!a5~n%yYGOVs^~cf0ykhbV~F|cHJkxYiCCb>NSAA$>EL(>W!S}eahq`AM7}3JbDb;okYq%W3Vqs%)bjfJI#!W zXALxtrYrMQ)m%3Ud-au*D}5t{OxF(`%#zU>In8G>G6Sq0rY|tAywk`GjsP#mmX_z8^w*@Pjlv$7ILwaN{ENN+P<~&p? zVKq?KLptJnTbeTI!LU}P&0?_{A0K}0mA&Se2AZI)+=Y{^g@0E<@G8<_i+vR_c&pcW?kz6HCJnX}?K9u@ z_}UB4qB5RNu?|6ZcSecD;o*XDyISMD7&+2O_cXB6KLflU+9a%~Xmz^)k$kGvZ!mAbJ~A??t)=2RLe`NL3ospx5p zMD}_qf7@$~990uk^>qPv%*6ZMyhU4{Z@(O_Z6A-Z>tf$HYTDj+cVvO7=ziO7Y$ifN1PAwAf@VWAM82&T+ZU zmD@oIw7KWo{CbzmO~Qih^S+6nER_muQo)F^-)1%q+50MbmirWiS7h%C1?kxAg6qG1 zD*W=1CV0hW6n9D+QbJE_tZ<`F7eF(@8-j{egBI6&HbWXH=9TM$BZ~}8jf#ygU*YPS zijsQLfXu(F0weVn#1z;%?#I=|A0f^nO*(pSH6EIM$>vE^SCAPH-NNKUeW46@Rz=^(|}qz>iAh=XAV3Y;xOY=O$bcax+^Tq|e)?|NbU&X> zc^ID-K3*SN4nOKKyh8tz?^apooy%3Xwxm%H{R-VToosWUfO>pYuH~El zEV%~by}aJ?RqL^NXR^lo?AFNLWnInSh%@8pxXQ-h z$dQR$x7^)h*^tZB5K6A;@DoDR&G&;>lt&xYUc}D8FTjr<4>tPjsiZo@|;ZY-VDX5C!4m@`fUxRkXM}!c&qnwvh&zHBEz|8jMEu?jT)xe1!7TN!D_c;#u>;h!i>iRM9hMNv}Pg2RA zWZUurt&A2xGeYUEv4TPTbN~n!8F=+JX2NLO3ztll?9@;(HRnr!aJhbu4%AoU@f(SP TZ6>aC6ujDz`UnC8i3NWGm+HX0 literal 0 HcmV?d00001