Skip to content

Commit

Permalink
Issue jetty#12428 - ALPN Processor for Bouncy Castle FIPS
Browse files Browse the repository at this point in the history
  • Loading branch information
lucarota authored and Luca Rota committed Dec 13, 2024
1 parent 86a1c58 commit fb2511d
Show file tree
Hide file tree
Showing 15 changed files with 662 additions and 0 deletions.
62 changes: 62 additions & 0 deletions jetty-core/jetty-alpn/jetty-alpn-bouncycastle-client/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn</artifactId>
<version>12.0.16-SNAPSHOT</version>
</parent>
<artifactId>jetty-alpn-bouncycastle-client</artifactId>
<name>Core :: ALPN :: Bouncy Castle Client</name>

<properties>
<bundle-symbolic-name>${project.groupId}.alpn.bouncycastle.client</bundle-symbolic-name>
</properties>

<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bctls-fips</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-client</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Description>Bouncy Castle Client ALPN</Bundle-Description>
<Import-Package>${osgi.slf4j.import.packages},org.bouncycastle;version="${bouncycastle.version}",*</Import-Package>
<Export-Package>*</Export-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability>
<Provide-Capability>osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Client</Provide-Capability>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<String> 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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.eclipse.jetty.alpn.bouncycastle.client.BouncycastleClientALPNProcessor
Original file line number Diff line number Diff line change
@@ -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<Session> 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;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Jetty Logging using jetty-slf4j-impl
#org.eclipse.jetty.LEVEL=DEBUG
90 changes: 90 additions & 0 deletions jetty-core/jetty-alpn/jetty-alpn-bouncycastle-server/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn</artifactId>
<version>12.0.16-SNAPSHOT</version>
</parent>
<artifactId>jetty-alpn-bouncycastle-server</artifactId>
<name>Core :: ALPN :: Bouncy Castle Server</name>

<properties>
<bundle-symbolic-name>${project.groupId}.alpn.bouncycastle.server</bundle-symbolic-name>
</properties>

<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bctls-fips</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-conscrypt-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client-transport</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-server</artifactId>
<scope>test</scope>
</dependency>

</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Description>Bouncy Castle ALPN</Bundle-Description>
<Import-Package>${osgi.slf4j.import.packages},org.bouncycastle;version="${bouncycastle.version}",*</Import-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability>
<Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server</Provide-Capability>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.alpn.bouncycastle.server=org.eclipse.jetty.server</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading

0 comments on commit fb2511d

Please # to comment.