diff --git a/activemq-amqp/src/main/java/org/apache/activemq/transport/amqp/AmqpNioSslTransportFactory.java b/activemq-amqp/src/main/java/org/apache/activemq/transport/amqp/AmqpNioSslTransportFactory.java index 7858a561acd..d8b2df6b80a 100644 --- a/activemq-amqp/src/main/java/org/apache/activemq/transport/amqp/AmqpNioSslTransportFactory.java +++ b/activemq-amqp/src/main/java/org/apache/activemq/transport/amqp/AmqpNioSslTransportFactory.java @@ -72,14 +72,20 @@ public TcpTransport createTransport(WireFormat wireFormat, Socket socket, } @Override - public TransportServer doBind(URI location) throws IOException { - if (SslContext.getCurrentSslContext() != null) { + public TransportServer doBind(URI location, SslContext sslContext) throws IOException { + if (sslContext != null) { try { - context = SslContext.getCurrentSslContext().getSSLContext(); + context = sslContext.getSSLContext(); } catch (Exception e) { throw new IOException(e); } } return super.doBind(location); } + + @Override + @SuppressWarnings("deprecation") + public TransportServer doBind(URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } } diff --git a/activemq-broker/src/main/java/org/apache/activemq/broker/SslBrokerService.java b/activemq-broker/src/main/java/org/apache/activemq/broker/SslBrokerService.java index 8cc944d9d3f..0b050622ab0 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/broker/SslBrokerService.java +++ b/activemq-broker/src/main/java/org/apache/activemq/broker/SslBrokerService.java @@ -90,14 +90,8 @@ protected TransportServer createSslTransportServer(URI brokerURI, KeyManager[] k // If given an SSL URI, use an SSL TransportFactory and configure // it to use the given key and trust managers. SslTransportFactory transportFactory = new SslTransportFactory(); - - SslContext ctx = new SslContext(km, tm, random); - SslContext.setCurrentSslContext(ctx); - try { - return transportFactory.doBind(brokerURI); - } finally { - SslContext.setCurrentSslContext(null); - } + SslContext ctx = new DefaultSslContext(km, tm, random); + return transportFactory.doBind(brokerURI, ctx); } else { // Else, business as usual. diff --git a/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnector.java b/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnector.java index ed0439075cc..ec5eee67146 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnector.java +++ b/activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnector.java @@ -81,6 +81,7 @@ public class TransportConnector implements Connector, BrokerServiceAware { private boolean warnOnRemoteClose = false; private boolean displayStackTrace = false; private boolean autoStart = true; + private SslContext sslContext; LinkedList peerBrokers = new LinkedList(); private AtomicBoolean started = new AtomicBoolean(false); @@ -350,7 +351,8 @@ protected TransportServer createTransportServer() throws IOException, URISyntaxE throw new IllegalArgumentException( "You must specify the brokerService property. Maybe this connector should be added to a broker?"); } - return TransportFactorySupport.bind(brokerService, uri); + SslContext ctx = sslContext != null ? sslContext : brokerService.getSslContext(); + return TransportFactorySupport.bind(brokerService, uri, ctx); } public DiscoveryAgent getDiscoveryAgent() throws IOException { @@ -717,6 +719,14 @@ public boolean isAutoStart() { return autoStart; } + public SslContext getSslContext() { + return sslContext; + } + + public void setSslContext(SslContext sslContext) { + this.sslContext = sslContext; + } + @Override public int getConnectionCount() { return connections.size(); diff --git a/activemq-broker/src/main/java/org/apache/activemq/network/DiscoveryNetworkConnector.java b/activemq-broker/src/main/java/org/apache/activemq/network/DiscoveryNetworkConnector.java index 3b9696cf925..bd5e681e96b 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/network/DiscoveryNetworkConnector.java +++ b/activemq-broker/src/main/java/org/apache/activemq/network/DiscoveryNetworkConnector.java @@ -119,43 +119,35 @@ public void onServiceAdd(DiscoveryEvent event) { LOG.info("Establishing network connection from {} to {}", localURI, connectUri); + SslContext sslContext = getSslContext() != null ? getSslContext() : getBrokerService().getSslContext(); + Transport remoteTransport; Transport localTransport; try { - // Allows the transport to access the broker's ssl configuration. - if (getSslContext() != null) { - SslContext.setCurrentSslContext(getSslContext()); - } else { - SslContext.setCurrentSslContext(getBrokerService().getSslContext()); - } + remoteTransport = TransportFactory.connect(connectUri, sslContext); + } catch (Exception e) { + LOG.warn("Could not connect to remote URI: {}: {}", connectUri, e.getMessage()); + LOG.debug("Connection failure exception: ", e); try { - remoteTransport = TransportFactory.connect(connectUri); - } catch (Exception e) { - LOG.warn("Could not connect to remote URI: {}: {}", connectUri, e.getMessage()); - LOG.debug("Connection failure exception: ", e); - try { - discoveryAgent.serviceFailed(event); - } catch (IOException e1) { - LOG.debug("Failure while handling create remote transport failure event: {}", e1.getMessage(), e1); - } - return; + discoveryAgent.serviceFailed(event); + } catch (IOException e1) { + LOG.debug("Failure while handling create remote transport failure event: {}", e1.getMessage(), e1); } - try { - localTransport = createLocalTransport(); - } catch (Exception e) { - ServiceSupport.dispose(remoteTransport); - LOG.warn("Could not connect to local URI: {}: {}", localURI, e.getMessage()); - LOG.debug("Connection failure exception: ", e); + return; + } + try { + localTransport = createLocalTransport(); + } catch (Exception e) { + ServiceSupport.dispose(remoteTransport); + LOG.warn("Could not connect to local URI: {}: {}", localURI, e.getMessage()); + LOG.debug("Connection failure exception: ", e); - try { - discoveryAgent.serviceFailed(event); - } catch (IOException e1) { - LOG.debug("Failure while handling create local transport failure event: {}", e1.getMessage(), e1); - } - return; + try { + discoveryAgent.serviceFailed(event); + } catch (IOException e1) { + LOG.debug("Failure while handling create local transport failure event: {}", e1.getMessage(), e1); } - } finally { - SslContext.setCurrentSslContext(null); + return; } NetworkBridge bridge = createBridge(localTransport, remoteTransport, event); try { diff --git a/activemq-broker/src/main/java/org/apache/activemq/transport/TransportFactorySupport.java b/activemq-broker/src/main/java/org/apache/activemq/transport/TransportFactorySupport.java index 63ca26c4a22..e0ba4a78593 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/transport/TransportFactorySupport.java +++ b/activemq-broker/src/main/java/org/apache/activemq/transport/TransportFactorySupport.java @@ -29,18 +29,15 @@ public class TransportFactorySupport { public static TransportServer bind(BrokerService brokerService, URI location) throws IOException { + return bind(brokerService, location, brokerService != null ? brokerService.getSslContext() : null); + } + + public static TransportServer bind(BrokerService brokerService, URI location, SslContext sslContext) throws IOException { TransportFactory tf = TransportFactory.findTransportFactory(location); - if( brokerService!=null && tf instanceof BrokerServiceAware) { - ((BrokerServiceAware)tf).setBrokerService(brokerService); - } - try { - if( brokerService!=null ) { - SslContext.setCurrentSslContext(brokerService.getSslContext()); - } - return tf.doBind(location); - } finally { - SslContext.setCurrentSslContext(null); + if (brokerService != null && tf instanceof BrokerServiceAware) { + ((BrokerServiceAware) tf).setBrokerService(brokerService); } + return tf.doBind(location, sslContext); } } diff --git a/activemq-broker/src/main/java/org/apache/activemq/transport/auto/AutoSslTransportFactory.java b/activemq-broker/src/main/java/org/apache/activemq/transport/auto/AutoSslTransportFactory.java index d1ad524c9ba..bcf9fa8fb8c 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/transport/auto/AutoSslTransportFactory.java +++ b/activemq-broker/src/main/java/org/apache/activemq/transport/auto/AutoSslTransportFactory.java @@ -29,6 +29,7 @@ import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.BrokerServiceAware; +import org.apache.activemq.broker.SslContext; import org.apache.activemq.transport.TransportServer; import org.apache.activemq.transport.tcp.SslTransportFactory; import org.apache.activemq.transport.tcp.TcpTransport; @@ -54,19 +55,31 @@ public void setBrokerService(BrokerService brokerService) { private Set enabledProtocols; - /** - * Overriding to use SslTransportServer and allow for proper reflection. - */ @Override + @SuppressWarnings("deprecation") public TransportServer doBind(final URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } + + @Override + public TransportServer doBind(final URI location, SslContext sslContext) throws IOException { try { Map options = new HashMap(URISupport.parseParameters(location)); Map autoProperties = IntrospectionSupport.extractProperties(options, "auto."); this.enabledProtocols = AutoTransportUtils.parseProtocols((String) autoProperties.get("protocols")); - ServerSocketFactory serverSocketFactory = createServerSocketFactory(); - AutoSslTransportServer server = createAutoSslTransportServer(location, (SSLServerSocketFactory)serverSocketFactory); + SSLServerSocketFactory serverSocketFactory; + if (sslContext != null) { + try { + serverSocketFactory = sslContext.getSSLContext().getServerSocketFactory(); + } catch (Exception e) { + throw IOExceptionSupport.create(e); + } + } else { + serverSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + } + AutoSslTransportServer server = createAutoSslTransportServer(location, serverSocketFactory); if (options.get("allowLinkStealing") != null){ allowLinkStealingSet = true; } diff --git a/activemq-broker/src/main/java/org/apache/activemq/transport/auto/nio/AutoNioSslTransportFactory.java b/activemq-broker/src/main/java/org/apache/activemq/transport/auto/nio/AutoNioSslTransportFactory.java index 8a29ab246dc..51e08474c7a 100644 --- a/activemq-broker/src/main/java/org/apache/activemq/transport/auto/nio/AutoNioSslTransportFactory.java +++ b/activemq-broker/src/main/java/org/apache/activemq/transport/auto/nio/AutoNioSslTransportFactory.java @@ -94,11 +94,17 @@ protected Transport createTransport(Socket socket, WireFormat format, SSLEngine private Set enabledProtocols; @Override + @SuppressWarnings("deprecation") public TransportServer doBind(final URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } + + @Override + public TransportServer doBind(final URI location, SslContext sslContext) throws IOException { try { - if (SslContext.getCurrentSslContext() != null) { + if (sslContext != null) { try { - context = SslContext.getCurrentSslContext().getSSLContext(); + context = sslContext.getSSLContext(); } catch (Exception e) { throw new IOException(e); } diff --git a/activemq-client/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java b/activemq-client/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java index 7a6417bd89f..31abfa31f04 100644 --- a/activemq-client/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java +++ b/activemq-client/src/main/java/org/apache/activemq/ActiveMQSslConnectionFactory.java @@ -36,7 +36,9 @@ import javax.net.ssl.TrustManagerFactory; import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.DefaultSslContext; import org.apache.activemq.transport.Transport; +import org.apache.activemq.transport.TransportFactory; import org.apache.activemq.util.JMSExceptionSupport; /** @@ -108,20 +110,31 @@ public void setKeyAndTrustManagers(final KeyManager[] km, final TrustManager[] t */ @Override protected Transport createTransport() throws JMSException { - SslContext existing = SslContext.getCurrentSslContext(); try { if (keyStore != null || trustStore != null) { keyManager = createKeyManager(); trustManager = createTrustManager(); } if (keyManager != null || trustManager != null) { - SslContext.setCurrentSslContext(new SslContext(keyManager, trustManager, secureRandom)); + SslContext sslContext = new DefaultSslContext(keyManager, trustManager, secureRandom); + URI connectBrokerUL = brokerURL; + String scheme = brokerURL.getScheme(); + if (scheme != null) { + if (scheme.equals("auto")) { + connectBrokerUL = new URI(brokerURL.toString().replace("auto", "tcp")); + } else if (scheme.equals("auto+ssl")) { + connectBrokerUL = new URI(brokerURL.toString().replace("auto+ssl", "ssl")); + } else if (scheme.equals("auto+nio")) { + connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio", "nio")); + } else if (scheme.equals("auto+nio+ssl")) { + connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio+ssl", "nio+ssl")); + } + } + return TransportFactory.connect(connectBrokerUL, sslContext); } return super.createTransport(); } catch (Exception e) { throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e); - } finally { - SslContext.setCurrentSslContext(existing); } } diff --git a/activemq-client/src/main/java/org/apache/activemq/broker/CompatibleSslContext.java b/activemq-client/src/main/java/org/apache/activemq/broker/CompatibleSslContext.java new file mode 100644 index 00000000000..77babb8cafd --- /dev/null +++ b/activemq-client/src/main/java/org/apache/activemq/broker/CompatibleSslContext.java @@ -0,0 +1,155 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.broker; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * An {@link SslContext} that is API-compatible with {@link ThreadLocalSslContext} + * but carries no ThreadLocal state. + * + *

This class exposes the same protected fields, list-based getters/setters, + * and mutator methods that {@link ThreadLocalSslContext} does, so subclasses + * (such as {@code SpringSslContext}) can be reparented onto this class without + * any source or binary breakage. + * + *

The SSLContext is lazily created on first call to {@link #getSSLContext()} + * and can be replaced directly via {@link #setSSLContext(SSLContext)}. + */ +public class CompatibleSslContext extends SslContext { + + protected String protocol = "TLS"; + protected String provider = null; + protected List keyManagers = new ArrayList<>(); + protected List trustManagers = new ArrayList<>(); + protected SecureRandom secureRandom; + + private volatile boolean initialized; + private SSLContext sslContext; + + public CompatibleSslContext() { + } + + public CompatibleSslContext(KeyManager[] km, TrustManager[] tm, SecureRandom random) { + if (km != null) { + setKeyManagers(Arrays.asList(km)); + } + if (tm != null) { + setTrustManagers(Arrays.asList(tm)); + } + setSecureRandom(random); + } + + public KeyManager[] getKeyManagersAsArray() { + KeyManager[] rc = new KeyManager[keyManagers.size()]; + return keyManagers.toArray(rc); + } + + public TrustManager[] getTrustManagersAsArray() { + TrustManager[] rc = new TrustManager[trustManagers.size()]; + return trustManagers.toArray(rc); + } + + public void addKeyManager(KeyManager km) { + keyManagers.add(km); + } + + public boolean removeKeyManager(KeyManager km) { + return keyManagers.remove(km); + } + + public void addTrustManager(TrustManager tm) { + trustManagers.add(tm); + } + + public boolean removeTrustManager(TrustManager tm) { + return trustManagers.remove(tm); + } + + public List getKeyManagers() { + return keyManagers; + } + + public void setKeyManagers(List keyManagers) { + this.keyManagers = keyManagers; + } + + public List getTrustManagers() { + return trustManagers; + } + + public void setTrustManagers(List trustManagers) { + this.trustManagers = trustManagers; + } + + public SecureRandom getSecureRandom() { + return secureRandom; + } + + public void setSecureRandom(SecureRandom secureRandom) { + this.secureRandom = secureRandom; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + @Override + public SSLContext getSSLContext() throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException { + if (!initialized) { + synchronized (this) { + if (!initialized) { + if (provider == null) { + sslContext = SSLContext.getInstance(protocol); + } else { + sslContext = SSLContext.getInstance(protocol, provider); + } + sslContext.init(getKeyManagersAsArray(), getTrustManagersAsArray(), getSecureRandom()); + initialized = true; + } + } + } + return sslContext; + } + + public synchronized void setSSLContext(SSLContext sslContext) { + this.sslContext = sslContext; + initialized = true; + } +} diff --git a/activemq-client/src/main/java/org/apache/activemq/broker/DefaultSslContext.java b/activemq-client/src/main/java/org/apache/activemq/broker/DefaultSslContext.java new file mode 100644 index 00000000000..3c67bb83843 --- /dev/null +++ b/activemq-client/src/main/java/org/apache/activemq/broker/DefaultSslContext.java @@ -0,0 +1,116 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.broker; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * A simple {@link SslContext} that holds key/trust managers directly and + * creates a single {@link SSLContext} on first use. + * + *

This is the recommended replacement for {@link ThreadLocalSslContext} + * when explicit parameter passing is used throughout the transport chain. + * It carries no ThreadLocal state and does not support runtime certificate + * reload. + */ +public class DefaultSslContext extends SslContext { + + private String protocol = "TLS"; + private String provider; + private KeyManager[] keyManagers; + private TrustManager[] trustManagers; + private SecureRandom secureRandom; + + private volatile SSLContext sslContext; + + public DefaultSslContext() { + } + + public DefaultSslContext(KeyManager[] km, TrustManager[] tm, SecureRandom random) { + this.keyManagers = km; + this.trustManagers = tm; + this.secureRandom = random; + } + + @Override + public SSLContext getSSLContext() throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException { + if (sslContext == null) { + synchronized (this) { + if (sslContext == null) { + SSLContext ctx; + if (provider == null) { + ctx = SSLContext.getInstance(protocol); + } else { + ctx = SSLContext.getInstance(protocol, provider); + } + ctx.init(keyManagers, trustManagers, secureRandom); + sslContext = ctx; + } + } + } + return sslContext; + } + + // --- Bean properties --- + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public KeyManager[] getKeyManagers() { + return keyManagers; + } + + public void setKeyManagers(KeyManager[] keyManagers) { + this.keyManagers = keyManagers; + } + + public TrustManager[] getTrustManagers() { + return trustManagers; + } + + public void setTrustManagers(TrustManager[] trustManagers) { + this.trustManagers = trustManagers; + } + + public SecureRandom getSecureRandom() { + return secureRandom; + } + + public void setSecureRandom(SecureRandom secureRandom) { + this.secureRandom = secureRandom; + } +} diff --git a/activemq-client/src/main/java/org/apache/activemq/broker/SslContext.java b/activemq-client/src/main/java/org/apache/activemq/broker/SslContext.java index 61e534a5e9e..bb34153995e 100644 --- a/activemq-client/src/main/java/org/apache/activemq/broker/SslContext.java +++ b/activemq-client/src/main/java/org/apache/activemq/broker/SslContext.java @@ -19,124 +19,46 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; /** - * A holder of SSL configuration. + * Provides an {@link SSLContext} for SSL/TLS transport connectors and + * network connectors. + * + *

Implementations may be static (see {@link ThreadLocalSslContext}) or + * support runtime certificate reload. */ -public class SslContext { - - protected String protocol = "TLS"; - protected String provider = null; - protected List keyManagers = new ArrayList(); - protected List trustManagers = new ArrayList(); - protected SecureRandom secureRandom; - private volatile boolean initialized; - private SSLContext sslContext; - - private static final ThreadLocal current = new ThreadLocal(); - - public SslContext() { - } - - public SslContext(KeyManager[] km, TrustManager[] tm, SecureRandom random) { - if( km!=null ) { - setKeyManagers(Arrays.asList(km)); - } - if( tm!=null ) { - setTrustManagers(Arrays.asList(tm)); - } - setSecureRandom(random); - } - - static public void setCurrentSslContext(SslContext bs) { - current.set(bs); - } - static public SslContext getCurrentSslContext() { - return current.get(); - } - - public KeyManager[] getKeyManagersAsArray() { - KeyManager rc[] = new KeyManager[keyManagers.size()]; - return keyManagers.toArray(rc); - } - public TrustManager[] getTrustManagersAsArray() { - TrustManager rc[] = new TrustManager[trustManagers.size()]; - return trustManagers.toArray(rc); - } - - public void addKeyManager(KeyManager km) { - keyManagers.add(km); - } - public boolean removeKeyManager(KeyManager km) { - return keyManagers.remove(km); - } - public void addTrustManager(TrustManager tm) { - trustManagers.add(tm); - } - public boolean removeTrustManager(TrustManager tm) { - return trustManagers.remove(tm); - } - - public List getKeyManagers() { - return keyManagers; - } - public void setKeyManagers(List keyManagers) { - this.keyManagers = keyManagers; - } - public List getTrustManagers() { - return trustManagers; - } - public void setTrustManagers(List trustManagers) { - this.trustManagers = trustManagers; - } - public SecureRandom getSecureRandom() { - return secureRandom; - } - public void setSecureRandom(SecureRandom secureRandom) { - this.secureRandom = secureRandom; - } - - public String getProtocol() { - return protocol; - } - public void setProtocol(String protocol) { - this.protocol = protocol; - } - public String getProvider() { - return provider; - } - public void setProvider(String provider) { - this.provider = provider; +public abstract class SslContext { + + /** + * Returns a fully initialised {@link SSLContext} ready for use by + * transport factories. + */ + public abstract SSLContext getSSLContext() throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException; + + /** + * Reload certificates from the underlying key/trust material. + * The default implementation is a no-op; reloadable implementations + * override this to swap in new credentials without restarting the + * broker. + */ + public void reload() throws Exception { } - public SSLContext getSSLContext() throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException { - if (!initialized) { - synchronized (this) { - if (!initialized) { - if (provider == null) { - sslContext = SSLContext.getInstance(protocol); - } else { - sslContext = SSLContext.getInstance(protocol, provider); - } - sslContext.init(getKeyManagersAsArray(), getTrustManagersAsArray(), getSecureRandom()); - initialized = true; - } - } - } - return sslContext; + /** + * @deprecated Use explicit parameter passing instead of ThreadLocal propagation. + */ + @Deprecated + public static void setCurrentSslContext(SslContext ctx) { + ThreadLocalSslContext.setCurrent(ctx); } - public synchronized void setSSLContext(SSLContext sslContext) { - this.sslContext = sslContext; - initialized = true; + + /** + * @deprecated Use explicit parameter passing instead of ThreadLocal propagation. + */ + @Deprecated + public static SslContext getCurrentSslContext() { + return ThreadLocalSslContext.getCurrent(); } - - } diff --git a/activemq-client/src/main/java/org/apache/activemq/broker/ThreadLocalSslContext.java b/activemq-client/src/main/java/org/apache/activemq/broker/ThreadLocalSslContext.java new file mode 100644 index 00000000000..39130d6ba64 --- /dev/null +++ b/activemq-client/src/main/java/org/apache/activemq/broker/ThreadLocalSslContext.java @@ -0,0 +1,161 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.broker; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * The original {@link SslContext} implementation that uses a {@link ThreadLocal} + * to propagate SSL configuration through the transport factory call chain. + * + *

This class preserves full backward compatibility with the pre-interface + * {@code SslContext} class. New code should prefer explicit parameter passing + * and, where runtime certificate reload is needed, use a + * {@code ReloadableSslContext} instead. + */ +public class ThreadLocalSslContext extends SslContext { + + private static final ThreadLocal current = new ThreadLocal<>(); + + protected String protocol = "TLS"; + protected String provider = null; + protected List keyManagers = new ArrayList<>(); + protected List trustManagers = new ArrayList<>(); + protected SecureRandom secureRandom; + private volatile boolean initialized; + private SSLContext sslContext; + + public ThreadLocalSslContext() { + } + + public ThreadLocalSslContext(KeyManager[] km, TrustManager[] tm, SecureRandom random) { + if (km != null) { + setKeyManagers(Arrays.asList(km)); + } + if (tm != null) { + setTrustManagers(Arrays.asList(tm)); + } + setSecureRandom(random); + } + + static void setCurrent(SslContext ctx) { + current.set(ctx); + } + + static SslContext getCurrent() { + return current.get(); + } + + public KeyManager[] getKeyManagersAsArray() { + KeyManager[] rc = new KeyManager[keyManagers.size()]; + return keyManagers.toArray(rc); + } + + public TrustManager[] getTrustManagersAsArray() { + TrustManager[] rc = new TrustManager[trustManagers.size()]; + return trustManagers.toArray(rc); + } + + public void addKeyManager(KeyManager km) { + keyManagers.add(km); + } + + public boolean removeKeyManager(KeyManager km) { + return keyManagers.remove(km); + } + + public void addTrustManager(TrustManager tm) { + trustManagers.add(tm); + } + + public boolean removeTrustManager(TrustManager tm) { + return trustManagers.remove(tm); + } + + public List getKeyManagers() { + return keyManagers; + } + + public void setKeyManagers(List keyManagers) { + this.keyManagers = keyManagers; + } + + public List getTrustManagers() { + return trustManagers; + } + + public void setTrustManagers(List trustManagers) { + this.trustManagers = trustManagers; + } + + public SecureRandom getSecureRandom() { + return secureRandom; + } + + public void setSecureRandom(SecureRandom secureRandom) { + this.secureRandom = secureRandom; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + @Override + public SSLContext getSSLContext() throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException { + if (!initialized) { + synchronized (this) { + if (!initialized) { + if (provider == null) { + sslContext = SSLContext.getInstance(protocol); + } else { + sslContext = SSLContext.getInstance(protocol, provider); + } + sslContext.init(getKeyManagersAsArray(), getTrustManagersAsArray(), getSecureRandom()); + initialized = true; + } + } + } + return sslContext; + } + + public synchronized void setSSLContext(SSLContext sslContext) { + this.sslContext = sslContext; + initialized = true; + } +} diff --git a/activemq-client/src/main/java/org/apache/activemq/transport/TransportFactory.java b/activemq-client/src/main/java/org/apache/activemq/transport/TransportFactory.java index 184a5d63911..cfb4e8c85aa 100644 --- a/activemq-client/src/main/java/org/apache/activemq/transport/TransportFactory.java +++ b/activemq-client/src/main/java/org/apache/activemq/transport/TransportFactory.java @@ -27,6 +27,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; +import org.apache.activemq.broker.SslContext; import org.apache.activemq.util.FactoryFinder; import org.apache.activemq.util.IOExceptionSupport; import org.apache.activemq.util.IntrospectionSupport; @@ -49,6 +50,39 @@ public abstract class TransportFactory { public abstract TransportServer doBind(URI location) throws IOException; + @SuppressWarnings("deprecation") + public TransportServer doBind(URI location, SslContext sslContext) throws IOException { + SslContext prev = SslContext.getCurrentSslContext(); + try { + SslContext.setCurrentSslContext(sslContext); + return doBind(location); + } finally { + SslContext.setCurrentSslContext(prev); + } + } + + @SuppressWarnings("deprecation") + public Transport doConnect(URI location, SslContext sslContext) throws Exception { + SslContext prev = SslContext.getCurrentSslContext(); + try { + SslContext.setCurrentSslContext(sslContext); + return doConnect(location); + } finally { + SslContext.setCurrentSslContext(prev); + } + } + + @SuppressWarnings("deprecation") + public Transport doCompositeConnect(URI location, SslContext sslContext) throws Exception { + SslContext prev = SslContext.getCurrentSslContext(); + try { + SslContext.setCurrentSslContext(sslContext); + return doCompositeConnect(location); + } finally { + SslContext.setCurrentSslContext(prev); + } + } + public Transport doConnect(URI location, Executor ex) throws Exception { return doConnect(location); } @@ -69,6 +103,11 @@ public static Transport connect(URI location) throws Exception { return tf.doConnect(location); } + public static Transport connect(URI location, SslContext sslContext) throws Exception { + TransportFactory tf = findTransportFactory(location); + return tf.doConnect(location, sslContext); + } + /** * Creates a normal transport. * @@ -95,6 +134,11 @@ public static Transport compositeConnect(URI location) throws Exception { return tf.doCompositeConnect(location); } + public static Transport compositeConnect(URI location, SslContext sslContext) throws Exception { + TransportFactory tf = findTransportFactory(location); + return tf.doCompositeConnect(location, sslContext); + } + /** * Creates a slimmed down transport that is more efficient so that it can be * used by composite transports like reliable and HA. diff --git a/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransport.java b/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransport.java index 7ab3b188187..dc859159670 100644 --- a/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransport.java +++ b/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransport.java @@ -132,8 +132,13 @@ public class FailoverTransport implements CompositeTransport { private String nestedExtraQueryOptions; private volatile boolean shuttingDown = false; + @SuppressWarnings("deprecation") public FailoverTransport() { - brokerSslContext = SslContext.getCurrentSslContext(); + this(SslContext.getCurrentSslContext()); + } + + public FailoverTransport(SslContext sslContext) { + brokerSslContext = sslContext; stateTracker.setTrackTransactions(true); // Setup a task that is used to reconnect the a connection async. reconnectTaskFactory = new TaskRunnerFactory(); @@ -1011,13 +1016,11 @@ final boolean doReconnect() { while ((transport != null || iter.hasNext()) && (connectedTransport.get() == null && !disposed)) { try { - SslContext.setCurrentSslContext(brokerSslContext); - // We could be starting with a backup and if so we wait to grab a // URI from the pool until next time around. if (transport == null) { uri = addExtraQueryOptions(iter.next()); - transport = TransportFactory.compositeConnect(uri); + transport = TransportFactory.compositeConnect(uri, brokerSslContext); } LOG.debug("Attempting {}th connect to: {}", connectFailures, uri); @@ -1076,7 +1079,6 @@ final boolean doReconnect() { } } } finally { - SslContext.setCurrentSslContext(null); } } } @@ -1194,11 +1196,10 @@ final boolean buildBackups() { URI uri = addExtraQueryOptions(iter.next()); if (connectedTransportURI != null && !connectedTransportURI.equals(uri)) { try { - SslContext.setCurrentSslContext(brokerSslContext); BackupTransport bt = new BackupTransport(this); bt.setUri(uri); if (!backups.contains(bt)) { - Transport t = TransportFactory.compositeConnect(uri); + Transport t = TransportFactory.compositeConnect(uri, brokerSslContext); t.setTransportListener(bt); t.start(); bt.setTransport(t); @@ -1222,8 +1223,6 @@ final boolean buildBackups() { } } catch (Exception e) { LOG.debug("Failed to build backup ", e); - } finally { - SslContext.setCurrentSslContext(null); } } } diff --git a/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransportFactory.java b/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransportFactory.java index 14b5b16e70b..8a9af81e5db 100644 --- a/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransportFactory.java +++ b/activemq-client/src/main/java/org/apache/activemq/transport/failover/FailoverTransportFactory.java @@ -20,6 +20,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Map; + +import org.apache.activemq.broker.SslContext; import org.apache.activemq.transport.MutexTransport; import org.apache.activemq.transport.ResponseCorrelator; import org.apache.activemq.transport.Transport; @@ -43,6 +45,18 @@ public Transport doConnect(URI location) throws IOException { } } + @Override + public Transport doConnect(URI location, SslContext sslContext) throws Exception { + try { + Transport transport = createTransport(URISupport.parseComposite(location), sslContext); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } catch (URISyntaxException e) { + throw new IOException("Invalid location: " + location); + } + } + @Override public Transport doCompositeConnect(URI location) throws IOException { try { @@ -52,14 +66,22 @@ public Transport doCompositeConnect(URI location) throws IOException { } } - /** - * @param compositData - * @return - * @throws IOException - */ + @Override + public Transport doCompositeConnect(URI location, SslContext sslContext) throws Exception { + try { + return createTransport(URISupport.parseComposite(location), sslContext); + } catch (URISyntaxException e) { + throw new IOException("Invalid location: " + location); + } + } + public Transport createTransport(CompositeData compositData) throws IOException { + return createTransport(compositData, null); + } + + public Transport createTransport(CompositeData compositData, SslContext sslContext) throws IOException { Map options = compositData.getParameters(); - FailoverTransport transport = createTransport(options); + FailoverTransport transport = createTransport(options, sslContext); if (!options.isEmpty()) { throw new IllegalArgumentException("Invalid connect parameters: " + options); } @@ -68,7 +90,11 @@ public Transport createTransport(CompositeData compositData) throws IOException } public FailoverTransport createTransport(Map parameters) throws IOException { - FailoverTransport transport = new FailoverTransport(); + return createTransport(parameters, null); + } + + public FailoverTransport createTransport(Map parameters, SslContext sslContext) throws IOException { + FailoverTransport transport = new FailoverTransport(sslContext); Map nestedExtraQueryOptions = IntrospectionSupport.extractProperties(parameters, "nested."); IntrospectionSupport.setProperties(transport, parameters); try { diff --git a/activemq-client/src/main/java/org/apache/activemq/transport/nio/NIOSSLTransportFactory.java b/activemq-client/src/main/java/org/apache/activemq/transport/nio/NIOSSLTransportFactory.java index 405314f617e..9913c856a5d 100644 --- a/activemq-client/src/main/java/org/apache/activemq/transport/nio/NIOSSLTransportFactory.java +++ b/activemq-client/src/main/java/org/apache/activemq/transport/nio/NIOSSLTransportFactory.java @@ -55,10 +55,10 @@ protected TcpTransportServer createTcpTransportServer(URI location, ServerSocket } @Override - public TransportServer doBind(URI location) throws IOException { - if (SslContext.getCurrentSslContext() != null) { + public TransportServer doBind(URI location, SslContext sslContext) throws IOException { + if (sslContext != null) { try { - context = SslContext.getCurrentSslContext().getSSLContext(); + context = sslContext.getSSLContext(); } catch (Exception e) { throw new IOException(e); } @@ -66,6 +66,28 @@ public TransportServer doBind(URI location) throws IOException { return super.doBind(location); } + @Override + @SuppressWarnings("deprecation") + public TransportServer doBind(URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } + + @Override + public Transport doConnect(URI location, SslContext sslContext) throws Exception { + if (sslContext != null) { + try { + context = sslContext.getSSLContext(); + } catch (Exception e) { + throw new IOException(e); + } + } + try { + return doConnect(location); + } finally { + context = null; + } + } + /** * Overriding to allow for proper configuration through reflection but * delegate to get common configuration @@ -122,17 +144,10 @@ public TcpTransport createTransport(WireFormat wireFormat, Socket socket, */ @Override protected SocketFactory createSocketFactory() throws IOException { - if (SslContext.getCurrentSslContext() != null) { - SslContext ctx = SslContext.getCurrentSslContext(); - try { - return ctx.getSSLContext().getSocketFactory(); - } catch (Exception e) { - throw IOExceptionSupport.create(e); - } - } else { - return SSLSocketFactory.getDefault(); + if (context != null) { + return context.getSocketFactory(); } - + return SSLSocketFactory.getDefault(); } } diff --git a/activemq-client/src/main/java/org/apache/activemq/transport/tcp/SslTransportFactory.java b/activemq-client/src/main/java/org/apache/activemq/transport/tcp/SslTransportFactory.java index b289eec25cf..9a48ee3a762 100644 --- a/activemq-client/src/main/java/org/apache/activemq/transport/tcp/SslTransportFactory.java +++ b/activemq-client/src/main/java/org/apache/activemq/transport/tcp/SslTransportFactory.java @@ -51,16 +51,31 @@ public class SslTransportFactory extends TcpTransportFactory { private static final Logger LOG = LoggerFactory.getLogger(SslTransportFactory.class); - /** - * Overriding to use SslTransportServer and allow for proper reflection. - */ + protected SslContext sslContext; + @Override + @SuppressWarnings("deprecation") public TransportServer doBind(final URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } + + @Override + public TransportServer doBind(final URI location, SslContext sslContext) throws IOException { + this.sslContext = sslContext; try { Map options = new HashMap(URISupport.parseParameters(location)); - ServerSocketFactory serverSocketFactory = createServerSocketFactory(); - SslTransportServer server = createSslTransportServer(location, (SSLServerSocketFactory)serverSocketFactory); + SSLServerSocketFactory serverSocketFactory; + if (sslContext != null) { + try { + serverSocketFactory = sslContext.getSSLContext().getServerSocketFactory(); + } catch (Exception e) { + throw IOExceptionSupport.create(e); + } + } else { + serverSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + } + SslTransportServer server = createSslTransportServer(location, serverSocketFactory); server.setWireFormatFactory(createWireFormatFactory(options)); IntrospectionSupport.setProperties(server, options); Map transportOptions = IntrospectionSupport.extractProperties(options, "transport."); @@ -100,9 +115,16 @@ public Transport compositeConfigure(Transport transport, WireFormat format, Map return super.compositeConfigure(transport, format, options); } - /** - * Overriding to use SslTransports. - */ + @Override + public Transport doConnect(URI location, SslContext sslContext) throws Exception { + this.sslContext = sslContext; + try { + return doConnect(location); + } finally { + this.sslContext = null; + } + } + @Override protected Transport createTransport(URI location, WireFormat wf) throws UnknownHostException, IOException { URI localLocation = null; @@ -131,37 +153,26 @@ protected Transport createTransport(URI location, WireFormat wf) throws UnknownH */ @Override protected ServerSocketFactory createServerSocketFactory() throws IOException { - if( SslContext.getCurrentSslContext()!=null ) { - SslContext ctx = SslContext.getCurrentSslContext(); + if (sslContext != null) { try { - return ctx.getSSLContext().getServerSocketFactory(); + return sslContext.getSSLContext().getServerSocketFactory(); } catch (Exception e) { throw IOExceptionSupport.create(e); } - } else { - return SSLServerSocketFactory.getDefault(); } + return SSLServerSocketFactory.getDefault(); } - /** - * Creates a new SSL SocketFactory. The given factory will use user-provided - * key and trust managers (if the user provided them). - * - * @return Newly created (Ssl)SocketFactory. - * @throws IOException - */ @Override protected SocketFactory createSocketFactory() throws IOException { - if( SslContext.getCurrentSslContext()!=null ) { - SslContext ctx = SslContext.getCurrentSslContext(); + if (sslContext != null) { try { - return ctx.getSSLContext().getSocketFactory(); + return sslContext.getSSLContext().getSocketFactory(); } catch (Exception e) { throw IOExceptionSupport.create(e); } - } else { - return SSLSocketFactory.getDefault(); } + return SSLSocketFactory.getDefault(); } @Override diff --git a/activemq-client/src/test/java/org/apache/activemq/broker/CompatibleSslContextTest.java b/activemq-client/src/test/java/org/apache/activemq/broker/CompatibleSslContextTest.java new file mode 100644 index 00000000000..8a66902afa0 --- /dev/null +++ b/activemq-client/src/test/java/org/apache/activemq/broker/CompatibleSslContextTest.java @@ -0,0 +1,199 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.broker; + +import static org.junit.Assert.*; + +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.junit.Test; + +public class CompatibleSslContextTest { + + @Test + public void testGetSSLContextReturnsNonNull() throws Exception { + CompatibleSslContext ctx = new CompatibleSslContext(); + SSLContext sslCtx = ctx.getSSLContext(); + assertNotNull(sslCtx); + assertEquals("TLS", sslCtx.getProtocol()); + } + + @Test + public void testGetSSLContextIsSingleton() throws Exception { + CompatibleSslContext ctx = new CompatibleSslContext(); + SSLContext first = ctx.getSSLContext(); + SSLContext second = ctx.getSSLContext(); + assertSame(first, second); + } + + @Test + public void testArrayConstructor() throws Exception { + TrustManager tm = new PermissiveTrustManager(); + CompatibleSslContext ctx = new CompatibleSslContext(null, new TrustManager[]{tm}, null); + + List list = ctx.getTrustManagers(); + assertEquals(1, list.size()); + assertSame(tm, list.get(0)); + assertTrue(ctx.getKeyManagers().isEmpty()); + } + + @Test + public void testListBasedGettersAndSetters() { + CompatibleSslContext ctx = new CompatibleSslContext(); + + assertTrue(ctx.getKeyManagers().isEmpty()); + assertTrue(ctx.getTrustManagers().isEmpty()); + + TrustManager tm = new PermissiveTrustManager(); + ctx.setTrustManagers(Arrays.asList(tm)); + assertEquals(1, ctx.getTrustManagers().size()); + + TrustManager[] array = ctx.getTrustManagersAsArray(); + assertEquals(1, array.length); + assertSame(tm, array[0]); + } + + @Test + public void testAddRemoveKeyManager() { + CompatibleSslContext ctx = new CompatibleSslContext(); + KeyManager km = new DummyKeyManager(); + + ctx.addKeyManager(km); + assertEquals(1, ctx.getKeyManagers().size()); + assertEquals(1, ctx.getKeyManagersAsArray().length); + + assertTrue(ctx.removeKeyManager(km)); + assertTrue(ctx.getKeyManagers().isEmpty()); + } + + @Test + public void testAddRemoveTrustManager() { + CompatibleSslContext ctx = new CompatibleSslContext(); + TrustManager tm = new PermissiveTrustManager(); + + ctx.addTrustManager(tm); + assertEquals(1, ctx.getTrustManagers().size()); + assertEquals(1, ctx.getTrustManagersAsArray().length); + + assertTrue(ctx.removeTrustManager(tm)); + assertTrue(ctx.getTrustManagers().isEmpty()); + } + + @Test + public void testProtocolOverride() throws Exception { + CompatibleSslContext ctx = new CompatibleSslContext(); + ctx.setProtocol("TLSv1.2"); + assertEquals("TLSv1.2", ctx.getProtocol()); + + SSLContext sslCtx = ctx.getSSLContext(); + assertEquals("TLSv1.2", sslCtx.getProtocol()); + } + + @Test + public void testSetSSLContextDirectly() throws Exception { + CompatibleSslContext ctx = new CompatibleSslContext(); + SSLContext manual = SSLContext.getInstance("TLS"); + manual.init(null, null, null); + ctx.setSSLContext(manual); + + assertSame(manual, ctx.getSSLContext()); + } + + @Test + public void testBeanProperties() { + CompatibleSslContext ctx = new CompatibleSslContext(); + + assertEquals("TLS", ctx.getProtocol()); + assertNull(ctx.getProvider()); + assertNull(ctx.getSecureRandom()); + + ctx.setProvider("SunJSSE"); + assertEquals("SunJSSE", ctx.getProvider()); + + SecureRandom sr = new SecureRandom(); + ctx.setSecureRandom(sr); + assertSame(sr, ctx.getSecureRandom()); + } + + @Test + public void testNoThreadLocalSideEffects() throws Exception { + SslContext before = SslContext.getCurrentSslContext(); + CompatibleSslContext ctx = new CompatibleSslContext(); + ctx.getSSLContext(); + assertSame("CompatibleSslContext must not touch ThreadLocal", + before, SslContext.getCurrentSslContext()); + } + + @Test + public void testProtectedFieldAccessFromSubclass() throws Exception { + CompatibleSslContext ctx = new CompatibleSslContext(); + + ctx.keyManagers.add(new DummyKeyManager()); + ctx.trustManagers.add(new PermissiveTrustManager()); + ctx.secureRandom = new SecureRandom(); + + assertEquals(1, ctx.getKeyManagersAsArray().length); + assertEquals(1, ctx.getTrustManagersAsArray().length); + assertNotNull(ctx.getSecureRandom()); + } + + @Test + public void testReloadIsNoOp() throws Exception { + CompatibleSslContext ctx = new CompatibleSslContext(); + SSLContext before = ctx.getSSLContext(); + ctx.reload(); + assertSame(before, ctx.getSSLContext()); + } + + @Test + public void testApiCompatibilityWithThreadLocalSslContext() throws Exception { + ThreadLocalSslContext threadLocal = new ThreadLocalSslContext(); + CompatibleSslContext compatible = new CompatibleSslContext(); + + threadLocal.setProtocol("TLSv1.2"); + compatible.setProtocol("TLSv1.2"); + + TrustManager tm = new PermissiveTrustManager(); + threadLocal.addTrustManager(tm); + compatible.addTrustManager(tm); + + assertEquals(threadLocal.getTrustManagers().size(), compatible.getTrustManagers().size()); + assertEquals(threadLocal.getTrustManagersAsArray().length, compatible.getTrustManagersAsArray().length); + assertEquals(threadLocal.getProtocol(), compatible.getProtocol()); + assertEquals(threadLocal.getSSLContext().getProtocol(), compatible.getSSLContext().getProtocol()); + } + + private static class PermissiveTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) {} + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) {} + @Override + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + } + + private static class DummyKeyManager implements KeyManager { + } +} diff --git a/activemq-client/src/test/java/org/apache/activemq/broker/DefaultSslContextTest.java b/activemq-client/src/test/java/org/apache/activemq/broker/DefaultSslContextTest.java new file mode 100644 index 00000000000..59df7f4fa31 --- /dev/null +++ b/activemq-client/src/test/java/org/apache/activemq/broker/DefaultSslContextTest.java @@ -0,0 +1,120 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.broker; + +import static org.junit.Assert.*; + +import java.security.SecureRandom; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.cert.X509Certificate; + +import org.junit.Test; + +public class DefaultSslContextTest { + + @Test + public void testGetSSLContextReturnsNonNull() throws Exception { + DefaultSslContext ctx = new DefaultSslContext(); + SSLContext sslCtx = ctx.getSSLContext(); + assertNotNull(sslCtx); + assertEquals("TLS", sslCtx.getProtocol()); + } + + @Test + public void testGetSSLContextIsSingleton() throws Exception { + DefaultSslContext ctx = new DefaultSslContext(); + SSLContext first = ctx.getSSLContext(); + SSLContext second = ctx.getSSLContext(); + assertSame(first, second); + } + + @Test + public void testConstructorWithManagers() throws Exception { + TrustManager tm = new PermissiveTrustManager(); + DefaultSslContext ctx = new DefaultSslContext(null, new TrustManager[]{tm}, null); + + SSLContext sslCtx = ctx.getSSLContext(); + assertNotNull(sslCtx); + assertArrayEquals(new TrustManager[]{tm}, ctx.getTrustManagers()); + } + + @Test + public void testProtocolOverride() throws Exception { + DefaultSslContext ctx = new DefaultSslContext(); + ctx.setProtocol("TLSv1.2"); + assertEquals("TLSv1.2", ctx.getProtocol()); + + SSLContext sslCtx = ctx.getSSLContext(); + assertEquals("TLSv1.2", sslCtx.getProtocol()); + } + + @Test + public void testReloadIsNoOp() throws Exception { + DefaultSslContext ctx = new DefaultSslContext(); + SSLContext before = ctx.getSSLContext(); + ctx.reload(); + assertSame(before, ctx.getSSLContext()); + } + + @Test + public void testBeanProperties() { + DefaultSslContext ctx = new DefaultSslContext(); + + assertNull(ctx.getKeyManagers()); + assertNull(ctx.getTrustManagers()); + assertNull(ctx.getSecureRandom()); + assertNull(ctx.getProvider()); + assertEquals("TLS", ctx.getProtocol()); + + KeyManager[] kms = new KeyManager[0]; + ctx.setKeyManagers(kms); + assertSame(kms, ctx.getKeyManagers()); + + TrustManager[] tms = new TrustManager[0]; + ctx.setTrustManagers(tms); + assertSame(tms, ctx.getTrustManagers()); + + SecureRandom sr = new SecureRandom(); + ctx.setSecureRandom(sr); + assertSame(sr, ctx.getSecureRandom()); + + ctx.setProvider("SunJSSE"); + assertEquals("SunJSSE", ctx.getProvider()); + } + + @Test + public void testNoThreadLocalSideEffects() throws Exception { + SslContext before = SslContext.getCurrentSslContext(); + DefaultSslContext ctx = new DefaultSslContext(); + ctx.getSSLContext(); + assertSame("DefaultSslContext must not touch ThreadLocal", + before, SslContext.getCurrentSslContext()); + } + + private static class PermissiveTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) {} + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) {} + @Override + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + } +} diff --git a/activemq-client/src/test/java/org/apache/activemq/broker/scheduler/SslContextTest.java b/activemq-client/src/test/java/org/apache/activemq/broker/scheduler/SslContextTest.java index f0405b04500..da4f531d84b 100644 --- a/activemq-client/src/test/java/org/apache/activemq/broker/scheduler/SslContextTest.java +++ b/activemq-client/src/test/java/org/apache/activemq/broker/scheduler/SslContextTest.java @@ -17,6 +17,7 @@ package org.apache.activemq.broker.scheduler; +import org.apache.activemq.broker.DefaultSslContext; import org.apache.activemq.broker.SslContext; import org.junit.Test; @@ -32,7 +33,7 @@ import static org.junit.Assert.assertTrue; public class SslContextTest { - SslContext underTest = new SslContext(); + SslContext underTest = new DefaultSslContext(); @Test public void testConcurrentGet() throws Exception { diff --git a/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsClientTransport.java b/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsClientTransport.java index d69f227bdd1..fbb0e2b6f3e 100644 --- a/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsClientTransport.java +++ b/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsClientTransport.java @@ -42,10 +42,18 @@ public class HttpsClientTransport extends HttpClientTransport { private boolean verifyHostName = true; public HttpsClientTransport(TextWireFormat wireFormat, URI remoteUrl) { + this(wireFormat, remoteUrl, null); + } + + public HttpsClientTransport(TextWireFormat wireFormat, URI remoteUrl, SslContext sslContext) { super(wireFormat, remoteUrl); try { - sslSocketFactory = createSocketFactory(); - } catch (IOException e) { + if (sslContext != null) { + sslSocketFactory = sslContext.getSSLContext().getSocketFactory(); + } else { + sslSocketFactory = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(); + } + } catch (Exception e) { throw new IllegalStateException("Error trying to configure TLS", e); } } @@ -69,25 +77,9 @@ private Registry createRegistry() { } } - /** - * Creates a new SSL SocketFactory. The given factory will use user-provided - * key and trust managers (if the user provided them). - * - * @return Newly created (Ssl)SocketFactory. - * @throws IOException - */ + @Deprecated protected javax.net.ssl.SSLSocketFactory createSocketFactory() throws IOException { - if (SslContext.getCurrentSslContext() != null) { - SslContext ctx = SslContext.getCurrentSslContext(); - try { - return ctx.getSSLContext().getSocketFactory(); - } catch (Exception e) { - throw IOExceptionSupport.create(e); - } - } else { - return (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(); - } - + return (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(); } @Override diff --git a/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsTransportFactory.java b/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsTransportFactory.java index d8f5ea2cd2f..8f6459bcc26 100644 --- a/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsTransportFactory.java +++ b/activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsTransportFactory.java @@ -37,15 +37,24 @@ */ public class HttpsTransportFactory extends HttpTransportFactory { + private SslContext sslContext; + public TransportServer doBind(String brokerId, URI location) throws IOException { return doBind(location); } @Override + @SuppressWarnings("deprecation") public TransportServer doBind(URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } + + @Override + public TransportServer doBind(URI location, SslContext sslContext) throws IOException { + this.sslContext = sslContext; try { Map options = new HashMap(URISupport.parseParameters(location)); - HttpsTransportServer result = new HttpsTransportServer(location, this, SslContext.getCurrentSslContext()); + HttpsTransportServer result = new HttpsTransportServer(location, this, sslContext); Map httpOptions = IntrospectionSupport.extractProperties(options, "http."); Map transportOptions = IntrospectionSupport.extractProperties(options, "transport."); result.setTransportOption(transportOptions); @@ -56,9 +65,18 @@ public TransportServer doBind(URI location) throws IOException { } } + @Override + public Transport doConnect(URI location, SslContext sslContext) throws Exception { + this.sslContext = sslContext; + try { + return doConnect(location); + } finally { + this.sslContext = null; + } + } + @Override protected Transport createTransport(URI location, WireFormat wf) throws IOException { - // need to remove options from uri try { URI uri = URISupport.removeQuery(location); @@ -69,7 +87,7 @@ protected Transport createTransport(URI location, WireFormat wf) throws IOExcept verifyHostName = Boolean.parseBoolean(transportOptions.get("verifyHostName").toString()); } - HttpsClientTransport clientTransport = new HttpsClientTransport(asTextWireFormat(wf), uri); + HttpsClientTransport clientTransport = new HttpsClientTransport(asTextWireFormat(wf), uri, sslContext); clientTransport.setVerifyHostName(verifyHostName); return clientTransport; } catch (URISyntaxException e) { diff --git a/activemq-http/src/main/java/org/apache/activemq/transport/wss/WSSTransportFactory.java b/activemq-http/src/main/java/org/apache/activemq/transport/wss/WSSTransportFactory.java index 05a8159ca52..925619b1999 100644 --- a/activemq-http/src/main/java/org/apache/activemq/transport/wss/WSSTransportFactory.java +++ b/activemq-http/src/main/java/org/apache/activemq/transport/wss/WSSTransportFactory.java @@ -39,10 +39,16 @@ public class WSSTransportFactory extends TransportFactory implements BrokerServi private BrokerService brokerService; @Override + @SuppressWarnings("deprecation") public TransportServer doBind(URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } + + @Override + public TransportServer doBind(URI location, SslContext sslContext) throws IOException { try { Map options = new HashMap(URISupport.parseParameters(location)); - WSSTransportServer result = new WSSTransportServer(location, SslContext.getCurrentSslContext()); + WSSTransportServer result = new WSSTransportServer(location, sslContext); Map httpOptions = IntrospectionSupport.extractProperties(options, "http."); Map transportOptions = IntrospectionSupport.extractProperties(options, ""); IntrospectionSupport.setProperties(result, transportOptions); diff --git a/activemq-mqtt/src/main/java/org/apache/activemq/transport/mqtt/MQTTNIOSSLTransportFactory.java b/activemq-mqtt/src/main/java/org/apache/activemq/transport/mqtt/MQTTNIOSSLTransportFactory.java index 87fc48eada7..38ec7cb3b69 100644 --- a/activemq-mqtt/src/main/java/org/apache/activemq/transport/mqtt/MQTTNIOSSLTransportFactory.java +++ b/activemq-mqtt/src/main/java/org/apache/activemq/transport/mqtt/MQTTNIOSSLTransportFactory.java @@ -75,10 +75,10 @@ public TcpTransport createTransport(WireFormat wireFormat, Socket socket, } @Override - public TransportServer doBind(URI location) throws IOException { - if (SslContext.getCurrentSslContext() != null) { + public TransportServer doBind(URI location, SslContext sslContext) throws IOException { + if (sslContext != null) { try { - context = SslContext.getCurrentSslContext().getSSLContext(); + context = sslContext.getSSLContext(); } catch (Exception e) { throw new IOException(e); } @@ -86,4 +86,10 @@ public TransportServer doBind(URI location) throws IOException { return super.doBind(location); } + @Override + @SuppressWarnings("deprecation") + public TransportServer doBind(URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } + } diff --git a/activemq-mqtt/src/test/java/org/apache/activemq/transport/mqtt/util/ResourceLoadingSslContext.java b/activemq-mqtt/src/test/java/org/apache/activemq/transport/mqtt/util/ResourceLoadingSslContext.java index b1b8375aee4..a57e61e8d0a 100644 --- a/activemq-mqtt/src/test/java/org/apache/activemq/transport/mqtt/util/ResourceLoadingSslContext.java +++ b/activemq-mqtt/src/test/java/org/apache/activemq/transport/mqtt/util/ResourceLoadingSslContext.java @@ -32,7 +32,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; -import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.CompatibleSslContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; @@ -42,7 +42,7 @@ /** * Extends the SslContext so that it's easier to configure from spring. */ -public class ResourceLoadingSslContext extends SslContext { +public class ResourceLoadingSslContext extends CompatibleSslContext { private String keyStoreType = "jks"; private String trustStoreType = "jks"; diff --git a/activemq-ra/src/test/java/org/apache/activemq/ra/SSLMAnagedConnectionFactoryTest.java b/activemq-ra/src/test/java/org/apache/activemq/ra/SSLMAnagedConnectionFactoryTest.java index 83a94a63cb1..55c6ef1e01d 100644 --- a/activemq-ra/src/test/java/org/apache/activemq/ra/SSLMAnagedConnectionFactoryTest.java +++ b/activemq-ra/src/test/java/org/apache/activemq/ra/SSLMAnagedConnectionFactoryTest.java @@ -25,6 +25,7 @@ import org.apache.activemq.broker.SslBrokerService; import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.DefaultSslContext; import org.apache.activemq.broker.TransportConnector; import org.apache.activemq.transport.TransportFactory; import org.apache.activemq.transport.tcp.SslTransportFactory; @@ -94,7 +95,7 @@ private void createAndStartBroker() throws Exception { connectionURI = connector.getPublishableConnectString(); SslTransportFactory sslFactory = new SslTransportFactory(); - SslContext ctx = new SslContext(km, tm, null); + SslContext ctx = new DefaultSslContext(km, tm, null); SslContext.setCurrentSslContext(ctx); TransportFactory.registerTransportFactory("ssl", sslFactory); } diff --git a/activemq-ra/src/test/java/org/apache/activemq/ra/SSLTest.java b/activemq-ra/src/test/java/org/apache/activemq/ra/SSLTest.java index 2aa172eb388..4ded32bd12b 100644 --- a/activemq-ra/src/test/java/org/apache/activemq/ra/SSLTest.java +++ b/activemq-ra/src/test/java/org/apache/activemq/ra/SSLTest.java @@ -65,6 +65,7 @@ import org.apache.activemq.advisory.AdvisorySupport; import org.apache.activemq.broker.SslBrokerService; import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.DefaultSslContext; import org.apache.activemq.broker.TransportConnector; import org.apache.activemq.command.ActiveMQMessage; import org.apache.activemq.command.ActiveMQQueue; @@ -115,7 +116,7 @@ private void createAndStartBroker() throws Exception { broker.waitUntilStarted(); // for client side SslTransportFactory sslFactory = new SslTransportFactory(); - SslContext ctx = new SslContext(km, tm, null); + SslContext ctx = new DefaultSslContext(km, tm, null); SslContext.setCurrentSslContext(ctx); TransportFactory.registerTransportFactory("ssl", sslFactory); } diff --git a/activemq-spring/src/main/java/org/apache/activemq/spring/SpringSslContext.java b/activemq-spring/src/main/java/org/apache/activemq/spring/SpringSslContext.java index adb7f58a820..3a25feb0053 100644 --- a/activemq-spring/src/main/java/org/apache/activemq/spring/SpringSslContext.java +++ b/activemq-spring/src/main/java/org/apache/activemq/spring/SpringSslContext.java @@ -30,7 +30,7 @@ import jakarta.annotation.PostConstruct; import javax.net.ssl.*; -import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.CompatibleSslContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; @@ -42,7 +42,7 @@ * * */ -public class SpringSslContext extends SslContext { +public class SpringSslContext extends CompatibleSslContext { private static final transient Logger LOG = LoggerFactory.getLogger(SpringSslContext.class); diff --git a/activemq-stomp/src/main/java/org/apache/activemq/transport/stomp/StompNIOSSLTransportFactory.java b/activemq-stomp/src/main/java/org/apache/activemq/transport/stomp/StompNIOSSLTransportFactory.java index 27a871224d2..24ae90a0194 100644 --- a/activemq-stomp/src/main/java/org/apache/activemq/transport/stomp/StompNIOSSLTransportFactory.java +++ b/activemq-stomp/src/main/java/org/apache/activemq/transport/stomp/StompNIOSSLTransportFactory.java @@ -73,14 +73,20 @@ public TcpTransport createTransport(WireFormat wireFormat, Socket socket, } @Override - public TransportServer doBind(URI location) throws IOException { - if (SslContext.getCurrentSslContext() != null) { + public TransportServer doBind(URI location, SslContext sslContext) throws IOException { + if (sslContext != null) { try { - context = SslContext.getCurrentSslContext().getSSLContext(); + context = sslContext.getSSLContext(); } catch (Exception e) { throw new IOException(e); } } return super.doBind(location); } + + @Override + @SuppressWarnings("deprecation") + public TransportServer doBind(URI location) throws IOException { + return doBind(location, SslContext.getCurrentSslContext()); + } } diff --git a/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/util/ResourceLoadingSslContext.java b/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/util/ResourceLoadingSslContext.java index 3bbfefc07bb..50a3a0c8458 100644 --- a/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/util/ResourceLoadingSslContext.java +++ b/activemq-stomp/src/test/java/org/apache/activemq/transport/stomp/util/ResourceLoadingSslContext.java @@ -32,7 +32,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; -import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.CompatibleSslContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; @@ -42,7 +42,7 @@ /** * Extends the SslContext so that it's easier to configure from spring. */ -public class ResourceLoadingSslContext extends SslContext { +public class ResourceLoadingSslContext extends CompatibleSslContext { private String keyStoreType="jks"; private String trustStoreType="jks"; diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/network/FailoverStaticNetworkTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/network/FailoverStaticNetworkTest.java index 03665155093..03c9f0a1d89 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/network/FailoverStaticNetworkTest.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/network/FailoverStaticNetworkTest.java @@ -44,6 +44,7 @@ import org.apache.activemq.AutoFailTestSupport; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.DefaultSslContext; import org.apache.activemq.broker.TransportConnector; import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter; @@ -120,7 +121,7 @@ private BrokerService createBroker(String listenPort, String dataDir) throws Exc public void setUp() throws Exception { KeyManager[] km = SslBrokerServiceTest.getKeyManager(); TrustManager[] tm = SslBrokerServiceTest.getTrustManager(); - sslContext = new SslContext(km, tm, null); + sslContext = new DefaultSslContext(km, tm, null); } @After diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/network/NetworkReconnectSslNioTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/network/NetworkReconnectSslNioTest.java index 7feeb892491..cbe4ab04545 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/network/NetworkReconnectSslNioTest.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/network/NetworkReconnectSslNioTest.java @@ -18,6 +18,7 @@ import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.DefaultSslContext; import org.apache.activemq.broker.TransportConnection; import org.apache.activemq.broker.TransportConnector; import org.apache.activemq.command.ConnectionError; @@ -44,7 +45,7 @@ public class NetworkReconnectSslNioTest { @Test public void testForceReconnect() throws Exception { - final SslContext sslContext = new SslContext(getKeyManager(), getTrustManager(), null); + final SslContext sslContext = new DefaultSslContext(getKeyManager(), getTrustManager(), null); BrokerService remote = new BrokerService(); remote.setBrokerName("R"); diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslBrokerServiceTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslBrokerServiceTest.java index 57406689c34..9ca8b4b7b49 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslBrokerServiceTest.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslBrokerServiceTest.java @@ -39,6 +39,7 @@ import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.SslBrokerService; import org.apache.activemq.broker.SslContext; +import org.apache.activemq.broker.DefaultSslContext; import org.apache.activemq.broker.TransportConnector; import org.apache.activemq.transport.TransportBrokerTestSupport; import org.apache.activemq.transport.TransportFactory; @@ -76,7 +77,7 @@ protected BrokerService createBroker() throws Exception { // for client side SslTransportFactory sslFactory = new SslTransportFactory(); - SslContext ctx = new SslContext(km, tm, null); + SslContext ctx = new DefaultSslContext(km, tm, null); SslContext.setCurrentSslContext(ctx); TransportFactory.registerTransportFactory("ssl", sslFactory); diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslContextBrokerServiceTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslContextBrokerServiceTest.java index a966f23cdeb..c6c977be227 100644 --- a/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslContextBrokerServiceTest.java +++ b/activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslContextBrokerServiceTest.java @@ -23,6 +23,7 @@ import junit.framework.TestCase; import org.apache.activemq.broker.BrokerService; +import org.apache.activemq.broker.CompatibleSslContext; import org.apache.activemq.broker.TransportConnector; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -43,8 +44,9 @@ public void testConfiguration() throws URISyntaxException { assertEquals(new URI("ssl://localhost:61616"), connector.getUri()); assertNotNull(broker.getSslContext()); - assertFalse(broker.getSslContext().getKeyManagers().isEmpty()); - assertFalse(broker.getSslContext().getTrustManagers().isEmpty()); + CompatibleSslContext sslCtx = (CompatibleSslContext) broker.getSslContext(); + assertFalse(sslCtx.getKeyManagers().isEmpty()); + assertFalse(sslCtx.getTrustManagers().isEmpty()); }