Add support for BoringSSL provider via netty-tcnative

This commit is contained in:
Chris Eager authored and Chris Eager committed 2026-09-30 16:22:07 -04:00
1 parent 60738acd17
commit e90c8685f8
10 files changed
+274 -40

No files matched your search

+7
View File
@@ -78,6 +78,8 @@
<luajava.version>4.1.0</luajava.version>
<micrometer.version>1.17.1</micrometer.version>
<netty.version>4.2.18.Final</netty.version>
<!-- netty-bom doesn't manage the BoringSSL uber-jar; keep in sync with netty's tcnative.version -->
<netty-tcnative.version>2.0.84.Final</netty-tcnative.version>
<!-- must be greater than or equal to the value from Google libraries-bom, see https://protobuf.dev/support/cross-version-runtime-guarantee/ -->
<!-- the latest 4.x should be fine https://central.sonatype.com/artifact/com.google.protobuf/protobuf-java/versions -->
<protobuf.version>4.36.0</protobuf.version>
@@ -141,6 +143,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${netty-tcnative.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-bom</artifactId>
+6
View File
@@ -513,6 +513,11 @@
<artifactId>netty-resolver-dns</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
@@ -795,6 +800,7 @@
<jvmFlag>-Djdk.nio.maxCachedBufferSize=262144</jvmFlag>
<jvmFlag>-Dlog4j2.formatMsgNoLookups=true</jvmFlag>
<jvmFlag>-Djdk.tls.server.newSessionTicketCount=0</jvmFlag>
<jvmFlag>-Djdk.tls.server.enableSessionTicketExtension=false</jvmFlag>
<jvmFlag>-XX:MaxRAMPercentage=75</jvmFlag>
<jvmFlag>-XX:+HeapDumpOnOutOfMemoryError</jvmFlag>
<jvmFlag>-XX:HeapDumpPath=/tmp/heapdump.bin</jvmFlag>
@@ -666,7 +666,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
final ScheduledExecutorService retryExecutor = ScheduledExecutorServiceBuilder.of(environment, "retry")
.threads(16).build();
final ScheduledExecutorService registrationIdentityTokenRefreshExecutor =
ScheduledExecutorServiceBuilder.of(environment, "registrationIdentityTokenRefresh").threads(1).build();
ScheduledExecutorServiceBuilder.of(environment, "registrationIdentityTokenRefresh").threads(1).build();
final ScheduledExecutorService presenceRenewalExecutor =
ScheduledExecutorServiceBuilder.of(environment, "presenceRenewal").threads(4).build();
@@ -726,11 +726,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
final ManagedEventLoopGroup<NioEventLoopGroup> dnsResolutionEventLoopGroup = new ManagedEventLoopGroup<>(new NioEventLoopGroup());
final DnsNameResolver cloudflareDnsResolver = new DnsNameResolverBuilder(dnsResolutionEventLoopGroup.getEventLoopGroup().next())
.resolvedAddressTypes(ResolvedAddressTypes.IPV6_PREFERRED)
.completeOncePreferredResolved(false)
.channelType(NioDatagramChannel.class)
.socketChannelType(NioSocketChannel.class)
.build();
.resolvedAddressTypes(ResolvedAddressTypes.IPV6_PREFERRED)
.completeOncePreferredResolved(false)
.channelType(NioDatagramChannel.class)
.socketChannelType(NioSocketChannel.class)
.build();
ExternalServiceCredentialsGenerator directoryV2CredentialsGenerator = DirectoryV2Controller.credentialsGenerator(
config.getDirectoryV2Configuration().getDirectoryV2ClientConfiguration());
@@ -1218,7 +1218,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
grpcLocalAddress);
@Nullable final Mapping<String, SslContext> sniMapping = config.getGrpc().h2c()
? null
: SniMapper.buildSniMapping(config.getTlsKeyStoreConfiguration().path(), config.getTlsKeyStoreConfiguration().password().value());
: SniMapper.buildSniMapping(config.getTlsKeyStoreConfiguration().path(),
config.getTlsKeyStoreConfiguration().password().value(),
config.getGrpc().sslProvider());
final OmnibusH2Server omnibusH2Server = new OmnibusH2Server(
sniMapping,
omnibusNioEventLoopGroup.getEventLoopGroup(),
@@ -4,6 +4,7 @@
*/
package org.whispersystems.textsecuregcm.configuration;
import io.netty.handler.ssl.SslProvider;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
@@ -15,13 +16,15 @@ import java.time.Duration;
/// @param websocketPort The port of a listening websocket server for handling legacy requests
/// @param idleTimeout The duration after which an idle connection may be disconnected
/// @param h2c If true, listen for plaintext h2c with prior-knowledge
/// @param sslProvider The TLS implementation used to terminate TLS. Defaults to `OPENSSL`.
public record GrpcConfiguration(
@NotNull String bindAddress,
@NotNull Integer port,
@NotNull String websocketAddress,
@NotNull Integer websocketPort,
@NotNull Duration idleTimeout,
boolean h2c) {
boolean h2c,
@NotNull SslProvider sslProvider) {
public GrpcConfiguration {
if (bindAddress == null || bindAddress.isEmpty()) {
@@ -33,5 +36,8 @@ public record GrpcConfiguration(
if (idleTimeout == null) {
idleTimeout = Duration.ofMinutes(5);
}
if (sslProvider == null) {
sslProvider = SslProvider.OPENSSL;
}
}
}
@@ -0,0 +1,123 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.grpc.net;
import com.google.common.annotations.VisibleForTesting;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.ReferenceCountedOpenSslEngine;
import io.netty.internal.tcnative.SSL;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
/// A server key manager for netty's BoringSSL provider that uses a client's signature algorithm preferences, enabling
/// support for ed25519.
///
/// This works around [netty#10916](https://github.com/netty/netty/issues/10916) by addressing two issues in key selection:
///
/// 1. Netty’s engine only declares support for RSA- and EC-family key types, and
/// 2. the handshake session's peer signature algorithm converter (`io.netty.handler.ssl.SignatureAlgorithmConverter`)
/// doesn't match BoringSSL's `ed25519`, so ed25519 gets dropped, even if one is presented as a candidate
class BoringSslServerKeyManager extends X509ExtendedKeyManager {
private final X509KeyManager delegate;
@VisibleForTesting
BoringSslServerKeyManager(final X509KeyManager delegate) {
if (!"BoringSSL".equals(OpenSsl.versionString())) {
throw new IllegalStateException("BoringSSL required, found " + OpenSsl.versionString());
}
this.delegate = delegate;
}
/// @throws IllegalArgumentException if `keyManagers` contains no [X509KeyManager]
static BoringSslServerKeyManager wrap(final KeyManager[] keyManagers) {
return Arrays.stream(keyManagers)
.filter(X509KeyManager.class::isInstance)
.map(X509KeyManager.class::cast)
.findFirst()
.map(BoringSslServerKeyManager::new)
.orElseThrow(() -> new IllegalArgumentException("No X509KeyManager found"));
}
@Override
public String chooseEngineServerAlias(final String keyType, final Principal[] issuers, final SSLEngine engine) {
if (!(engine instanceof ReferenceCountedOpenSslEngine openSslEngine)) {
throw new IllegalArgumentException("BoringSslServerKeyManager requires netty's OpenSSL provider");
}
@Nullable final String[] peerSignatureAlgorithms = SSL.getSigAlgs(openSslEngine.sslPointer());
if (peerSignatureAlgorithms == null) {
return null;
}
for (final String signatureAlgorithm : peerSignatureAlgorithms) {
final Optional<String> maybeAlias = getJdkKeyType(signatureAlgorithm)
// The Netty OpenSSL engine doesn't advertise support for Ed25519, and so we can't include the engine in the
// delegate's selection process. Instead, we loop through the peer signature algorithms and find a matching key.
.map(jdkKeyType -> delegate.chooseServerAlias(jdkKeyType, issuers, null));
if (maybeAlias.isPresent()) {
return maybeAlias.get();
}
}
return null;
}
/// Maps a [BoringSSL signature algorithm name](https://github.com/google/boringssl/blob/8525ff3/ssl/ssl_privkey.cc#L420-L433)
/// to the JDK key type that can produce it
@VisibleForTesting
static Optional<String> getJdkKeyType(final String signatureAlgorithm) {
if (signatureAlgorithm.equals("ed25519")) {
return Optional.of("EdDSA");
} else if (signatureAlgorithm.startsWith("ecdsa_")) {
return Optional.of("EC");
} else if (signatureAlgorithm.startsWith("rsa_pss_rsae_")) {
return Optional.of("RSA");
}
// We don't need to be exhaustive, because we require TLS 1.3 and only use a limited number of key types.
return Optional.empty();
}
@Override
public String chooseServerAlias(final String keyType, final Principal[] issuers, final Socket socket) {
return delegate.chooseServerAlias(keyType, issuers, socket);
}
@Override
public String[] getServerAliases(final String keyType, final Principal[] issuers) {
return delegate.getServerAliases(keyType, issuers);
}
@Override
public String chooseClientAlias(final String[] keyTypes, final Principal[] issuers, final Socket socket) {
return delegate.chooseClientAlias(keyTypes, issuers, socket);
}
@Override
public String[] getClientAliases(final String keyType, final Principal[] issuers) {
return delegate.getClientAliases(keyType, issuers);
}
@Override
public X509Certificate[] getCertificateChain(final String alias) {
return delegate.getCertificateChain(alias);
}
@Override
public PrivateKey getPrivateKey(final String alias) {
return delegate.getPrivateKey(alias);
}
}
@@ -7,8 +7,10 @@ package org.whispersystems.textsecuregcm.grpc.net;
import com.google.common.annotations.VisibleForTesting;
import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.util.Mapping;
import java.io.FileInputStream;
import java.io.IOException;
@@ -49,25 +51,35 @@ public class SniMapper {
///
/// @param keyStorePath The path to the [KeyStore]
/// @param keyStorePassword The password for the keyStore
/// @param sslProvider The TLS implementation to use
/// @return A [Mapping] that maps domains to the corresponding [SslContext] containing the certificates for that
/// domain
public static Mapping<String, SslContext> buildSniMapping(final String keyStorePath, final String keyStorePassword)
throws IOException {
public static Mapping<String, SslContext> buildSniMapping(final String keyStorePath, final String keyStorePassword,
final SslProvider sslProvider) throws IOException {
try (final FileInputStream fis = new FileInputStream(keyStorePath)) {
return buildSniMapping(fis, keyStorePassword);
return buildSniMapping(fis, keyStorePassword, sslProvider);
}
}
@VisibleForTesting
static Mapping<String, SslContext> buildSniMapping(final InputStream keyStore, final String keyStorePassword)
throws IOException {
static Mapping<String, SslContext> buildSniMapping(final InputStream keyStore, final String keyStorePassword,
final SslProvider sslProvider) throws IOException {
if (sslProvider != SslProvider.JDK && sslProvider != SslProvider.OPENSSL) {
throw new IllegalArgumentException("Unsupported SSL provider: " + sslProvider);
}
if (sslProvider == SslProvider.OPENSSL) {
OpenSsl.ensureAvailability();
}
try {
final Map<String, KeyStore> domainKeyStores = partitionByDomain(keyStore, keyStorePassword.toCharArray());
final Map<String, SslContext> sslContextsByDomain = new HashMap<>();
for (final Map.Entry<String, KeyStore> entry : domainKeyStores.entrySet()) {
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(entry.getValue(), keyStorePassword.toCharArray());
sslContextsByDomain.put(entry.getKey(), buildSslContext(kmf));
sslContextsByDomain.put(entry.getKey(), buildSslContext(kmf, sslProvider));
}
// Netty expects the SNI mapping to always return an SslContext. Per RFC-6066 it's valid to continue the handshake
@@ -78,15 +90,23 @@ public class SniMapper {
.orElseThrow(() -> new IllegalArgumentException("Key store contained no certificates"))
.getValue();
logger.info("Loaded TLS contexts for domains: {}", sslContextsByDomain.keySet());
logger.info("Loaded {} TLS contexts for domains: {}", sslProvider, sslContextsByDomain.keySet());
return hostname -> sslContextsByDomain.getOrDefault(hostname, defaultSslContext);
} catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | UnrecoverableKeyException e) {
throw new IOException("Failed to load keystore", e);
}
}
private static SslContext buildSslContext(final KeyManagerFactory kmf) throws SSLException {
return SslContextBuilder.forServer(kmf)
private static SslContext buildSslContext(final KeyManagerFactory kmf, final SslProvider sslProvider)
throws SSLException {
final SslContextBuilder sslContextBuilder = sslProvider == SslProvider.OPENSSL
// work around netty's OpenSSL provider not selecting Ed25519 certificates on its own
? SslContextBuilder.forServer(BoringSslServerKeyManager.wrap(kmf.getKeyManagers()))
: SslContextBuilder.forServer(kmf);
return sslContextBuilder
.sslProvider(sslProvider)
.applicationProtocolConfig(new ApplicationProtocolConfig(
ApplicationProtocolConfig.Protocol.ALPN,
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
@@ -0,0 +1,30 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.grpc.net;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class BoringSslServerKeyManagerTest {
@ParameterizedTest
@CsvSource(nullValues = "null", value = {
"ed25519, EdDSA",
"ecdsa_sha256, EC",
"ecdsa_secp256r1_sha256, EC",
"rsa_pss_rsae_sha256, RSA",
"rsa_pss_rsae_sha512, RSA",
"rsa_pkcs1_sha256, null",
"rsa_pss_pss_sha256, null",
"ed448, null",
})
void getJdkKeyType(final String boringSslSignatureAlgorithm, @Nullable final String expectedKeyType) {
assertEquals(Optional.ofNullable(expectedKeyType), BoringSslServerKeyManager.getJdkKeyType(boringSslSignatureAlgorithm));
}
}
@@ -51,6 +51,7 @@ import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.pkitesting.CertificateBuilder;
import io.netty.util.Mapping;
@@ -142,7 +143,7 @@ class OmnibusH2ServerTest {
.store(byteArrayOutputStream, keyStorePassword);
sniMapping = SniMapper.buildSniMapping(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()),
new String(keyStorePassword));
new String(keyStorePassword), SslProvider.OPENSSL);
nioEventLoopGroup = new NioEventLoopGroup();
localEventLoopGroup = new DefaultEventLoopGroup();
@@ -5,7 +5,9 @@
package org.whispersystems.textsecuregcm.grpc.net;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import io.netty.bootstrap.Bootstrap;
@@ -23,6 +25,7 @@ import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.pkitesting.CertificateBuilder;
import io.netty.pkitesting.X509Bundle;
@@ -33,11 +36,16 @@ import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
@@ -45,10 +53,9 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
class SniMapperTest {
@@ -63,7 +70,8 @@ class SniMapperTest {
};
private static DefaultEventLoopGroup eventLoopGroup;
private static Mapping<String, SslContext> sniMapping;
private static final Map<SslProvider, Mapping<String, SslContext>> sniMappingsByProvider =
new EnumMap<>(SslProvider.class);
private Channel serverChannel;
@@ -96,14 +104,16 @@ class SniMapperTest {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
keyStore.store(byteArrayOutputStream, keyStorePassword);
sniMapping = SniMapper.buildSniMapping(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()),
new String(keyStorePassword));
for (final SslProvider sslProvider : new SslProvider[] { SslProvider.JDK, SslProvider.OPENSSL }) {
sniMappingsByProvider.put(sslProvider, SniMapper.buildSniMapping(
new ByteArrayInputStream(byteArrayOutputStream.toByteArray()), new String(keyStorePassword), sslProvider));
}
eventLoopGroup = new DefaultEventLoopGroup();
}
@BeforeEach
void setUp() throws Exception {
private void startServer(final SslProvider sslProvider) throws InterruptedException {
final Mapping<String, SslContext> sniMapping = sniMappingsByProvider.get(sslProvider);
final LocalAddress localAddress = new LocalAddress(SniMapper.class.getSimpleName());
serverChannel = new ServerBootstrap()
.group(eventLoopGroup)
@@ -131,9 +141,11 @@ class SniMapperTest {
eventLoopGroup.shutdownGracefully(1, 1000, TimeUnit.MILLISECONDS).sync();
}
@Test
void unknownDomain() throws Exception {
assertNotNull(sniMapping.map("unknown.example.com"));
@ParameterizedTest
@EnumSource(value = SslProvider.class, names = {"JDK", "OPENSSL"})
void unknownDomain(final SslProvider sslProvider) throws Exception {
assertNotNull(sniMappingsByProvider.get(sslProvider).map("unknown.example.com"));
startServer(sslProvider);
final X509Certificate defaultCertificate = connectAndGetServerCertificate("unknown.example.com", null);
// bar.example.com is the lexicographically first domain, so we should default to it.
@@ -141,7 +153,7 @@ class SniMapperTest {
}
static List<Arguments> selectCertificate() {
return List.of(
final List<Arguments> cases = List.of(
Arguments.of(FOO_DOMAIN, List.of(), "Ed25519"),
Arguments.of(BAR_DOMAIN, List.of(), "Ed25519"),
Arguments.of(BAR_DOMAIN, List.of("ed25519"), "Ed25519"),
@@ -149,21 +161,44 @@ class SniMapperTest {
Arguments.of(FOO_DOMAIN, List.of("rsa_pss_rsae_sha256", "rsa_pss_rsae_sha384", "rsa_pss_rsae_sha512", "rsa_pkcs1_sha256", "ed25519"), "SHA256withRSA"),
Arguments.of(FOO_DOMAIN, List.of("ed25519", "rsa_pss_rsae_sha256", "rsa_pss_rsae_sha384", "rsa_pss_rsae_sha512"), "Ed25519")
);
// Each provider should select the same certificate
return Stream.of(SslProvider.JDK, SslProvider.OPENSSL)
.flatMap(sslProvider -> cases.stream().map(arguments -> {
final Object[] args = arguments.get();
return Arguments.of(sslProvider, args[0], args[1], args[2]);
}))
.toList();
}
@ParameterizedTest
@MethodSource
void selectCertificate(final String sni, final List<String> signatureSchemes, final String expectedSigAlgorithm)
throws Exception {
void selectCertificate(final SslProvider sslProvider, final String sni, final List<String> signatureSchemes,
final String expectedSigAlgorithm) throws Exception {
startServer(sslProvider);
final X509Certificate serverCert = connectAndGetServerCertificate(sni, signatureSchemes.toArray(String[]::new));
assertNotNull(serverCert);
assertCertificateIsForDomain(serverCert, sni);
assertEquals(expectedSigAlgorithm, serverCert.getSigAlgName());
}
@ParameterizedTest
@EnumSource(value = SslProvider.class, names = {"JDK", "OPENSSL"})
void noCommonSignatureAlgorithm(final SslProvider sslProvider) throws Exception {
startServer(sslProvider);
final ExecutionException executionException = assertThrows(ExecutionException.class,
() -> connectAndGetServerCertificate(FOO_DOMAIN, new String[] { "ecdsa_secp256r1_sha256" }),
"server doesn’t have an ECDSA key");
assertInstanceOf(SSLException.class, executionException.getCause());
}
private X509Certificate connectAndGetServerCertificate(final String sniHostname,
final String[] signatureSchemes) throws Exception {
final SslContext clientSsl = SslContextBuilder.forClient()
// the client can always use the JDK provider
.sslProvider(SslProvider.JDK)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.protocols("TLSv1.3")
.build();
@@ -51,7 +51,8 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
class TlsHandshakeMetricsHandlerTest {
@@ -114,9 +115,10 @@ class TlsHandshakeMetricsHandlerTest {
eventLoopGroup.shutdownGracefully(0, 1000, TimeUnit.MILLISECONDS).sync();
}
@Test
void handshake() throws Exception {
final AtomicReference<Channel> lastServerChildChannel = startServer(buildSniMapping());
@ParameterizedTest
@EnumSource(value = SslProvider.class, names = {"JDK", "OPENSSL"})
void handshake(final SslProvider sslProvider) throws Exception {
final AtomicReference<Channel> lastServerChildChannel = startServer(buildSniMapping(sslProvider));
connect(null);
assertEquals(1, getCount(true));
@@ -125,9 +127,11 @@ class TlsHandshakeMetricsHandlerTest {
assertNull(lastServerChildChannel.get().pipeline().get(TlsHandshakeMetricsHandler.class), "the metrics handler should remove itself");
}
@Test
void failedHandshake() throws Exception {
startServer(buildSniMapping());
@ParameterizedTest
@EnumSource(value = SslProvider.class, names = {"JDK", "OPENSSL"})
void failedHandshake(final SslProvider sslProvider) throws Exception {
startServer(buildSniMapping(sslProvider));
// The server only holds an Ed25519 key
connect(new String[] { "rsa_pss_rsae_sha256" });
@@ -136,8 +140,8 @@ class TlsHandshakeMetricsHandlerTest {
assertEquals(1, getCount(false));
}
private static Mapping<String, SslContext> buildSniMapping() throws Exception {
return SniMapper.buildSniMapping(new ByteArrayInputStream(keyStoreBytes), keyStorePassword);
private static Mapping<String, SslContext> buildSniMapping(final SslProvider sslProvider) throws Exception {
return SniMapper.buildSniMapping(new ByteArrayInputStream(keyStoreBytes), keyStorePassword, sslProvider);
}
/// Starts the server and returns a reference to the child channel, for introspection by tests