summaryrefslogtreecommitdiff
path: root/qpid/java/broker-core
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/java/broker-core')
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/BrokerProperties.java3
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFactoryImpl.java9
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ManagedObject.java1
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Port.java25
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java45
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java9
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java50
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPort.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java6
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPort.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPortImpl.java6
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPort.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPortImpl.java6
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java2
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java38
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java14
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java2
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java71
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java343
-rw-r--r--qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java221
20 files changed, 641 insertions, 216 deletions
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/BrokerProperties.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/BrokerProperties.java
index ccda1e1fe1..765e1e4fa5 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/BrokerProperties.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/BrokerProperties.java
@@ -40,9 +40,6 @@ public class BrokerProperties
public static final String PROPERTY_DEFAULT_SUPPORTED_PROTOCOL_REPLY = "qpid.broker_default_supported_protocol_version_reply";
public static final String PROPERTY_DISABLED_FEATURES = "qpid.broker_disabled_features";
- public static final String PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES = "qpid.broker_default_amqp_protocol_excludes";
- public static final String PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_INCLUDES = "qpid.broker_default_amqp_protocol_includes";
-
public static final String PROPERTY_MANAGEMENT_RIGHTS_INFER_ALL_ACCESS = "qpid.broker_jmx_method_rights_infer_all_access";
public static final String PROPERTY_USE_CUSTOM_RMI_SOCKET_FACTORY = "qpid.broker_jmx_use_custom_rmi_socket_factory";
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFactoryImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFactoryImpl.java
index 350e4fcd44..27d914c639 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFactoryImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectFactoryImpl.java
@@ -137,15 +137,6 @@ public class ConfiguredObjectFactoryImpl implements ConfiguredObjectFactory
else
{
factory = getConfiguredObjectTypeFactory(category, null);
- if(factory == null)
- {
- ManagedObject annotation = categoryClass.getAnnotation(ManagedObject.class);
- factory = getConfiguredObjectTypeFactory(category, annotation.defaultType());
- if(factory == null)
- {
- throw new NoFactoryForTypeException(category, annotation.defaultType());
- }
- }
}
return factory;
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ManagedObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ManagedObject.java
index 8cfb84135e..f18869bced 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ManagedObject.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ManagedObject.java
@@ -35,4 +35,5 @@ public @interface ManagedObject
boolean creatable() default true;
String defaultType() default ""; // in this case the class/interface itself is to be used
String type() default "";
+ boolean register() default true;
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Port.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Port.java
index e98ff1a79a..eca3b0c7b1 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Port.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Port.java
@@ -20,7 +20,6 @@
*/
package org.apache.qpid.server.model;
-import java.security.AccessControlException;
import java.util.Collection;
import java.util.Set;
@@ -64,30 +63,6 @@ public interface Port<X extends Port<X>> extends ConfiguredObject<X>
@ManagedAttribute
Collection<TrustStore> getTrustStores();
-
-
-
-
-
- void addTransport(Transport transport) throws IllegalStateException,
- AccessControlException,
- IllegalArgumentException;
-
- Transport removeTransport(Transport transport) throws IllegalStateException,
- AccessControlException,
- IllegalArgumentException;
-
-
- void addProtocol(Protocol protocol) throws IllegalStateException,
- AccessControlException,
- IllegalArgumentException;
-
- Protocol removeProtocol(Protocol protocol) throws IllegalStateException,
- AccessControlException,
- IllegalArgumentException;
-
- Collection<Protocol> getAvailableProtocols();
-
//children
Collection<VirtualHostAlias> getVirtualHostBindings();
Collection<Connection> getConnections();
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
index 32587c0f4e..61790441f9 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
@@ -190,40 +190,12 @@ abstract public class AbstractPort<X extends AbstractPort<X>> extends AbstractCo
}
@Override
- public void addTransport(Transport transport)
- throws IllegalStateException, AccessControlException, IllegalArgumentException
- {
- throw new IllegalStateException();
- }
-
- @Override
- public Transport removeTransport(Transport transport)
- throws IllegalStateException, AccessControlException, IllegalArgumentException
- {
- throw new IllegalStateException();
- }
-
- @Override
public Set<Protocol> getProtocols()
{
return _protocols;
}
@Override
- public void addProtocol(Protocol protocol)
- throws IllegalStateException, AccessControlException, IllegalArgumentException
- {
- throw new IllegalStateException();
- }
-
- @Override
- public Protocol removeProtocol(Protocol protocol)
- throws IllegalStateException, AccessControlException, IllegalArgumentException
- {
- throw new IllegalStateException();
- }
-
- @Override
public Collection<VirtualHostAlias> getVirtualHostBindings()
{
List<VirtualHostAlias> aliases = new ArrayList<VirtualHostAlias>();
@@ -251,19 +223,6 @@ abstract public class AbstractPort<X extends AbstractPort<X>> extends AbstractCo
}
@Override
- public Set<Protocol> getAvailableProtocols()
- {
- Set<Protocol> protocols = getProtocols();
- if(protocols == null || protocols.isEmpty())
- {
- protocols = getDefaultProtocols();
- }
- return protocols;
- }
-
- protected abstract Set<Protocol> getDefaultProtocols();
-
- @Override
public State getState()
{
return _state;
@@ -382,11 +341,11 @@ abstract public class AbstractPort<X extends AbstractPort<X>> extends AbstractCo
for (Port<?> existingPort : existingPorts)
{
- Collection<Protocol> portProtocols = existingPort.getAvailableProtocols();
+ Collection<Protocol> portProtocols = existingPort.getProtocols();
if (portProtocols != null)
{
final ArrayList<Protocol> intersection = new ArrayList(portProtocols);
- intersection.retainAll(getAvailableProtocols());
+ intersection.retainAll(getProtocols());
if(!intersection.isEmpty())
{
throw new IllegalConfigurationException("Port for protocols " + intersection + " already exists. Only one management port per protocol can be created.");
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
index fa599b4d5f..b50a289b22 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
@@ -24,6 +24,7 @@ import java.util.Set;
import org.apache.qpid.server.model.AuthenticationProvider;
import org.apache.qpid.server.model.ManagedAttribute;
+import org.apache.qpid.server.model.ManagedContextDefault;
import org.apache.qpid.server.model.ManagedObject;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Protocol;
@@ -42,6 +43,11 @@ public interface AmqpPort<X extends AmqpPort<X>> extends Port<X>
String SEND_BUFFER_SIZE = "sendBufferSize";
String RECEIVE_BUFFER_SIZE = "receiveBufferSize";
+ String DEFAULT_AMQP_PROTOCOLS = "qpid.port.default_amqp_protocols";
+
+ @ManagedContextDefault(name = DEFAULT_AMQP_PROTOCOLS)
+ String INSTALLED_PROTOCOLS = AmqpPortImpl.getInstalledProtocolsAsString();
+
@ManagedAttribute( defaultValue = AmqpPort.DEFAULT_AMQP_TCP_NO_DELAY )
boolean isTcpNoDelay();
@@ -66,8 +72,9 @@ public interface AmqpPort<X extends AmqpPort<X>> extends Port<X>
validValues = {"org.apache.qpid.server.model.port.AmqpPortImpl#getAllAvailableTransportCombinations()"})
Set<Transport> getTransports();
- @ManagedAttribute( validValues = {"org.apache.qpid.server.model.port.AmqpPortImpl#getAllAvailableProtocolCombinations()"} )
+ @ManagedAttribute( defaultValue = "${" + DEFAULT_AMQP_PROTOCOLS + "}", validValues = {"org.apache.qpid.server.model.port.AmqpPortImpl#getAllAvailableProtocolCombinations()"} )
Set<Protocol> getProtocols();
VirtualHostImpl getVirtualHost(String name);
+
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java
index 1fbc0c8bc1..2c958b00d0 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPortImpl.java
@@ -25,7 +25,6 @@ import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -43,6 +42,7 @@ import org.apache.qpid.server.logging.messages.BrokerMessages;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.KeyStore;
import org.apache.qpid.server.model.ManagedAttributeField;
+import org.apache.qpid.server.model.ManagedContextDefault;
import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.State;
@@ -53,6 +53,7 @@ import org.apache.qpid.server.plugin.QpidServiceLoader;
import org.apache.qpid.server.plugin.TransportProviderFactory;
import org.apache.qpid.server.transport.AcceptingTransport;
import org.apache.qpid.server.transport.TransportProvider;
+import org.apache.qpid.server.util.ServerScopedRuntimeException;
import org.apache.qpid.server.virtualhost.VirtualHostImpl;
import org.apache.qpid.transport.network.security.ssl.QpidMultipleTrustManager;
@@ -110,34 +111,6 @@ public class AmqpPortImpl extends AbstractPortWithAuthProvider<AmqpPortImpl> imp
return (VirtualHostImpl) _broker.findVirtualHostByName(name);
}
- protected Set<Protocol> getDefaultProtocols()
- {
- Set<Protocol> defaultProtocols = EnumSet.of(Protocol.AMQP_0_8, Protocol.AMQP_0_9, Protocol.AMQP_0_9_1,
- Protocol.AMQP_0_10, Protocol.AMQP_1_0);
- String excludedProtocols = System.getProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES);
- if (excludedProtocols != null)
- {
- String[] excludes = excludedProtocols.split(",");
- for (String exclude : excludes)
- {
- Protocol protocol = Protocol.valueOf(exclude);
- defaultProtocols.remove(protocol);
- }
- }
- String includedProtocols = System.getProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_INCLUDES);
- if (includedProtocols != null)
- {
- String[] includes = includedProtocols.split(",");
- for (String include : includes)
- {
- Protocol protocol = Protocol.valueOf(include);
- defaultProtocols.add(protocol);
- }
- }
- return defaultProtocols;
- }
-
-
@Override
protected State onActivate()
{
@@ -178,7 +151,7 @@ public class AmqpPortImpl extends AbstractPortWithAuthProvider<AmqpPortImpl> imp
_transport = transportProvider.createTransport(transportSet,
sslContext,
this,
- getAvailableProtocols(),
+ getProtocols(),
defaultSupportedProtocolReply);
_transport.start();
@@ -363,4 +336,21 @@ public class AmqpPortImpl extends AbstractPortWithAuthProvider<AmqpPortImpl> imp
}
return Collections.unmodifiableSet(combinationsAsString);
}
+
+
+ public static String getInstalledProtocolsAsString()
+ {
+ Set<Protocol> installedProtocols = getInstalledProtocols();
+ ObjectMapper mapper = new ObjectMapper();
+
+ try(StringWriter output = new StringWriter())
+ {
+ mapper.writeValue(output, installedProtocols);
+ return output.toString();
+ }
+ catch (IOException e)
+ {
+ throw new ServerScopedRuntimeException(e);
+ }
+ }
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPort.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPort.java
index fa2af121ae..51d31cb8ab 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPort.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPort.java
@@ -51,7 +51,7 @@ public interface HttpPort<X extends HttpPort<X>> extends Port<X>
validValues = {"[ \"TCP\" ]", "[ \"SSL\" ]", "[ \"TCP\", \"SSL\" ]"})
Set<Transport> getTransports();
- @ManagedAttribute( validValues = { "[ \"HTTP\"]"} )
+ @ManagedAttribute( defaultValue = "HTTP", validValues = { "[ \"HTTP\"]"} )
Set<Protocol> getProtocols();
void setPortManager(PortManager manager);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java
index a89ba9bbff..33abee9bde 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/HttpPortImpl.java
@@ -40,12 +40,6 @@ public class HttpPortImpl extends AbstractPortWithAuthProvider<HttpPortImpl> imp
super(attributes, broker);
}
- @Override
- protected Set<Protocol> getDefaultProtocols()
- {
- return Collections.singleton(Protocol.HTTP);
- }
-
public void setPortManager(PortManager manager)
{
_portManager = manager;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPort.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPort.java
index 48754e92e4..981d81a342 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPort.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPort.java
@@ -51,7 +51,7 @@ public interface JmxPort<X extends JmxPort<X>> extends Port<X>
validValues = {"[ \"TCP\" ]", "[ \"SSL\" ]"})
Set<Transport> getTransports();
- @ManagedAttribute( validValues = { "[ \"JMX_RMI\"]"} )
+ @ManagedAttribute( defaultValue = "JMX_RMI", validValues = { "[ \"JMX_RMI\"]"} )
Set<Protocol> getProtocols();
void setPortManager(PortManager manager);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPortImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPortImpl.java
index ac691c0860..a235613c29 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPortImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/JmxPortImpl.java
@@ -48,12 +48,6 @@ public class JmxPortImpl extends AbstractPortWithAuthProvider<JmxPortImpl> imple
}
@Override
- protected Set<Protocol> getDefaultProtocols()
- {
- return Collections.singleton(Protocol.JMX_RMI);
- }
-
- @Override
public void setPortManager(PortManager manager)
{
_portManager = manager;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPort.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPort.java
index d2420aa343..8fad90ada3 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPort.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPort.java
@@ -32,7 +32,7 @@ import org.apache.qpid.server.model.Transport;
public interface RmiPort<X extends RmiPort<X>> extends Port<X>
{
- @ManagedAttribute( validValues = { "[ \"RMI\"]"} )
+ @ManagedAttribute( defaultValue = "RMI", validValues = { "[ \"RMI\"]"} )
Set<Protocol> getProtocols();
@ManagedAttribute( defaultValue = "TCP",
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPortImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPortImpl.java
index e236b7cb91..82e68d75c8 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPortImpl.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/RmiPortImpl.java
@@ -56,12 +56,6 @@ public class RmiPortImpl extends AbstractPort<RmiPortImpl> implements RmiPort<Rm
}
- @Override
- protected Set<Protocol> getDefaultProtocols()
- {
- return Collections.singleton(Protocol.RMI);
- }
-
public void setPortManager(PortManager manager)
{
_portManager = manager;
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java
index 5b62f7cffd..0e532cee89 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java
@@ -46,7 +46,7 @@ import org.apache.qpid.server.security.auth.UsernamePrincipal;
import org.apache.qpid.server.security.auth.sasl.plain.PlainPasswordCallback;
import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer;
-@ManagedObject( category = false, type = "Simple" )
+@ManagedObject( category = false, type = "Simple", register = false )
public class SimpleAuthenticationManager extends AbstractAuthenticationManager<SimpleAuthenticationManager>
{
private static final Logger _logger = Logger.getLogger(SimpleAuthenticationManager.class);
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java
index c0c92f0389..b094ea96f9 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java
@@ -36,17 +36,25 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.xml.bind.DatatypeConverter;
-import org.apache.qpid.server.configuration.IllegalConfigurationException;
-
class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
{
private static final String CIPHER_NAME = "AES/CBC/PKCS5Padding";
private static final int AES_INITIALIZATION_VECTOR_LENGTH = 16;
+ private static final String AES_ALGORITHM = "AES";
private final SecretKey _secretKey;
private final SecureRandom _random = new SecureRandom();
AESKeyFileEncrypter(SecretKey secretKey)
{
+ if(secretKey == null)
+ {
+ throw new NullPointerException("A non null secret key must be supplied");
+ }
+ if(!AES_ALGORITHM.equals(secretKey.getAlgorithm()))
+ {
+ throw new IllegalArgumentException("Provided secret key was for the algorithm: " + secretKey.getAlgorithm()
+ + "when" + AES_ALGORITHM + "was needed.");
+ }
_secretKey = secretKey;
}
@@ -68,19 +76,26 @@ class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
}
catch (IOException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e)
{
- throw new IllegalConfigurationException("Unable to encrypt secret", e);
+ throw new IllegalArgumentException("Unable to encrypt secret", e);
}
}
@Override
public String decrypt(final String encrypted)
{
+ if(!isValidBase64(encrypted))
+ {
+ throw new IllegalArgumentException("Encrypted value is not valid Base 64 data: '" + encrypted + "'");
+ }
byte[] encryptedBytes = DatatypeConverter.parseBase64Binary(encrypted);
try
{
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
- cipher.init(Cipher.DECRYPT_MODE, _secretKey, new IvParameterSpec(encryptedBytes, 0,
- AES_INITIALIZATION_VECTOR_LENGTH));
+
+ IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptedBytes, 0, AES_INITIALIZATION_VECTOR_LENGTH);
+
+ cipher.init(Cipher.DECRYPT_MODE, _secretKey, ivParameterSpec);
+
return new String(readFromCipherStream(encryptedBytes,
AES_INITIALIZATION_VECTOR_LENGTH,
encryptedBytes.length - AES_INITIALIZATION_VECTOR_LENGTH,
@@ -88,10 +103,15 @@ class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
}
catch (IOException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e)
{
- throw new IllegalConfigurationException("Unable to encrypt secret", e);
+ throw new IllegalArgumentException("Unable to encrypt secret", e);
}
}
+ private boolean isValidBase64(final String encrypted)
+ {
+ return encrypted.matches("^([\\w\\d+/]{4})*([\\w\\d+/]{2}==|[\\w\\d+/]{3}=)?$");
+ }
+
private byte[] readFromCipherStream(final byte[] unencryptedBytes, final Cipher cipher) throws IOException
{
@@ -106,16 +126,16 @@ class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
offset,
length), cipher))
{
- byte[] buf = new byte[1024];
+ byte[] buf = new byte[512];
int pos = 0;
int read;
while ((read = cipherInputStream.read(buf, pos, buf.length - pos)) != -1)
{
pos += read;
- if (pos == buf.length - 1)
+ if (pos == buf.length)
{
byte[] tmp = buf;
- buf = new byte[buf.length + 1024];
+ buf = new byte[buf.length + 512];
System.arraycopy(tmp, 0, buf, 0, tmp.length);
}
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
index 447f19b7ce..ef92c2a131 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
@@ -46,13 +46,15 @@ import org.apache.qpid.server.plugin.PluggableService;
@PluggableService
public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterFactory
{
- private static final String ENCRYPTER_KEY_FILE = "encrypter.key.file";
+ static final String ENCRYPTER_KEY_FILE = "encrypter.key.file";
private static final int AES_KEY_SIZE_BITS = 256;
private static final int AES_KEY_SIZE_BYTES = AES_KEY_SIZE_BITS / 8;
private static final String AES_ALGORITHM = "AES";
- public static String TYPE = "AESKeyFile";
+ public static final String TYPE = "AESKeyFile";
+
+ static final String DEFAULT_KEYS_SUBDIR_NAME = ".keys";
@Override
public ConfigurationSecretEncrypter createEncrypter(final ConfiguredObject<?> object)
@@ -66,7 +68,7 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
{
fileLocation = object.getContextValue(String.class, BrokerOptions.QPID_WORK_DIR)
- + File.separator + ".keys" + File.separator
+ + File.separator + DEFAULT_KEYS_SUBDIR_NAME + File.separator
+ object.getCategoryClass().getSimpleName() + "_"
+ object.getName() + ".key";
@@ -94,14 +96,14 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
|| permissions.contains(PosixFilePermission.GROUP_WRITE)
|| permissions.contains(PosixFilePermission.OTHERS_WRITE))
{
- throw new IllegalStateException("Key file '"
+ throw new IllegalArgumentException("Key file '"
+ fileLocation
+ "' has incorrect permissions. Only the owner "
+ "should be able to read or write this file.");
}
if(Files.size(file.toPath()) != AES_KEY_SIZE_BYTES)
{
- throw new IllegalConfigurationException("Key file '" + fileLocation + "' contains an incorrect about of data");
+ throw new IllegalArgumentException("Key file '" + fileLocation + "' contains an incorrect about of data");
}
try(FileInputStream inputStream = new FileInputStream(file))
@@ -151,7 +153,7 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
}
catch (NoSuchAlgorithmException | IOException e)
{
- throw new IllegalConfigurationException("Cannot create key file: " + e.getMessage(), e);
+ throw new IllegalArgumentException("Cannot create key file: " + e.getMessage(), e);
}
}
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
index 450fc30bf2..eacc4f2458 100644
--- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java
@@ -251,7 +251,7 @@ public abstract class AbstractVirtualHost<X extends AbstractVirtualHost<X>> exte
{
for(Port port :_broker.getPorts())
{
- if (Protocol.hasAmqpProtocol(port.getAvailableProtocols()))
+ if (Protocol.hasAmqpProtocol(port.getProtocols()))
{
_aliases.add(new VirtualHostAliasAdapter(this, port));
}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java
index 48681a6075..523203c756 100644
--- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java
@@ -25,16 +25,13 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
-import org.apache.qpid.server.configuration.BrokerProperties;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor;
import org.apache.qpid.server.configuration.updater.TaskExecutor;
@@ -100,8 +97,6 @@ public class PortFactoryTest extends QpidTestCase
}
- setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES, null);
- setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_INCLUDES, null);
_factory = new ConfiguredObjectFactoryImpl(BrokerModel.getInstance());
_attributes.put(Port.ID, _portId);
_attributes.put(Port.NAME, getName());
@@ -114,62 +109,6 @@ public class PortFactoryTest extends QpidTestCase
_attributes.put(Port.BINDING_ADDRESS, "127.0.0.1");
}
- public void testDefaultProtocols()
- {
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.PORT, 1);
- attributes.put(Port.NAME, getName());
- attributes.put(Port.DESIRED_STATE, State.QUIESCED);
-
- attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName);
- Port<?> port = _factory.create(Port.class, attributes, _broker);
-
- Collection<Protocol> protocols = port.getAvailableProtocols();
-
- EnumSet<Protocol> expected = EnumSet.of(Protocol.AMQP_0_8, Protocol.AMQP_0_9, Protocol.AMQP_0_9_1, Protocol.AMQP_0_10,
- Protocol.AMQP_1_0);
- assertEquals("Unexpected protocols", new HashSet<Protocol>(expected), new HashSet<Protocol>(protocols));
- }
-
- public void testDefaultProtocolsWhenProtocolExcludeSystemPropertyIsSet()
- {
- setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES, Protocol.AMQP_1_0.name() + ","
- + Protocol.AMQP_0_10.name());
-
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.PORT, 1);
- attributes.put(Port.NAME, getName());
- attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName);
- attributes.put(Port.DESIRED_STATE, State.QUIESCED);
- Port<?> port = _factory.create(Port.class, attributes, _broker);
-
-
- Collection<Protocol> protocols = port.getAvailableProtocols();
-
- EnumSet<Protocol> expected = EnumSet.of(Protocol.AMQP_0_8, Protocol.AMQP_0_9, Protocol.AMQP_0_9_1);
- assertEquals("Unexpected protocols", new HashSet<Protocol>(expected), new HashSet<Protocol>(protocols));
- }
-
- public void testDefaultProtocolsWhenProtocolIncludeSystemPropertyIsSet()
- {
- setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES, Protocol.AMQP_1_0.name() + ","
- + Protocol.AMQP_0_10.name() + "," + Protocol.AMQP_0_9_1.name());
- setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_INCLUDES, Protocol.AMQP_0_10.name() + ","
- + Protocol.AMQP_0_9_1.name());
-
- Map<String, Object> attributes = new HashMap<String, Object>();
- attributes.put(Port.PORT, 1);
- attributes.put(Port.NAME, getName());
- attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName);
- attributes.put(Port.DESIRED_STATE, State.QUIESCED);
- Port<?> port = _factory.create(Port.class, attributes, _broker);
-
- Collection<Protocol> protocols = port.getAvailableProtocols();
-
- EnumSet<Protocol> expected = EnumSet.of(Protocol.AMQP_0_8, Protocol.AMQP_0_9, Protocol.AMQP_0_9_1, Protocol.AMQP_0_10);
- assertEquals("Unexpected protocols", new HashSet<Protocol>(expected), new HashSet<Protocol>(protocols));
- }
-
public void testCreatePortWithMinimumAttributes()
{
Map<String, Object> attributes = new HashMap<String, Object>();
@@ -184,8 +123,6 @@ public class PortFactoryTest extends QpidTestCase
assertTrue(port instanceof AmqpPort);
assertEquals("Unexpected port", 1, port.getPort());
assertEquals("Unexpected transports", Collections.singleton(PortFactory.DEFAULT_TRANSPORT), port.getTransports());
- assertEquals("Unexpected protocols", EnumSet.of(Protocol.AMQP_0_8, Protocol.AMQP_0_9, Protocol.AMQP_0_9_1, Protocol.AMQP_0_10,
- Protocol.AMQP_1_0), port.getAvailableProtocols());
assertEquals("Unexpected send buffer size", PortFactory.DEFAULT_AMQP_SEND_BUFFER_SIZE,
port.getAttribute(AmqpPort.SEND_BUFFER_SIZE));
assertEquals("Unexpected receive buffer size", PortFactory.DEFAULT_AMQP_RECEIVE_BUFFER_SIZE,
@@ -333,7 +270,7 @@ public class PortFactoryTest extends QpidTestCase
{
assertEquals(_tcpTransports, port.getTransports());
}
- assertEquals(amqp010ProtocolSet, port.getAvailableProtocols());
+ assertEquals(amqp010ProtocolSet, port.getProtocols());
assertEquals("Unexpected send buffer size", 2, port.getAttribute(AmqpPort.SEND_BUFFER_SIZE));
assertEquals("Unexpected receive buffer size", 1, port.getAttribute(AmqpPort.RECEIVE_BUFFER_SIZE));
assertEquals("Unexpected need client auth", needClientAuth, port.getAttribute(Port.NEED_CLIENT_AUTH));
@@ -361,7 +298,7 @@ public class PortFactoryTest extends QpidTestCase
assertEquals(_portId, port.getId());
assertEquals(_portNumber, port.getPort());
assertEquals(_tcpTransports, port.getTransports());
- assertEquals(nonAmqpProtocolSet, port.getAvailableProtocols());
+ assertEquals(nonAmqpProtocolSet, port.getProtocols());
}
public void testCreateNonAmqpPortWithPartiallySetAttributes()
@@ -382,7 +319,7 @@ public class PortFactoryTest extends QpidTestCase
assertEquals(_portId, port.getId());
assertEquals(_portNumber, port.getPort());
assertEquals(Collections.singleton(PortFactory.DEFAULT_TRANSPORT), port.getTransports());
- assertEquals(nonAmqpProtocolSet, port.getAvailableProtocols());
+ assertEquals(nonAmqpProtocolSet, port.getProtocols());
}
@@ -411,7 +348,7 @@ public class PortFactoryTest extends QpidTestCase
attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.RMI));
Port rmiPort = mock(Port.class);
- when(rmiPort.getAvailableProtocols()).thenReturn(Collections.singleton(Protocol.RMI));
+ when(rmiPort.getProtocols()).thenReturn(Collections.singleton(Protocol.RMI));
when(_broker.getPorts()).thenReturn(Collections.singletonList(rmiPort));
try
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java
new file mode 100644
index 0000000000..320c5dbdc8
--- /dev/null
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java
@@ -0,0 +1,343 @@
+/*
+ *
+ * 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.qpid.server.security.encryption;
+
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.crypto.Cipher;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import org.apache.qpid.server.BrokerOptions;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+public class AESKeyFileEncrypterFactoryTest extends QpidTestCase
+{
+ private Broker _broker;
+ private Path _tmpDir;
+ private AESKeyFileEncrypterFactory _factory;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ _broker = mock(Broker.class);
+ _tmpDir = Files.createTempDirectory(getTestName());
+
+ when(_broker.getContextKeys(eq(false))).thenReturn(Collections.<String>emptySet());
+ when(_broker.getContextValue(eq(String.class), eq(BrokerOptions.QPID_WORK_DIR))).thenReturn(_tmpDir.toString());
+ when(_broker.getCategoryClass()).thenReturn(Broker.class);
+ when(_broker.getName()).thenReturn(getName());
+ final ArgumentCaptor<Map> contextCaptor = ArgumentCaptor.forClass(Map.class);
+
+ when(_broker.setAttribute(eq("context"), anyMap(), contextCaptor.capture() )).thenAnswer(new Answer<Void>() {
+
+ @Override
+ public Void answer(final InvocationOnMock invocationOnMock) throws Throwable
+ {
+ Map replacementContext = contextCaptor.getValue();
+ when(_broker.getContext()).thenReturn(replacementContext);
+ return null;
+ }
+ });
+
+ _factory = new AESKeyFileEncrypterFactory();
+ }
+
+ public void testCreateKeyInDefaultLocation() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ ConfigurationSecretEncrypter encrypter = _factory.createEncrypter(_broker);
+
+ KeyFilePathChecker keyFilePathChecker = new KeyFilePathChecker();
+
+ doChecks(encrypter, keyFilePathChecker);
+
+ String pathName = (String) _broker.getContext().get(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE);
+
+ // check the context variable was set
+ assertEquals(keyFilePathChecker.getKeyFile().toString(), pathName);
+ }
+ }
+
+ private void doChecks(final ConfigurationSecretEncrypter encrypter,
+ final KeyFilePathChecker keyFilePathChecker) throws IOException
+ {
+ // walk the directory to find the file
+ Files.walkFileTree(_tmpDir, keyFilePathChecker);
+
+ // check the file was actually found
+ assertNotNull(keyFilePathChecker.getKeyFile());
+
+ String secret = "notasecret";
+
+ // check the encrypter works
+ assertEquals(secret, encrypter.decrypt(encrypter.encrypt(secret)));
+
+ }
+
+ public void testSettingContextKeyLeadsToFileCreation() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ String filename = UUID.randomUUID().toString() + ".key";
+ String subdirName = getTestName() + File.separator + "test";
+ String fileLocation = _tmpDir.toString() + File.separator + subdirName + File.separator + filename;
+
+ when(_broker.getContextKeys(eq(false))).thenReturn(Collections.singleton(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE));
+ when(_broker.getContextValue(eq(String.class),
+ eq(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE))).thenReturn(fileLocation);
+
+ ConfigurationSecretEncrypter encrypter = _factory.createEncrypter(_broker);
+
+ KeyFilePathChecker keyFilePathChecker = new KeyFilePathChecker(subdirName, filename);
+
+ doChecks(encrypter, keyFilePathChecker);
+ }
+ }
+
+
+ public void testUnableToCreateFileInSpecifiedLocation() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+
+ String filename = UUID.randomUUID().toString() + ".key";
+ String subdirName = getTestName() + File.separator + "test";
+ String fileLocation = _tmpDir.toString() + File.separator + subdirName + File.separator + filename;
+
+ when(_broker.getContextKeys(eq(false))).thenReturn(Collections.singleton(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE));
+ when(_broker.getContextValue(eq(String.class),
+ eq(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE))).thenReturn(fileLocation);
+
+ Files.createDirectories(Paths.get(fileLocation));
+
+ try
+ {
+ ConfigurationSecretEncrypter encrypter = _factory.createEncrypter(_broker);
+ fail("should not be able to create a key file where a directory currently is");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+ }
+
+
+ public void testPermissionsAreChecked() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+
+ String filename = UUID.randomUUID().toString() + ".key";
+ String subdirName = getTestName() + File.separator + "test";
+ String fileLocation = _tmpDir.toString() + File.separator + subdirName + File.separator + filename;
+
+ when(_broker.getContextKeys(eq(false))).thenReturn(Collections.singleton(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE));
+ when(_broker.getContextValue(eq(String.class),
+ eq(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE))).thenReturn(fileLocation);
+
+ Files.createDirectories(Paths.get(_tmpDir.toString(), subdirName));
+
+ File file = new File(fileLocation);
+ file.createNewFile();
+ Files.setPosixFilePermissions(file.toPath(),
+ EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.GROUP_READ));
+
+ try
+ {
+ ConfigurationSecretEncrypter encrypter = _factory.createEncrypter(_broker);
+ fail("should not be able to create a key file where the file is readable");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+ }
+
+ public void testInvalidKey() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ String filename = UUID.randomUUID().toString() + ".key";
+ String subdirName = getTestName() + File.separator + "test";
+ String fileLocation = _tmpDir.toString() + File.separator + subdirName + File.separator + filename;
+
+ when(_broker.getContextKeys(eq(false))).thenReturn(Collections.singleton(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE));
+ when(_broker.getContextValue(eq(String.class),
+ eq(AESKeyFileEncrypterFactory.ENCRYPTER_KEY_FILE))).thenReturn(fileLocation);
+
+ Files.createDirectories(Paths.get(_tmpDir.toString(), subdirName));
+
+ File file = new File(fileLocation);
+ try (FileOutputStream fos = new FileOutputStream(file))
+ {
+ fos.write("This is not an AES key. It is a string saying it is not an AES key".getBytes(
+ StandardCharsets.US_ASCII));
+ }
+ Files.setPosixFilePermissions(file.toPath(), EnumSet.of(PosixFilePermission.OWNER_READ));
+
+ try
+ {
+ ConfigurationSecretEncrypter encrypter = _factory.createEncrypter(_broker);
+ fail("should not be able to start where the key is not a valid key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ Files.walkFileTree(_tmpDir,
+ new SimpleFileVisitor<Path>()
+ {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
+ throws IOException
+ {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
+ throws IOException
+ {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ super.tearDown();
+ }
+
+ private boolean isStrongEncryptionEnabled() throws NoSuchAlgorithmException
+ {
+ return Cipher.getMaxAllowedKeyLength("AES")>=256;
+ }
+
+ private class KeyFilePathChecker extends SimpleFileVisitor<Path>
+ {
+
+ private final String _fileName;
+ private final String _subdirName;
+ private Path _keyFile;
+ private boolean _inKeysSubdir;
+
+ public KeyFilePathChecker()
+ {
+ this(AESKeyFileEncrypterFactory.DEFAULT_KEYS_SUBDIR_NAME, "Broker_" + AESKeyFileEncrypterFactoryTest.this.getName() + ".key");
+ }
+
+ public KeyFilePathChecker(final String subdirName, final String fileName)
+ {
+ _subdirName = subdirName;
+ _fileName = fileName;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException
+ {
+ if(!_inKeysSubdir && dir.endsWith(_subdirName))
+ {
+ _inKeysSubdir = true;
+ assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.OTHERS_READ));
+ assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.OTHERS_WRITE));
+ assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.OTHERS_EXECUTE));
+
+ assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.GROUP_READ));
+ assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.GROUP_WRITE));
+ assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.GROUP_EXECUTE));
+ return FileVisitResult.CONTINUE;
+ }
+ else
+ {
+ return _inKeysSubdir ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE;
+ }
+
+ }
+
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException
+ {
+ if(_inKeysSubdir)
+ {
+ if(file.endsWith(_fileName))
+ {
+ _keyFile = file;
+
+ assertFalse(Files.getPosixFilePermissions(file).contains(PosixFilePermission.OTHERS_READ));
+ assertFalse(Files.getPosixFilePermissions(file).contains(PosixFilePermission.OTHERS_WRITE));
+ assertFalse(Files.getPosixFilePermissions(file).contains(PosixFilePermission.OTHERS_EXECUTE));
+
+ assertFalse(Files.getPosixFilePermissions(file).contains(PosixFilePermission.GROUP_READ));
+ assertFalse(Files.getPosixFilePermissions(file).contains(PosixFilePermission.GROUP_WRITE));
+ assertFalse(Files.getPosixFilePermissions(file).contains(PosixFilePermission.GROUP_EXECUTE));
+
+ return FileVisitResult.TERMINATE;
+ }
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException
+ {
+ _inKeysSubdir = false;
+ return FileVisitResult.CONTINUE;
+ }
+
+ public Path getKeyFile()
+ {
+ return _keyFile;
+ }
+
+ }
+}
diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java
new file mode 100644
index 0000000000..3feb458a8b
--- /dev/null
+++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java
@@ -0,0 +1,221 @@
+/*
+ *
+ * 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.qpid.server.security.encryption;
+
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.qpid.test.utils.QpidTestCase;
+
+public class AESKeyFileEncrypterTest extends QpidTestCase
+{
+ private final SecureRandom _random = new SecureRandom();
+ public static final String PLAINTEXT = "notaverygoodpassword";
+
+ public void testSimpleEncryptDecrypt() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ doTestSimpleEncryptDecrypt(PLAINTEXT);
+ }
+ }
+
+
+ public void testRepeatedEncryptionsReturnDifferentValues() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ SecretKeySpec secretKey = createSecretKey();
+ AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
+
+ Set<String> encryptions = new HashSet<>();
+
+ int iterations = 100;
+
+ for (int i = 0; i < iterations; i++)
+ {
+ encryptions.add(encrypter.encrypt(PLAINTEXT));
+ }
+
+ assertEquals("Not all encryptions were distinct", iterations, encryptions.size());
+
+ for (String encrypted : encryptions)
+ {
+ assertEquals("Not all encryptions decrypt correctly", PLAINTEXT, encrypter.decrypt(encrypted));
+ }
+ }
+ }
+
+ public void testCreationFailsOnInvalidSecret() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ try
+ {
+ new AESKeyFileEncrypter(null);
+ fail("An encrypter should not be creatable from a null key");
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ PBEKeySpec keySpec = new PBEKeySpec("password".toCharArray());
+ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
+ new AESKeyFileEncrypter(factory.generateSecret(keySpec));
+ fail("An encrypter should not be creatable from the wrong type of secret key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+ }
+
+ public void testEncryptionOfEmptyString() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ String text = "";
+ doTestSimpleEncryptDecrypt(text);
+ }
+ }
+
+ private void doTestSimpleEncryptDecrypt(final String text)
+ {
+ SecretKeySpec secretKey = createSecretKey();
+ AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
+
+ String encrypted = encrypter.encrypt(text);
+ assertNotNull("Encrypter did not return a result from encryption", encrypted);
+ assertFalse("Plain text and encrypted version are equal", text.equals(encrypted));
+ String decrypted = encrypter.decrypt(encrypted);
+ assertNotNull("Encrypter did not return a result from decryption",decrypted);
+ assertTrue("Encryption was not reversible", text.equals(decrypted));
+ }
+
+ public void testEncryptingNullFails() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ try
+ {
+ SecretKeySpec secretKey = createSecretKey();
+ AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
+
+ String encrypted = encrypter.encrypt(null);
+ fail("Attempting to encrypt null should fail");
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+ }
+ }
+
+ public void testEncryptingVeryLargeSecret() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ Random random = new Random();
+ byte[] data = new byte[4096];
+ random.nextBytes(data);
+ for (int i = 0; i < data.length; i++)
+ {
+ data[i] = (byte) (data[i] & 0xEF);
+ }
+ doTestSimpleEncryptDecrypt(new String(data, StandardCharsets.US_ASCII));
+ }
+ }
+
+ private boolean isStrongEncryptionEnabled() throws NoSuchAlgorithmException
+ {
+ return Cipher.getMaxAllowedKeyLength("AES")>=256;
+ }
+
+ public void testDecryptNonsense() throws Exception
+ {
+ if(isStrongEncryptionEnabled())
+ {
+ SecretKeySpec secretKey = createSecretKey();
+ AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
+
+
+ try
+ {
+ encrypter.decrypt(null);
+ fail("Should not decrypt a null value");
+ }
+ catch (NullPointerException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ encrypter.decrypt("");
+ fail("Should not decrypt the empty String");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ encrypter.decrypt("thisisnonsense");
+ fail("Should not decrypt a small amount of nonsense");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+
+ try
+ {
+ String answer = encrypter.decrypt("thisisn'tvalidBase64!soitshouldfailwithanIllegalArgumentException");
+ fail("Should not decrypt a larger amount of nonsense");
+ }
+ catch (IllegalArgumentException e)
+ {
+ // pass
+ }
+ }
+ }
+
+ private SecretKeySpec createSecretKey()
+ {
+ final byte[] keyData = new byte[32];
+ _random.nextBytes(keyData);
+ return new SecretKeySpec(keyData, "AES");
+ }
+}