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 000000000000..9c35858fe8d7
Binary files /dev/null and b/jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/src/test/resources/keystore.p12 differ
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