/* * Copyright 2015 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.apiman.gateway.platforms.servlet.auth.tls; import io.apiman.common.config.options.TLSOptions; import io.apiman.gateway.engine.IApiConnection; import io.apiman.gateway.engine.IApiConnectionResponse; import io.apiman.gateway.engine.IApiConnector; import io.apiman.gateway.engine.async.IAsyncResult; import io.apiman.gateway.engine.async.IAsyncResultHandler; import io.apiman.gateway.engine.auth.RequiredAuthType; import io.apiman.gateway.engine.beans.Api; import io.apiman.gateway.engine.beans.ApiRequest; import io.apiman.gateway.engine.beans.exceptions.ConnectorException; import io.apiman.gateway.platforms.servlet.connectors.HttpConnectorFactory; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Paths; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Request; 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.server.handler.AbstractHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * Tests mutual TLS auth using self-generated CA. In these scenarios the certificates for each system element * are signed by the CA, but the certificates are not held within the trust stores - only the CA is. The * certificates are trusted by virtue of the CA being trusted. * * @author Marc Savy <msavy@redhat.com> */ @SuppressWarnings("nls") public class CAMutualAuthTest { private Server server; private HttpConfiguration http_config; private Map<String, String> config = new HashMap<>(); @Rule public ExpectedException exception = ExpectedException.none(); @Before public void setupJetty() throws Exception { server = new Server(); server.setStopAtShutdown(true); http_config = new HttpConfiguration(); http_config.setSecureScheme("https"); SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(getResourcePath("2waytest/mutual_trust_via_ca/service_ks.jks")); sslContextFactory.setKeyStorePassword("password"); sslContextFactory.setKeyManagerPassword("password"); sslContextFactory.setTrustStorePath(getResourcePath("2waytest/mutual_trust_via_ca/common_ts.jks")); sslContextFactory.setTrustStorePassword("password"); sslContextFactory.setNeedClientAuth(true); HttpConfiguration https_config = new HttpConfiguration(http_config); https_config.addCustomizer(new SecureRequestCustomizer()); ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory,"http/1.1"), new HttpConnectionFactory(https_config)); sslConnector.setPort(8008); server.addConnector(sslConnector); // Thanks to Jetty getting started guide. server.setHandler(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { Enumeration<String> z = request.getAttributeNames(); while (z.hasMoreElements()) { String elem = z.nextElement(); System.out.println(elem + " - " + request.getAttribute(elem)); } response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); response.getWriter().println("apiman"); } }); server.start(); } @After public void destroyJetty() throws Exception { server.stop(); server.destroy(); config.clear(); } ApiRequest request = new ApiRequest(); Api api = new Api(); { request.setApiKey("12345"); request.setDestination("/"); request.getHeaders().put("test", "it-worked"); request.setTransportSecure(true); request.setRemoteAddr("https://localhost:8008/"); request.setType("GET"); api.setEndpoint("https://localhost:8008/"); api.getEndpointProperties().put(RequiredAuthType.ENDPOINT_AUTHORIZATION_TYPE, "mtls"); } /** * Scenario: * - CA inherited trust * - gateway trusts API via CA * - API trusts gateway via CA */ @Test public void shouldSucceedWithValidMTLS() { config.put(TLSOptions.TLS_TRUSTSTORE, getResourcePath("2waytest/mutual_trust_via_ca/common_ts.jks")); config.put(TLSOptions.TLS_TRUSTSTOREPASSWORD, "password"); config.put(TLSOptions.TLS_KEYSTORE, getResourcePath("2waytest/mutual_trust_via_ca/gateway_ks.jks")); config.put(TLSOptions.TLS_KEYSTOREPASSWORD, "password"); config.put(TLSOptions.TLS_KEYPASSWORD, "password"); config.put(TLSOptions.TLS_ALLOWANYHOST, "true"); config.put(TLSOptions.TLS_ALLOWSELFSIGNED, "false"); HttpConnectorFactory factory = new HttpConnectorFactory(config); IApiConnector connector = factory.createConnector(request, api, RequiredAuthType.MTLS, false); IApiConnection connection = connector.connect(request, new IAsyncResultHandler<IApiConnectionResponse>() { @Override public void handle(IAsyncResult<IApiConnectionResponse> result) { if (result.isError()) throw new RuntimeException(result.getError()); Assert.assertTrue(result.isSuccess()); } }); connection.end(); } /** * Scenario: * - CA is only in API trust store, missing from gateway trust store * - Gateway does not trust API, as it does not trust CA * - API trusts gateway via CA */ @Test public void shouldFailWhenCANotTrusted() { // Keystore does not trust the root CA API is signed with. config.put(TLSOptions.TLS_TRUSTSTORE, getResourcePath("2waytest/basic_mutual_auth/gateway_ts.jks")); config.put(TLSOptions.TLS_TRUSTSTOREPASSWORD, "password"); config.put(TLSOptions.TLS_KEYSTORE, getResourcePath("2waytest/mutual_trust_via_ca/gateway_ks.jks")); config.put(TLSOptions.TLS_KEYSTOREPASSWORD, "password"); config.put(TLSOptions.TLS_KEYPASSWORD, "password"); config.put(TLSOptions.TLS_ALLOWANYHOST, "true"); config.put(TLSOptions.TLS_ALLOWSELFSIGNED, "false"); HttpConnectorFactory factory = new HttpConnectorFactory(config); IApiConnector connector = factory.createConnector(request, api, RequiredAuthType.MTLS, false); IApiConnection connection = connector.connect(request, new IAsyncResultHandler<IApiConnectionResponse>() { @Override public void handle(IAsyncResult<IApiConnectionResponse> result) { Assert.assertTrue(result.isError()); System.out.println(result.getError()); Assert.assertTrue(result.getError() instanceof ConnectorException); } }); exception.expect(RuntimeException.class); connection.end(); } private String getResourcePath(String res) { URL resource = CAMutualAuthTest.class.getResource(res); try { return Paths.get(resource.toURI()).toFile().getAbsolutePath(); } catch (URISyntaxException e) { throw new RuntimeException(e); } } }