Skip to content

Commit d84fee0

Browse files
committed
ARTEMIS-5778 Pass SSLSupport parameters to TrustManagerFactoryPlugin
1 parent d591048 commit d84fee0

File tree

6 files changed

+215
-7
lines changed

6 files changed

+215
-7
lines changed

artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/TrustManagerFactoryPlugin.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,33 @@
2020

2121
public interface TrustManagerFactoryPlugin {
2222

23+
/**
24+
* Parameters used as input for TrustManagerFactory.
25+
*/
26+
interface Parameters {
27+
String getTruststoreProvider();
28+
29+
String getTruststoreType();
30+
31+
String getTruststorePath();
32+
33+
String getTruststorePassword();
34+
35+
String getCrlPath();
36+
}
37+
2338
/**
2439
* {@return the TrustManagerFactory used when invoking {@link javax.net.ssl.TrustManagerFactory#getTrustManagers()}
2540
* to initialize the {@code SSLContext}}
2641
*/
2742
TrustManagerFactory getTrustManagerFactory();
43+
44+
/**
45+
* @param parameters the parameters to be used to initialize the {@code TrustManagerFactory}
46+
* {@return the TrustManagerFactory used when invoking {@link javax.net.ssl.TrustManagerFactory#getTrustManagers()}
47+
* to initialize the {@code SSLContext}}
48+
*/
49+
default TrustManagerFactory getTrustManagerFactory(TrustManagerFactoryPlugin.Parameters parameters) {
50+
return getTrustManagerFactory();
51+
}
2852
}

artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
* by default (see java.security.Security#getProviders()). The main thing to keep in mind is that PKCS#11 keystores
6666
* will either use null, and empty string, or NONE for their keystore path.
6767
*/
68-
public class SSLSupport {
68+
public class SSLSupport implements TrustManagerFactoryPlugin.Parameters {
6969

7070
public static final String NONE = "NONE";
7171
private String keystoreProvider = TransportConstants.DEFAULT_KEYSTORE_PROVIDER;
@@ -278,7 +278,7 @@ public static String parseArrayIntoCommandSeparatedList(String[] suites) {
278278

279279
private TrustManagerFactory loadTrustManagerFactory() throws Exception {
280280
if (trustManagerFactoryPlugin != null) {
281-
return SecurityManagerShim.doPrivileged((PrivilegedAction<TrustManagerFactory>) () -> ((TrustManagerFactoryPlugin) ClassloadingUtil.newInstanceFromClassLoader(SSLSupport.class, trustManagerFactoryPlugin, TrustManagerFactoryPlugin.class)).getTrustManagerFactory());
281+
return SecurityManagerShim.doPrivileged((PrivilegedAction<TrustManagerFactory>) () -> ((TrustManagerFactoryPlugin) ClassloadingUtil.newInstanceFromClassLoader(SSLSupport.class, trustManagerFactoryPlugin, TrustManagerFactoryPlugin.class)).getTrustManagerFactory(this));
282282
} else if (trustAll) {
283283
//This is useful for testing but not should be used outside of that purpose
284284
return InsecureTrustManagerFactory.INSTANCE;

docs/user-manual/configuring-transports.adoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,10 +443,14 @@ When used on a `connector` the `sniHost` value is used for the `server_name` ext
443443
trustManagerFactoryPlugin::
444444
This is valid on either an `acceptor` or `connector`.
445445
It defines the name of the class which implements `org.apache.activemq.artemis.api.core.TrustManagerFactoryPlugin`.
446-
This is a simple interface with a single method which returns a `javax.net.ssl.TrustManagerFactory`.
446+
This interface provides methods that return a `javax.net.ssl.TrustManagerFactory`.
447447
The `TrustManagerFactory` will be used when the underlying `javax.net.ssl.SSLContext` is initialized.
448448
This allows fine-grained customization of who/what the broker & client trusts.
449449
+
450+
The interface includes a default method `getTrustManagerFactory()` and an optional parametrized method `getTrustManagerFactory(Parameters parameters)`.
451+
If the plugin implements the parametrized version, it will receive the configured SSL trust parameters (`truststoreProvider`, `truststoreType`, `truststorePath`, `truststorePassword`, `crlPath`) which can be used to initialize the trust manager.
452+
If only the default method is implemented, the plugin will not receive these parameters.
453+
+
450454
This value takes precedence of all other SSL parameters which apply to the trust manager (i.e. `trustAll`, `truststoreProvider`, `truststorePath`, `truststorePassword`, `crlPath`).
451455
+
452456
Any plugin specified will need to be placed on the xref:using-server.adoc#adding-runtime-dependencies[broker's classpath].

tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/CoreClientOverOneWaySSLTest.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,31 @@ public void testOneWaySSLwithSNIOnlyOnTheBroker() throws Exception {
289289
}
290290

291291
@TestTemplate
292-
public void testOneWaySSLwithTrustManagerPlugin() throws Exception {
293-
createCustomSslServer(null, null, false, null, TestTrustManagerFactoryPlugin.class.getName());
292+
public void testOneWaySSLWithTrustManagerPlugin() throws Exception {
293+
try {
294+
testOneWaySSLWithTrustManagerPlugin(TestTrustManagerFactoryPlugin.class.getName());
295+
296+
assertTrue(TestTrustManagerFactoryPlugin.triggered.get());
297+
} finally {
298+
TestTrustManagerFactoryPlugin.triggered.set(false);
299+
}
300+
}
301+
302+
@TestTemplate
303+
public void testOneWaySSLWithParametrizedTrustManagerPlugin() throws Exception {
304+
try {
305+
testOneWaySSLWithTrustManagerPlugin(TestParametrizedTrustManagerFactoryPlugin.class.getName());
306+
307+
assertTrue(TestParametrizedTrustManagerFactoryPlugin.wasInvokedWithParameters.get());
308+
assertFalse(TestParametrizedTrustManagerFactoryPlugin.wasInvokedWithoutParameters.get());
309+
assertNotNull(TestParametrizedTrustManagerFactoryPlugin.receivedParameters.get());
310+
} finally {
311+
TestParametrizedTrustManagerFactoryPlugin.reset();
312+
}
313+
}
314+
315+
private void testOneWaySSLWithTrustManagerPlugin(String trustManagerFactoryPlugin) throws Exception {
316+
createCustomSslServer(null, null, false, null, trustManagerFactoryPlugin);
294317
String text = RandomUtil.randomUUIDString();
295318

296319
tc.getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME, true);
@@ -302,8 +325,6 @@ public void testOneWaySSLwithTrustManagerPlugin() throws Exception {
302325
ServerLocator locator = addServerLocator(ActiveMQClient.createServerLocatorWithoutHA(tc));
303326
ClientSessionFactory sf = addSessionFactory(createSessionFactory(locator));
304327

305-
assertTrue(TestTrustManagerFactoryPlugin.triggered.get());
306-
307328
ClientSession session = addClientSession(sf.createSession(false, true, true));
308329
session.createQueue(QueueConfiguration.of(CoreClientOverOneWaySSLTest.QUEUE).setDurable(false));
309330
ClientProducer producer = addClientProducer(session.createProducer(CoreClientOverOneWaySSLTest.QUEUE));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.activemq.artemis.tests.integration.ssl;
18+
19+
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
20+
import org.apache.activemq.artemis.api.core.TrustManagerFactoryPlugin;
21+
22+
import javax.net.ssl.TrustManagerFactory;
23+
import java.util.concurrent.atomic.AtomicBoolean;
24+
import java.util.concurrent.atomic.AtomicReference;
25+
26+
public class TestParametrizedTrustManagerFactoryPlugin implements TrustManagerFactoryPlugin {
27+
28+
public static AtomicBoolean wasInvokedWithParameters = new AtomicBoolean(false);
29+
public static AtomicBoolean wasInvokedWithoutParameters = new AtomicBoolean(false);
30+
public static AtomicReference<Parameters> receivedParameters = new AtomicReference<>(null);
31+
32+
public static void reset() {
33+
wasInvokedWithParameters.set(false);
34+
wasInvokedWithoutParameters.set(false);
35+
receivedParameters.set(null);
36+
}
37+
38+
@Override
39+
public TrustManagerFactory getTrustManagerFactory() {
40+
wasInvokedWithoutParameters.set(true);
41+
return InsecureTrustManagerFactory.INSTANCE;
42+
}
43+
44+
@Override
45+
public TrustManagerFactory getTrustManagerFactory(Parameters parameters) {
46+
wasInvokedWithParameters.set(true);
47+
receivedParameters.set(parameters);
48+
return InsecureTrustManagerFactory.INSTANCE;
49+
}
50+
}

tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/remoting/impl/ssl/SSLSupportTest.java

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,23 @@
1616
*/
1717
package org.apache.activemq.artemis.tests.unit.core.remoting.impl.ssl;
1818

19+
import static org.junit.jupiter.api.Assertions.assertEquals;
1920
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertNotNull;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
2023
import static org.junit.jupiter.api.Assertions.fail;
2124

2225
import java.io.File;
2326
import java.net.URL;
2427
import java.util.Arrays;
2528
import java.util.Collection;
29+
import java.util.concurrent.atomic.AtomicBoolean;
30+
import java.util.concurrent.atomic.AtomicReference;
2631

32+
import javax.net.ssl.SSLContext;
33+
import javax.net.ssl.TrustManagerFactory;
34+
35+
import org.apache.activemq.artemis.api.core.TrustManagerFactoryPlugin;
2736
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
2837
import org.apache.activemq.artemis.core.remoting.impl.ssl.SSLSupport;
2938
import org.apache.activemq.artemis.tests.extensions.parameterized.ParameterizedTestExtension;
@@ -287,4 +296,104 @@ public void testContextWithTrustAll() throws Exception {
287296
.setTrustAll(true)
288297
.createContext();
289298
}
299+
300+
@TestTemplate
301+
public void testTrustManagerFactoryPlugin() throws Exception {
302+
SSLContext context = new SSLSupport()
303+
.setKeystoreProvider(storeProvider)
304+
.setKeystoreType(storeType)
305+
.setKeystorePath(keyStorePath)
306+
.setKeystorePassword(keyStorePassword)
307+
.setTruststoreProvider(storeProvider)
308+
.setTruststoreType(storeType)
309+
.setTruststorePath(trustStorePath)
310+
.setTruststorePassword(trustStorePassword)
311+
.setTrustAll(true) // This should be ignored when plugin is set
312+
.setTrustManagerFactoryPlugin(TestTrustManagerFactoryPlugin.class.getName())
313+
.createContext();
314+
315+
assertNotNull(context);
316+
assertTrue(TestTrustManagerFactoryPlugin.wasInvoked.get());
317+
}
318+
319+
@TestTemplate
320+
public void testParametrizedTrustManagerFactoryPlugin() throws Exception {
321+
TestParametrizedTrustManagerFactoryPlugin.reset();
322+
323+
final String crlPath = "test-crl-path";
324+
325+
SSLContext context = new SSLSupport()
326+
.setKeystoreProvider(storeProvider)
327+
.setKeystoreType(storeType)
328+
.setKeystorePath(keyStorePath)
329+
.setKeystorePassword(keyStorePassword)
330+
.setTruststoreProvider(storeProvider)
331+
.setTruststoreType(storeType)
332+
.setTruststorePath(trustStorePath)
333+
.setTruststorePassword(trustStorePassword)
334+
.setCrlPath(crlPath)
335+
.setTrustAll(true) // This should be ignored when plugin is set
336+
.setTrustManagerFactoryPlugin(TestParametrizedTrustManagerFactoryPlugin.class.getName())
337+
.createContext();
338+
339+
assertNotNull(context);
340+
assertFalse(TestParametrizedTrustManagerFactoryPlugin.wasInvokedWithoutParameters.get());
341+
assertTrue(TestParametrizedTrustManagerFactoryPlugin.wasInvokedWithParameters.get());
342+
343+
TrustManagerFactoryPlugin.Parameters receivedParameters =
344+
TestParametrizedTrustManagerFactoryPlugin.receivedParameters.get();
345+
assertNotNull(receivedParameters);
346+
347+
assertEquals(storeProvider, receivedParameters.getTruststoreProvider());
348+
assertEquals(storeType, receivedParameters.getTruststoreType());
349+
assertEquals(trustStorePath, receivedParameters.getTruststorePath());
350+
assertEquals(trustStorePassword, receivedParameters.getTruststorePassword());
351+
assertEquals(crlPath, receivedParameters.getCrlPath());
352+
}
353+
354+
/**
355+
* Test implementation of TrustManagerFactoryPlugin without parameters that tracks invocation
356+
*/
357+
public static class TestTrustManagerFactoryPlugin implements TrustManagerFactoryPlugin {
358+
public static AtomicBoolean wasInvoked = new AtomicBoolean(false);
359+
360+
public static void reset() {
361+
wasInvoked.set(false);
362+
}
363+
364+
@Override
365+
public TrustManagerFactory getTrustManagerFactory() {
366+
wasInvoked.set(true);
367+
return null;
368+
}
369+
}
370+
371+
/**
372+
* Test implementation of TrustManagerFactoryPlugin with parameters that tracks invocation
373+
*/
374+
public static class TestParametrizedTrustManagerFactoryPlugin implements TrustManagerFactoryPlugin {
375+
376+
public static AtomicBoolean wasInvokedWithParameters = new AtomicBoolean(false);
377+
public static AtomicBoolean wasInvokedWithoutParameters = new AtomicBoolean(false);
378+
public static AtomicReference<Parameters> receivedParameters = new AtomicReference<>(null);
379+
380+
public static void reset() {
381+
wasInvokedWithParameters.set(false);
382+
wasInvokedWithoutParameters.set(false);
383+
receivedParameters.set(null);
384+
}
385+
386+
@Override
387+
public TrustManagerFactory getTrustManagerFactory() {
388+
wasInvokedWithoutParameters.set(true);
389+
return null;
390+
}
391+
392+
@Override
393+
public TrustManagerFactory getTrustManagerFactory(Parameters parameters) {
394+
wasInvokedWithParameters.set(true);
395+
receivedParameters.set(parameters);
396+
return null;
397+
}
398+
}
290399
}

0 commit comments

Comments
 (0)