Skip to content

Commit 9052770

Browse files
committed
Allow to set SSLParameter in TLS configuration
Fixes #11
1 parent 5fcdb40 commit 9052770

File tree

9 files changed

+171
-4
lines changed

9 files changed

+171
-4
lines changed

src/docs/asciidoc/api.adoc

+4
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ The server certificate chain and the client private key are the typical
201201
elements that need to be configured.
202202
|The JDK trust manager and no client private key.
203203

204+
|`tls#sslParameters`
205+
|Set the `SSLParameters` for the `SSLEngine`. The provided parameters will be merged into the parameters returned by `SSLEngine#getSSLParameters()`. Can be used to set SNI information with `SSLParameters#setServerNames(List)`.
206+
|`null`
207+
204208
|`tls#trustEverything`
205209
|Helper to configure a `SslContext` that trusts all server certificates
206210
and does not use a client private key. **Only for development**.

src/main/java/com/rabbitmq/stream/EnvironmentBuilder.java

+17
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.util.List;
2626
import java.util.Map;
2727
import java.util.concurrent.ScheduledExecutorService;
28+
import javax.net.ssl.SSLEngine;
29+
import javax.net.ssl.SSLParameters;
2830

2931
/**
3032
* API to configure and create an {@link Environment}.
@@ -332,6 +334,21 @@ interface TlsConfiguration {
332334
*/
333335
TlsConfiguration sslContext(SslContext sslContext);
334336

