From fb2511d711a371ed4ab42d8accc1cff638351217 Mon Sep 17 00:00:00 2001 From: Luca Rota Date: Thu, 12 Dec 2024 16:07:55 +0100 Subject: [PATCH] Issue #12428 - ALPN Processor for Bouncy Castle FIPS --- .../jetty-alpn-bouncycastle-client/pom.xml | 62 ++++++++ .../src/main/java/module-info.java | 23 +++ .../BouncycastleClientALPNProcessor.java | 100 ++++++++++++ ....eclipse.jetty.io.ssl.ALPNProcessor$Client | 1 + .../client/BouncycastleHTTP2ClientTest.java | 108 +++++++++++++ .../test/resources/jetty-logging.properties | 2 + .../jetty-alpn-bouncycastle-server/pom.xml | 90 +++++++++++ .../src/main/java/module-info.java | 25 +++ .../BouncycastleServerALPNProcessor.java | 89 +++++++++++ ....eclipse.jetty.io.ssl.ALPNProcessor$Server | 1 + .../server/ConscryptHTTP2ServerTest.java | 150 ++++++++++++++++++ .../test/resources/jetty-logging.properties | 3 + .../src/test/resources/keystore.p12 | Bin 0 -> 2742 bytes jetty-core/jetty-alpn/pom.xml | 2 + pom.xml | 6 + 15 files changed, 662 insertions(+) create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/pom.xml create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/module-info.java create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/org/eclipse/jetty/alpn/bouncycastle/client/BouncycastleClientALPNProcessor.java create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/java/org/eclipse/jetty/alpn/java/client/BouncycastleHTTP2ClientTest.java create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/resources/jetty-logging.properties create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/pom.xml create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/module-info.java create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/org/eclipse/jetty/alpn/bouncycastle/server/BouncycastleServerALPNProcessor.java create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptHTTP2ServerTest.java create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/jetty-logging.properties create mode 100644 jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/keystore.p12 diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/pom.xml new file mode 100644 index 000000000000..955c5218fb4d --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + + org.eclipse.jetty + jetty-alpn + 12.0.16-SNAPSHOT + + jetty-alpn-bouncycastle-client + Core :: ALPN :: Bouncy Castle Client + + + ${project.groupId}.alpn.bouncycastle.client + + + + + org.bouncycastle + bctls-fips + + + org.eclipse.jetty + jetty-alpn-client + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.http2 + jetty-http2-client + test + + + + + + org.apache.felix + maven-bundle-plugin + true + + + Bouncy Castle Client ALPN + ${osgi.slf4j.import.packages},org.bouncycastle;version="${bouncycastle.version}",* + * + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional + osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Client + <_nouses>true + + + + + + + diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/module-info.java b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/module-info.java new file mode 100644 index 000000000000..910ab12cacf5 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/module-info.java @@ -0,0 +1,23 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +module org.eclipse.jetty.alpn.bouncycastle.client +{ + requires org.slf4j; + + requires transitive org.eclipse.jetty.alpn.client; + requires org.bouncycastle.fips.tls; + + provides org.eclipse.jetty.io.ssl.ALPNProcessor.Client with + org.eclipse.jetty.alpn.bouncycastle.client.BouncycastleClientALPNProcessor; +} diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/org/eclipse/jetty/alpn/bouncycastle/client/BouncycastleClientALPNProcessor.java b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/org/eclipse/jetty/alpn/bouncycastle/client/BouncycastleClientALPNProcessor.java new file mode 100644 index 000000000000..55b1fbf2d34c --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/java/org/eclipse/jetty/alpn/bouncycastle/client/BouncycastleClientALPNProcessor.java @@ -0,0 +1,100 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.alpn.bouncycastle.client; + +import java.security.Security; +import java.util.List; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; +import org.eclipse.jetty.alpn.client.ALPNClientConnection; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.ssl.ALPNProcessor; +import org.eclipse.jetty.io.ssl.SslConnection.SslEndPoint; +import org.eclipse.jetty.io.ssl.SslHandshakeListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BouncycastleClientALPNProcessor implements ALPNProcessor.Client +{ + private static final Logger LOG = LoggerFactory.getLogger(BouncycastleClientALPNProcessor.class); + + @Override + public void init() + { + if (Security.getProvider("BCJSSE") == null) + { + Security.addProvider(new BouncyCastleJsseProvider()); + if (LOG.isDebugEnabled()) + LOG.debug("Added BouncyCastle JSSE provider"); + } + } + + @Override + public boolean appliesTo(SSLEngine sslEngine) + { + return sslEngine.getClass().getName().startsWith("org.bouncycastle.jsse.provider."); + } + + @Override + public void configure(SSLEngine sslEngine, Connection connection) + { + try + { + ALPNClientConnection alpn = (ALPNClientConnection)connection; + SSLParameters sslParameters = sslEngine.getSSLParameters(); + List protocols = alpn.getProtocols(); + sslParameters.setApplicationProtocols(protocols.toArray(new String[0])); + sslEngine.setSSLParameters(sslParameters); + SslEndPoint sslEndPoint = (SslEndPoint)connection.getEndPoint(); + sslEndPoint.getSslConnection().addHandshakeListener(new ALPNListener(alpn)); + } + catch (RuntimeException x) + { + throw x; + } + catch (Exception x) + { + throw new RuntimeException(x); + } + } + + private final class ALPNListener implements SslHandshakeListener + { + private final ALPNClientConnection alpnConnection; + + private ALPNListener(ALPNClientConnection connection) + { + alpnConnection = connection; + } + + @Override + public void handshakeSucceeded(Event event) + { + try + { + SSLEngine sslEngine = alpnConnection.getSSLEngine(); + String protocol = sslEngine.getApplicationProtocol(); + if (LOG.isDebugEnabled()) + LOG.debug("Selected {} for {}", protocol, alpnConnection); + alpnConnection.selected(protocol); + } + catch (Throwable e) + { + LOG.warn("Unable to process Bouncycastle ApplicationProtocol for {}", alpnConnection, e); + alpnConnection.selected(null); + } + } + } +} diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client new file mode 100644 index 000000000000..af7cfc2b9312 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client @@ -0,0 +1 @@ +org.eclipse.jetty.alpn.bouncycastle.client.BouncycastleClientALPNProcessor diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/java/org/eclipse/jetty/alpn/java/client/BouncycastleHTTP2ClientTest.java b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/java/org/eclipse/jetty/alpn/java/client/BouncycastleHTTP2ClientTest.java new file mode 100644 index 000000000000..e6500b9d42d2 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/java/org/eclipse/jetty/alpn/java/client/BouncycastleHTTP2ClientTest.java @@ -0,0 +1,108 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.alpn.java.client; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.InetSocketAddress; +import java.net.Socket; +import java.security.Security; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.http2.api.Session; +import org.eclipse.jetty.http2.api.Stream; +import org.eclipse.jetty.http2.client.HTTP2Client; +import org.eclipse.jetty.http2.frames.HeadersFrame; +import org.eclipse.jetty.util.FuturePromise; +import org.eclipse.jetty.util.Jetty; +import org.eclipse.jetty.util.Promise; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +public class BouncycastleHTTP2ClientTest +{ + @Tag("external") + @Test + public void testBouncycastleHTTP2Client() throws Exception + { + String host = "webtide.com"; + int port = 443; + + Assumptions.assumeTrue(canConnectTo(host, port)); + + Security.insertProviderAt(new BouncyCastleJsseProvider(), 1); + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setProvider("BCJSSE"); + + try (HTTP2Client client = new HTTP2Client()) + { + client.addBean(sslContextFactory); + client.start(); + + FuturePromise sessionPromise = new FuturePromise<>(); + client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener() {}, sessionPromise); + Session session = sessionPromise.get(15, TimeUnit.SECONDS); + + HttpFields requestFields = HttpFields.build().put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION); + MetaData.Request metaData = new MetaData.Request("GET", HttpURI.from("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields); + HeadersFrame headersFrame = new HeadersFrame(metaData, null, true); + CountDownLatch latch = new CountDownLatch(1); + session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener() + { + @Override + public void onHeaders(Stream stream, HeadersFrame frame) + { + System.err.println(frame); + if (frame.isEndStream()) + latch.countDown(); + stream.demand(); + } + + @Override + public void onDataAvailable(Stream stream) + { + Stream.Data data = stream.readData(); + System.err.println(data); + data.release(); + if (data.frame().isEndStream()) + latch.countDown(); + else + stream.demand(); + } + }); + + assertTrue(latch.await(15, TimeUnit.SECONDS)); + } + } + + private boolean canConnectTo(String host, int port) + { + try + { + new Socket(host, port).close(); + return true; + } + catch (Throwable x) + { + return false; + } + } +} diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/resources/jetty-logging.properties b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/resources/jetty-logging.properties new file mode 100644 index 000000000000..56cc73e5d684 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/src/test/resources/jetty-logging.properties @@ -0,0 +1,2 @@ +# Jetty Logging using jetty-slf4j-impl +#org.eclipse.jetty.LEVEL=DEBUG diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/pom.xml new file mode 100644 index 000000000000..a6f488e39058 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/pom.xml @@ -0,0 +1,90 @@ + + + + 4.0.0 + + org.eclipse.jetty + jetty-alpn + 12.0.16-SNAPSHOT + + jetty-alpn-bouncycastle-server + Core :: ALPN :: Bouncy Castle Server + + + ${project.groupId}.alpn.bouncycastle.server + + + + + org.bouncycastle + bctls-fips + + + org.eclipse.jetty + jetty-alpn-server + + + org.eclipse.jetty + jetty-io + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-alpn-conscrypt-client + test + + + org.eclipse.jetty + jetty-client + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.http2 + jetty-http2-client + test + + + org.eclipse.jetty.http2 + jetty-http2-client-transport + test + + + org.eclipse.jetty.http2 + jetty-http2-server + test + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + Bouncy Castle ALPN + ${osgi.slf4j.import.packages},org.bouncycastle;version="${bouncycastle.version}",* + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional + osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server + <_nouses>true + + + + + maven-surefire-plugin + + @{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.alpn.bouncycastle.server=org.eclipse.jetty.server + + + + + diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/module-info.java b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/module-info.java new file mode 100644 index 000000000000..14353f4aecc6 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/module-info.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +import org.eclipse.jetty.alpn.bouncycastle.server.BouncycastleServerALPNProcessor; + +module org.eclipse.jetty.alpn.conscrypt.server +{ + requires org.slf4j; + + requires transitive org.eclipse.jetty.alpn.server; + requires org.bouncycastle.fips.tls; + + provides org.eclipse.jetty.io.ssl.ALPNProcessor.Server with + BouncycastleServerALPNProcessor; +} diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/org/eclipse/jetty/alpn/bouncycastle/server/BouncycastleServerALPNProcessor.java b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/org/eclipse/jetty/alpn/bouncycastle/server/BouncycastleServerALPNProcessor.java new file mode 100644 index 000000000000..38277a393175 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/java/org/eclipse/jetty/alpn/bouncycastle/server/BouncycastleServerALPNProcessor.java @@ -0,0 +1,89 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.alpn.bouncycastle.server; + +import java.util.List; +import java.util.function.BiFunction; +import javax.net.ssl.SSLEngine; +import org.eclipse.jetty.alpn.server.ALPNServerConnection; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.ssl.ALPNProcessor; +import org.eclipse.jetty.io.ssl.SslConnection.SslEndPoint; +import org.eclipse.jetty.io.ssl.SslHandshakeListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BouncycastleServerALPNProcessor implements ALPNProcessor.Server +{ + private static final Logger LOG = LoggerFactory.getLogger(BouncycastleServerALPNProcessor.class); + + @Override + public boolean appliesTo(SSLEngine sslEngine) + { + return sslEngine.getClass().getName().startsWith("org.bouncycastle.jsse.provider."); + } + + @Override + public void configure(SSLEngine sslEngine, Connection connection) + { + sslEngine.setHandshakeApplicationProtocolSelector(new ALPNCallback((ALPNServerConnection)connection)); + } + + private final class ALPNCallback implements BiFunction, String>, SslHandshakeListener + { + private final ALPNServerConnection alpnConnection; + + private ALPNCallback(ALPNServerConnection connection) + { + alpnConnection = connection; + SslEndPoint sslEndPoint = (SslEndPoint)alpnConnection.getEndPoint(); + sslEndPoint.getSslConnection().addHandshakeListener(this); + } + + @Override + public String apply(SSLEngine engine, List protocols) + { + try + { + if (LOG.isDebugEnabled()) + LOG.debug("apply {} {}", alpnConnection, protocols); + alpnConnection.select(protocols); + return alpnConnection.getProtocol(); + } + catch (Throwable x) + { + // Cannot negotiate the protocol, return null to have + // JSSE send Alert.NO_APPLICATION_PROTOCOL to the client. + return null; + } + } + + @Override + public void handshakeSucceeded(Event event) + { + String protocol = alpnConnection.getProtocol(); + if (LOG.isDebugEnabled()) + LOG.debug("TLS handshake succeeded, protocol={} for {}", protocol, alpnConnection); + if (protocol == null) + alpnConnection.unsupported(); + } + + @Override + public void handshakeFailed(Event event, Throwable failure) + { + if (LOG.isDebugEnabled()) + LOG.debug("TLS handshake failed {}", alpnConnection, failure); + } + } +} diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server new file mode 100644 index 000000000000..242bac9dde9d --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server @@ -0,0 +1 @@ +org.eclipse.jetty.alpn.bouncycastle.server.BouncycastleServerALPNProcessor diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptHTTP2ServerTest.java b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptHTTP2ServerTest.java new file mode 100644 index 000000000000..576efa632b95 --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptHTTP2ServerTest.java @@ -0,0 +1,150 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.alpn.conscrypt.server; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.Security; + +import org.conscrypt.OpenSSLProvider; +import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http2.client.HTTP2Client; +import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +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.util.Callback; +import org.eclipse.jetty.util.JavaVersion; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Test server that verifies that the Conscrypt ALPN mechanism works for both server and client side + */ +@DisabledOnOs(architectures = "aarch64", disabledReason = "Conscrypt does not provide aarch64 native libs as of version 2.5.2") +public class ConscryptHTTP2ServerTest +{ + static + { + Security.addProvider(new OpenSSLProvider()); + } + + private final HttpConfiguration httpsConfig = new HttpConfiguration(); + private final Server server = new Server(); + + private SslContextFactory.Server newServerSslContextFactory() + { + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + configureSslContextFactory(sslContextFactory); + return sslContextFactory; + } + + private SslContextFactory.Client newClientSslContextFactory() + { + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + configureSslContextFactory(sslContextFactory); + sslContextFactory.setEndpointIdentificationAlgorithm(null); + return sslContextFactory; + } + + private void configureSslContextFactory(SslContextFactory sslContextFactory) + { + Path path = Paths.get("src", "test", "resources"); + File keys = path.resolve("keystore.p12").toFile(); + sslContextFactory.setKeyStorePath(keys.getAbsolutePath()); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setProvider("Conscrypt"); + if (JavaVersion.VERSION.getPlatform() < 9) + { + // Conscrypt enables TLSv1.3 by default but it's not supported in Java 8. + sslContextFactory.addExcludeProtocols("TLSv1.3"); + } + } + + @BeforeEach + public void startServer() throws Exception + { + httpsConfig.setSecureScheme("https"); + httpsConfig.setSendXPoweredBy(true); + httpsConfig.setSendServerVersion(true); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + + HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig); + HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig); + ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory(); + alpn.setDefaultProtocol(http.getProtocol()); + SslConnectionFactory ssl = new SslConnectionFactory(newServerSslContextFactory(), alpn.getProtocol()); + + ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http); + http2Connector.setPort(0); + server.addConnector(http2Connector); + + server.setHandler(new Handler.Abstract() + { + @Override + public boolean handle(Request request, Response response, Callback callback) + { + callback.succeeded(); + return true; + } + }); + + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testSimpleRequest() throws Exception + { + ClientConnector clientConnector = new ClientConnector(); + clientConnector.setSslContextFactory(newClientSslContextFactory()); + HTTP2Client h2Client = new HTTP2Client(clientConnector); + try (HttpClient client = new HttpClient(new HttpClientTransportOverHTTP2(h2Client))) + { + client.start(); + int port = ((ServerConnector)server.getConnectors()[0]).getLocalPort(); + ContentResponse contentResponse = client.GET("https://localhost:" + port); + assertEquals(200, contentResponse.getStatus()); + } + } + + @Test + public void testSNIRequired() throws Exception + { + // The KeyStore contains 1 certificate with two DNS names. + httpsConfig.getCustomizer(SecureRequestCustomizer.class).setSniRequired(true); + testSimpleRequest(); + } +} diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/jetty-logging.properties b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/jetty-logging.properties new file mode 100644 index 000000000000..2f2fa6d19d9e --- /dev/null +++ b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Jetty Logging using jetty-slf4j-impl +#org.eclipse.jetty.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG diff --git a/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/keystore.p12 b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..9c35858fe8d7ce144c49a04afc3c7d7d6377fc02 GIT binary patch literal 2742 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 Wk(qkj_NLS@W`Z0xgrWW`g8l}^A@xK6 literal 0 HcmV?d00001 diff --git a/jetty-core/jetty-alpn/pom.xml b/jetty-core/jetty-alpn/pom.xml index 1aa8b6857491..e2452f1949a8 100644 --- a/jetty-core/jetty-alpn/pom.xml +++ b/jetty-core/jetty-alpn/pom.xml @@ -14,6 +14,8 @@ jetty-alpn-client jetty-alpn-conscrypt-client jetty-alpn-conscrypt-server + jetty-alpn-bouncycastle-client + jetty-alpn-bouncycastle-server jetty-alpn-java-client jetty-alpn-java-server jetty-alpn-server diff --git a/pom.xml b/pom.xml index c91a0cc208f1..0907a4f65e6b 100644 --- a/pom.xml +++ b/pom.xml @@ -167,6 +167,7 @@ 9.7.1 4.2.2 7.0.0 + 2.0.19 3.6.0 1.5 3.2.1 @@ -609,6 +610,11 @@ awaitility ${awaitility.version} + + org.bouncycastle + bctls-fips + ${bouncycastle.version} + org.codehaus.plexus plexus-classworlds