337+
/**
338+
* Set {@link SSLParameters} for the {@link javax.net.ssl.SSLEngine}.
339+
*
340+
* <p>Provided {@link SSLParameters} will be merged into the {@link SSLParameters} returned by
341+
* {@link SSLEngine#getSSLParameters()}, that is non-null property values from the provided
342+
* instance will override those in the original instance.
343+
*
344+
* <p>This is typically use to provide SNI information with {@link
345+
* SSLParameters#setServerNames(List)}.
346+
*
347+
* @param sslParameters
348+
* @return the TLS configuration helper
349+
*/
350+
TlsConfiguration sslParameters(SSLParameters sslParameters);
351+
335352
/**
336353
* Convenience method to set a {@link SslContext} that trusts all servers.
337354
*

src/main/java/com/rabbitmq/stream/impl/Client.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -285,12 +285,16 @@ public void write(
285285
if (parameters.sslContext != null) {
286286
SslHandler sslHandler =
287287
parameters.sslContext.newHandler(ch.alloc(), parameters.host, parameters.port);
288+
SSLEngine sslEngine = sslHandler.engine();
289+
SSLParameters sslParameters = sslEngine.getSSLParameters();
288290
if (parameters.tlsHostnameVerification) {
289-
SSLEngine sslEngine = sslHandler.engine();
290-
SSLParameters sslParameters = sslEngine.getSSLParameters();
291291
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
292-
sslEngine.setSSLParameters(sslParameters);
293292
}
293+
if (parameters.sslParameters != null) {
294+
Utils.mergeSslParameters(sslParameters, parameters.sslParameters);
295+
}
296+
sslEngine.setSSLParameters(sslParameters);
297+
294298
ch.pipeline().addFirst("ssl", sslHandler);
295299
}
296300
channelCustomizer.customize(ch);
@@ -1919,6 +1923,7 @@ public static class ClientParameters {
19191923
private ChunkChecksum chunkChecksum = JdkChunkChecksum.CRC32_SINGLETON;
19201924
private MetricsCollector metricsCollector = NoOpMetricsCollector.SINGLETON;
19211925
private SslContext sslContext;
1926+
private SSLParameters sslParameters;
19221927
private boolean tlsHostnameVerification = true;
19231928
private ByteBufAllocator byteBufAllocator;
19241929
private Duration rpcTimeout;
@@ -2065,6 +2070,11 @@ public ClientParameters sslContext(SslContext sslContext) {
20652070
return this;
20662071
}
20672072

2073+
public ClientParameters sslParameters(SSLParameters sslParameters) {
2074+
this.sslParameters = sslParameters;
2075+
return this;
2076+
}
2077+
20682078
public ClientParameters tlsHostnameVerification(boolean tlsHostnameVerification) {
20692079
this.tlsHostnameVerification = tlsHostnameVerification;
20702080
return this;

src/main/java/com/rabbitmq/stream/impl/StreamEnvironment.java

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class StreamEnvironment implements Environment {
145145
clientParametersPrototype.sslContext(sslContext);
146146
clientParametersPrototype.tlsHostnameVerification(
147147
tlsConfiguration.hostnameVerificationEnabled());
148+
clientParametersPrototype.sslParameters(tlsConfiguration.sslParameters());
148149

149150
} catch (SSLException e) {
150151
throw new StreamException("Error while creating Netty SSL context", e);

src/main/java/com/rabbitmq/stream/impl/StreamEnvironmentBuilder.java

+12
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.concurrent.ScheduledExecutorService;
3939
import java.util.stream.Collectors;
4040
import javax.net.ssl.SSLException;
41+
import javax.net.ssl.SSLParameters;
4142
import org.slf4j.Logger;
4243
import org.slf4j.LoggerFactory;
4344

@@ -300,6 +301,7 @@ static final class DefaultTlsConfiguration implements TlsConfiguration {
300301
private boolean enabled = false;
301302
private boolean hostnameVerification = true;
302303
private SslContext sslContext;
304+
private SSLParameters sslParameters;
303305

304306
private DefaultTlsConfiguration(EnvironmentBuilder environmentBuilder) {
305307
this.environmentBuilder = environmentBuilder;
@@ -323,6 +325,12 @@ public TlsConfiguration sslContext(SslContext sslContext) {
323325
return this;
324326
}
325327

328+
@Override
329+
public TlsConfiguration sslParameters(SSLParameters sslParameters) {
330+
this.sslParameters = sslParameters;
331+
return this;
332+
}
333+
326334
@Override
327335
public TlsConfiguration trustEverything() {
328336
LOGGER.warn(
@@ -360,5 +368,9 @@ public boolean hostnameVerificationEnabled() {
360368
public SslContext sslContext() {
361369
return sslContext;
362370
}
371+
372+
public SSLParameters sslParameters() {
373+
return sslParameters;
374+
}
363375
}
364376
}

src/main/java/com/rabbitmq/stream/impl/Utils.java

+90
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import java.util.function.Consumer;
2727
import java.util.function.LongConsumer;
2828
import java.util.function.Predicate;
29+
import java.util.stream.Collectors;
30+
import javax.net.ssl.SSLParameters;
2931
import javax.net.ssl.X509TrustManager;
3032
import org.slf4j.Logger;
3133
import org.slf4j.LoggerFactory;
@@ -213,4 +215,92 @@ public X509Certificate[] getAcceptedIssuers() {
213215
return new X509Certificate[0];
214216
}
215217
}
218+
219+
static void mergeSslParameters(SSLParameters original, SSLParameters provided) {
220+
if (notEmptyArray(provided.getCipherSuites())) {
221+
LOGGER.debug(
222+
"Setting SSLParameters cipherSuites from {} to {}",
223+
arrayToString(original.getCipherSuites()),
224+
arrayToString(provided.getCipherSuites()));
225+
original.setCipherSuites(provided.getCipherSuites());
226+
}
227+
if (notEmptyArray(provided.getProtocols())) {
228+
LOGGER.debug(
229+
"Setting SSLParameters protocols from {} to {}",
230+
arrayToString(original.getProtocols()),
231+
arrayToString(provided.getProtocols()));
232+
original.setProtocols(provided.getProtocols());
233+
}
234+
if (original.getWantClientAuth() != provided.getWantClientAuth()) {
235+
LOGGER.debug(
236+
"Setting SSLParameters wantClientAuth from {} to {}",
237+
original.getWantClientAuth(),
238+
provided.getWantClientAuth());
239+
original.setWantClientAuth(provided.getWantClientAuth());
240+
}
241+
if (original.getNeedClientAuth() != provided.getNeedClientAuth()) {
242+
LOGGER.debug(
243+
"Setting SSLParameters needClientAuth from {} to {}",
244+
original.getNeedClientAuth(),
245+
provided.getNeedClientAuth());
246+
original.setNeedClientAuth(provided.getNeedClientAuth());
247+
}
248+
if (notNullOrBlank(provided.getEndpointIdentificationAlgorithm())) {
249+
LOGGER.debug(
250+
"Setting SSLParameters endpointIdentificationAlgorithm from {} to {}",
251+
original.getEndpointIdentificationAlgorithm(),
252+
provided.getEndpointIdentificationAlgorithm());
253+
original.setEndpointIdentificationAlgorithm(provided.getEndpointIdentificationAlgorithm());
254+
}
255+
if (provided.getAlgorithmConstraints() != null) {
256+
LOGGER.debug(
257+
"Setting SSLParameters algorithmConstraints from {} to {}",
258+
original.getAlgorithmConstraints(),
259+
provided.getAlgorithmConstraints());
260+
original.setAlgorithmConstraints(provided.getAlgorithmConstraints());
261+
}
262+
if (provided.getServerNames() != null) {
263+
LOGGER.debug(
264+
"Setting SSLParameters serverNames from {} to {}",
265+
original.getServerNames(),
266+
provided.getServerNames());
267+
original.setServerNames(provided.getServerNames());
268+
}
269+
if (provided.getSNIMatchers() != null) {
270+
LOGGER.debug(
271+
"Setting SSLParameters SNIMatchers from {} to {}",
272+
original.getSNIMatchers(),
273+
provided.getSNIMatchers());
274+
original.setSNIMatchers(provided.getSNIMatchers());
275+
}
276+
if (original.getUseCipherSuitesOrder() != provided.getUseCipherSuitesOrder()) {
277+
LOGGER.debug(
278+
"Setting SSLParameters useCipherSuitesOrder from {} to {}",
279+
original.getUseCipherSuitesOrder(),
280+
provided.getUseCipherSuitesOrder());
281+
original.setUseCipherSuitesOrder(provided.getUseCipherSuitesOrder());
282+
}
283+
}
284+
285+
private static boolean notNullOrBlank(String str) {
286+
return str != null && !str.trim().isEmpty();
287+
}
288+
289+
private static String arrayToString(Object[] array) {
290+
if (emptyArray(array)) {
291+
return "";
292+
} else {
293+
return Arrays.stream(array)
294+
.map(o -> o == null ? "null" : o.toString())
295+
.collect(Collectors.joining());
296+
}
297+
}
298+
299+
private static boolean emptyArray(Object[] array) {
300+
return array == null || array.length == 0;
301+
}
302+
303+
private static boolean notEmptyArray(Object[] array) {
304+
return !emptyArray(array);
305+
}
216306
}

src/test/java/com/rabbitmq/stream/impl/StreamEnvironmentTest.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import static com.rabbitmq.stream.impl.TestUtils.latchAssert;
1717
import static com.rabbitmq.stream.impl.TestUtils.localhost;
18+
import static com.rabbitmq.stream.impl.TestUtils.localhostTls;
1819
import static com.rabbitmq.stream.impl.TestUtils.streamName;
1920
import static com.rabbitmq.stream.impl.TestUtils.waitAtMost;
2021
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -33,6 +34,7 @@
3334
import com.rabbitmq.stream.StreamException;
3435
import com.rabbitmq.stream.impl.Client.StreamMetadata;
3536
import com.rabbitmq.stream.impl.MonitoringTestUtils.EnvironmentInfo;
37+
import com.rabbitmq.stream.impl.TestUtils.DisabledIfTlsNotEnabled;
3638
import io.netty.channel.EventLoopGroup;
3739
import io.netty.channel.nio.NioEventLoopGroup;
3840
import java.net.ConnectException;
@@ -48,6 +50,8 @@
4850
import java.util.concurrent.CountDownLatch;
4951
import java.util.stream.Collectors;
5052
import java.util.stream.IntStream;
53+
import javax.net.ssl.SNIHostName;
54+
import javax.net.ssl.SSLParameters;
5155
import org.junit.jupiter.api.AfterAll;
5256
import org.junit.jupiter.api.BeforeAll;
5357
import org.junit.jupiter.api.BeforeEach;
@@ -78,7 +82,8 @@ static void afterAll() throws Exception {
7882
@BeforeEach
7983
void init() {
8084
environmentBuilder = Environment.builder();
81-
environmentBuilder.addressResolver(add -> localhost());
85+
environmentBuilder.addressResolver(
86+
add -> add.port() == Client.DEFAULT_PORT ? localhost() : localhostTls());
8287
environmentBuilder.eventLoopGroup(eventLoopGroup);
8388
}
8489

@@ -129,6 +134,21 @@ void environmentCreationShouldSucceedWithUrlContainingAllCorrectInformation() {
129134
environmentBuilder.uri("rabbitmq-stream://guest:guest@localhost:5552/%2f").build().close();
130135
}
131136

137+
@DisabledIfTlsNotEnabled
138+
@Test
139+
void environmentCreationShouldSucceedWhenUsingTls() {
140+
SSLParameters sslParameters = new SSLParameters();
141+
sslParameters.setServerNames(Collections.singletonList(new SNIHostName("localhost")));
142+
environmentBuilder
143+
.uri("rabbitmq-stream+tls://guest:guest@localhost:5551/%2f")
144+
.tls()
145+
.trustEverything()
146+
.sslParameters(sslParameters)
147+
.environmentBuilder()
148+
.build()
149+
.close();
150+
}
151+
132152
@Test
133153
void producersAndConsumersShouldBeClosedWhenEnvironmentIsClosed() {
134154
Environment environment = environmentBuilder.build();

src/test/java/com/rabbitmq/stream/impl/TestUtils.java

+4
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ static Address localhost() {
114114
return new Address("localhost", Client.DEFAULT_PORT);
115115
}
116116

117+
static Address localhostTls() {
118+
return new Address("localhost", Client.DEFAULT_TLS_PORT);
119+
}
120+
117121
static byte b(int value) {
118122
return (byte) value;
119123
}

src/test/java/com/rabbitmq/stream/impl/TlsTest.java

+9
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@
4343
import java.util.Collections;
4444
import java.util.concurrent.CountDownLatch;
4545
import java.util.stream.IntStream;
46+
import javax.net.ssl.SNIHostName;
4647
import javax.net.ssl.SSLException;
4748
import javax.net.ssl.SSLHandshakeException;
49+
import javax.net.ssl.SSLParameters;
4850
import org.junit.jupiter.api.Test;
4951
import org.junit.jupiter.api.extension.ExtendWith;
5052

@@ -172,6 +174,13 @@ void unverifiedConnection() {
172174
cf.get(new ClientParameters().sslContext(alwaysTrustSslContext()));
173175
}
174176

177+
@Test
178+
void unverifiedConnectionWithSslParameters() {
179+
SSLParameters sslParameters = new SSLParameters();
180+
sslParameters.setServerNames(Collections.singletonList(new SNIHostName("localhost")));
181+
cf.get(new ClientParameters().sslContext(alwaysTrustSslContext()).sslParameters(sslParameters));
182+
}
183+
175184
@Test
176185
void verifiedConnectionWithCorrectServerCertificate() throws Exception {
177186
SslContext context = SslContextBuilder.forClient().trustManager(caCertificate()).build();

0 commit comments

Comments
 (0)