You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2021/02/14 23:02:05 UTC
[qpid-broker-j] 01/04: QPID-8504:[Broker-J]Change encryption mode
to GCM
This is an automated email from the ASF dual-hosted git repository.
orudyy pushed a commit to branch 8.0.x
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git
commit f0993392c62e85e01f667966a40eca6d9af557db
Author: Dedeepya T <de...@yahoo.co.in>
AuthorDate: Sat Feb 13 13:56:17 2021 +0530
QPID-8504:[Broker-J]Change encryption mode to GCM
---
.../encryption/AESGCMKeyFileEncrypter.java | 115 +++++++
.../encryption/AESGCMKeyFileEncrypterFactory.java | 129 ++++++++
.../security/encryption/AESKeyFileEncrypter.java | 47 +--
.../encryption/AESKeyFileEncrypterFactory.java | 315 ++----------------
...ava => AbstractAESKeyFileEncrypterFactory.java} | 234 +++++---------
.../security/encryption/EncryptionHelper.java | 72 +++++
...java => AESGCMKeyFileEncrypterFactoryTest.java} | 27 +-
.../encryption/AESGCMKeyFileEncrypterTest.java | 359 +++++++++++++++++++++
.../encryption/AESKeyFileEncrypterFactoryTest.java | 20 +-
.../encryption/AESKeyFileEncrypterTest.java | 47 ++-
10 files changed, 835 insertions(+), 530 deletions(-)
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypter.java b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypter.java
new file mode 100644
index 0000000..1cf736c
--- /dev/null
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypter.java
@@ -0,0 +1,115 @@
+/*
+ * 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.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+import org.apache.qpid.server.util.Strings;
+
+class AESGCMKeyFileEncrypter implements ConfigurationSecretEncrypter
+{
+ private static final String CIPHER_NAME = "AES/GCM/NoPadding";
+ private static final int GCM_INITIALIZATION_VECTOR_LENGTH = 12;
+ private static final int GCM_TAG_LENGTH = 128;
+ private static final String AES_ALGORITHM = "AES";
+ private final SecureRandom _random = new SecureRandom();
+ private final SecretKey _secretKey;
+
+ AESGCMKeyFileEncrypter(SecretKey secretKey)
+ {
+ if (secretKey == null)
+ {
+ throw new IllegalArgumentException("A non null secret key must be supplied");
+ }
+ if (!AES_ALGORITHM.equals(secretKey.getAlgorithm()))
+ {
+ throw new IllegalArgumentException(String.format(
+ "Provided secret key was for the algorithm: %s when %s was needed.",
+ secretKey.getAlgorithm(),
+ AES_ALGORITHM));
+ }
+ _secretKey = secretKey;
+ }
+
+ @Override
+ public String encrypt(final String unencrypted)
+ {
+ byte[] unencryptedBytes = unencrypted.getBytes(StandardCharsets.UTF_8);
+ try
+ {
+ byte[] ivbytes = new byte[GCM_INITIALIZATION_VECTOR_LENGTH];
+ _random.nextBytes(ivbytes);
+ Cipher cipher = Cipher.getInstance(CIPHER_NAME);
+ GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, ivbytes);
+ cipher.init(Cipher.ENCRYPT_MODE, _secretKey, gcmParameterSpec);
+ byte[] encryptedBytes = EncryptionHelper.readFromCipherStream(unencryptedBytes, cipher);
+ byte[] output = new byte[GCM_INITIALIZATION_VECTOR_LENGTH + encryptedBytes.length];
+ System.arraycopy(ivbytes, 0, output, 0, GCM_INITIALIZATION_VECTOR_LENGTH);
+ System.arraycopy(encryptedBytes, 0, output, GCM_INITIALIZATION_VECTOR_LENGTH, encryptedBytes.length);
+ return Base64.getEncoder().encodeToString(output);
+ }
+ catch (IOException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e)
+ {
+ throw new IllegalArgumentException("Unable to encrypt secret", e);
+ }
+ }
+
+ @Override
+ public String decrypt(final String encrypted)
+ {
+ if (!EncryptionHelper.isValidBase64(encrypted))
+ {
+ throw new IllegalArgumentException("Encrypted value is not valid Base 64 data: '" + encrypted + "'");
+ }
+ byte[] encryptedBytes = Strings.decodeBase64(encrypted);
+ try
+ {
+ Cipher cipher = Cipher.getInstance(CIPHER_NAME);
+ byte[] ivbytes = new byte[GCM_INITIALIZATION_VECTOR_LENGTH];
+ if (encryptedBytes.length >= GCM_INITIALIZATION_VECTOR_LENGTH)
+ {
+ System.arraycopy(encryptedBytes, 0, ivbytes, 0, GCM_INITIALIZATION_VECTOR_LENGTH);
+ }
+ GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, ivbytes);
+ cipher.init(Cipher.DECRYPT_MODE, _secretKey, gcmParameterSpec);
+ return new String(EncryptionHelper.readFromCipherStream(encryptedBytes,
+ GCM_INITIALIZATION_VECTOR_LENGTH,
+ encryptedBytes.length
+ - GCM_INITIALIZATION_VECTOR_LENGTH,
+ cipher), StandardCharsets.UTF_8);
+ }
+ catch (IOException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e)
+ {
+ throw new IllegalArgumentException("Unable to decrypt secret", e);
+ }
+ }
+}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterFactory.java b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterFactory.java
new file mode 100644
index 0000000..da6f1ae
--- /dev/null
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterFactory.java
@@ -0,0 +1,129 @@
+/*
+ * 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.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.SystemConfig;
+import org.apache.qpid.server.plugin.ConditionallyAvailable;
+import org.apache.qpid.server.plugin.ConfigurationSecretEncrypterFactory;
+import org.apache.qpid.server.plugin.PluggableService;
+
+@PluggableService
+public class AESGCMKeyFileEncrypterFactory extends AbstractAESKeyFileEncrypterFactory
+ implements ConfigurationSecretEncrypterFactory, ConditionallyAvailable
+{
+ public static final String TYPE = "AESGCMKeyFile";
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAESKeyFileEncrypterFactory.class);
+
+ @Override
+ public String getType()
+ {
+ return TYPE;
+ }
+
+ @Override
+ public ConfigurationSecretEncrypter createEncrypter(final ConfiguredObject<?> object)
+ {
+ String fileLocation;
+ if (object.getContextKeys(false).contains(ENCRYPTER_KEY_FILE))
+ {
+ fileLocation = object.getContextValue(String.class, ENCRYPTER_KEY_FILE);
+ }
+ else
+ {
+
+ fileLocation = object.getContextValue(String.class, SystemConfig.QPID_WORK_DIR)
+ + File.separator + DEFAULT_KEYS_SUBDIR_NAME + File.separator
+ + object.getCategoryClass().getSimpleName() + "_"
+ + object.getName() + ".key";
+
+ Map<String, String> context = object.getContext();
+ Map<String, String> modifiedContext = new LinkedHashMap<>(context);
+ modifiedContext.put(ENCRYPTER_KEY_FILE, fileLocation);
+
+ object.setAttributes(Collections.<String, Object>singletonMap(ConfiguredObject.CONTEXT, modifiedContext));
+ }
+ File file = new File(fileLocation);
+ if (!file.exists())
+ {
+ LOGGER.warn(
+ "Configuration encryption is enabled, but no configuration secret was found. A new configuration secret will be created at '{}'.",
+ fileLocation);
+ try
+ {
+ createAndPopulateKeyFile(file);
+ }
+ catch (IllegalAccessException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ if (!file.isFile())
+ {
+ throw new IllegalArgumentException(String.format("File '%s' is not a regular file.", fileLocation));
+ }
+ try
+ {
+ checkFilePermissions(fileLocation, file);
+ if (Files.size(file.toPath()) != AES_KEY_SIZE_BYTES)
+ {
+ throw new IllegalArgumentException(String.format("Key file '%s' contains an incorrect about of data",
+ fileLocation));
+ }
+
+ try (FileInputStream inputStream = new FileInputStream(file))
+ {
+ byte[] key = new byte[AES_KEY_SIZE_BYTES];
+ int pos = 0;
+ int read;
+ while (pos < key.length && -1 != (read = inputStream.read(key, pos, key.length - pos)))
+ {
+ pos += read;
+ }
+ if (pos != key.length)
+ {
+ throw new IllegalConfigurationException(String.format(
+ "Key file '%s' contained an incorrect about of data",
+ fileLocation));
+ }
+ SecretKeySpec keySpec = new SecretKeySpec(key, AES_ALGORITHM);
+ return new AESGCMKeyFileEncrypter(keySpec);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new IllegalConfigurationException("Unable to get file permissions: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java
index ed90d4f..d4a4ace 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypter.java
@@ -21,7 +21,6 @@
package org.apache.qpid.server.security.encryption;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
@@ -31,7 +30,6 @@ import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
@@ -70,7 +68,7 @@ class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
_random.nextBytes(ivbytes);
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, _secretKey, new IvParameterSpec(ivbytes));
- byte[] encryptedBytes = readFromCipherStream(unencryptedBytes, cipher);
+ byte[] encryptedBytes = EncryptionHelper.readFromCipherStream(unencryptedBytes, cipher);
byte[] output = new byte[AES_INITIALIZATION_VECTOR_LENGTH + encryptedBytes.length];
System.arraycopy(ivbytes, 0, output, 0, AES_INITIALIZATION_VECTOR_LENGTH);
System.arraycopy(encryptedBytes, 0, output, AES_INITIALIZATION_VECTOR_LENGTH, encryptedBytes.length);
@@ -85,7 +83,7 @@ class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
@Override
public String decrypt(final String encrypted)
{
- if(!isValidBase64(encrypted))
+ if(!EncryptionHelper.isValidBase64(encrypted))
{
throw new IllegalArgumentException("Encrypted value is not valid Base 64 data: '" + encrypted + "'");
}
@@ -98,7 +96,7 @@ class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
cipher.init(Cipher.DECRYPT_MODE, _secretKey, ivParameterSpec);
- return new String(readFromCipherStream(encryptedBytes,
+ return new String(EncryptionHelper.readFromCipherStream(encryptedBytes,
AES_INITIALIZATION_VECTOR_LENGTH,
encryptedBytes.length - AES_INITIALIZATION_VECTOR_LENGTH,
cipher), StandardCharsets.UTF_8);
@@ -109,44 +107,5 @@ class AESKeyFileEncrypter implements ConfigurationSecretEncrypter
}
}
- 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
- {
- return readFromCipherStream(unencryptedBytes, 0, unencryptedBytes.length, cipher);
- }
-
- private byte[] readFromCipherStream(final byte[] unencryptedBytes, int offset, int length, final Cipher cipher)
- throws IOException
- {
- final byte[] encryptedBytes;
- try (CipherInputStream cipherInputStream = new CipherInputStream(new ByteArrayInputStream(unencryptedBytes,
- offset,
- length), cipher))
- {
- 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)
- {
- byte[] tmp = buf;
- buf = new byte[buf.length + 512];
- System.arraycopy(tmp, 0, buf, 0, tmp.length);
- }
- }
- encryptedBytes = new byte[pos];
- System.arraycopy(buf, 0, encryptedBytes, 0, pos);
- }
- return encryptedBytes;
- }
-
-
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
index c176575..cd1797a 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
@@ -22,34 +22,12 @@ package org.apache.qpid.server.security.encryption;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.AclEntry;
-import java.nio.file.attribute.AclEntryPermission;
-import java.nio.file.attribute.AclEntryType;
-import java.nio.file.attribute.AclFileAttributeView;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.PosixFileAttributeView;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.PosixFilePermissions;
-import java.nio.file.attribute.UserPrincipal;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashSet;
import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
-import java.util.Set;
-import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
@@ -63,54 +41,23 @@ import org.apache.qpid.server.plugin.ConfigurationSecretEncrypterFactory;
import org.apache.qpid.server.plugin.PluggableService;
@PluggableService
-public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterFactory, ConditionallyAvailable
+public class AESKeyFileEncrypterFactory extends AbstractAESKeyFileEncrypterFactory
+ implements ConfigurationSecretEncrypterFactory, ConditionallyAvailable
{
- private static final Logger LOGGER = LoggerFactory.getLogger(AESKeyFileEncrypterFactory.class);
-
- 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 final String TYPE = "AESKeyFile";
+ private static final Logger LOGGER = LoggerFactory.getLogger(AESKeyFileEncrypterFactory.class);
- static final String DEFAULT_KEYS_SUBDIR_NAME = ".keys";
-
- private static final boolean IS_AVAILABLE;
-
- private static final String ILLEGAL_ARG_EXCEPTION = "Unable to determine a mechanism to protect access to the key file on this filesystem";
-
- static
+ @Override
+ public String getType()
{
- boolean isAvailable;
- try
- {
- final int allowedKeyLength = Cipher.getMaxAllowedKeyLength(AES_ALGORITHM);
- isAvailable = allowedKeyLength >=AES_KEY_SIZE_BITS;
- if(!isAvailable)
- {
- LOGGER.warn("The {} configuration encryption encryption mechanism is not available. "
- + "Maximum available AES key length is {} but {} is required. "
- + "Ensure the full strength JCE policy has been installed into your JVM.", TYPE, allowedKeyLength, AES_KEY_SIZE_BITS);
- }
- }
- catch (NoSuchAlgorithmException e)
- {
- isAvailable = false;
-
- LOGGER.error("The " + TYPE + " configuration encryption encryption mechanism is not available. "
- + "The " + AES_ALGORITHM + " algorithm is not available within the JVM (despite it being a requirement).");
- }
-
- IS_AVAILABLE = isAvailable;
+ return TYPE;
}
@Override
public ConfigurationSecretEncrypter createEncrypter(final ConfiguredObject<?> object)
{
String fileLocation;
- if(object.getContextKeys(false).contains(ENCRYPTER_KEY_FILE))
+ if (object.getContextKeys(false).contains(ENCRYPTER_KEY_FILE))
{
fileLocation = object.getContextValue(String.class, ENCRYPTER_KEY_FILE);
}
@@ -129,35 +76,48 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
object.setAttributes(Collections.<String, Object>singletonMap(ConfiguredObject.CONTEXT, modifiedContext));
}
File file = new File(fileLocation);
- if(!file.exists())
+ if (!file.exists())
{
- LOGGER.warn("Configuration encryption is enabled, but no configuration secret was found. A new configuration secret will be created at '{}'.", fileLocation);
- createAndPopulateKeyFile(file);
+ LOGGER.warn(
+ "Configuration encryption is enabled, but no configuration secret was found. A new configuration secret will be created at '{}'.",
+ fileLocation);
+ try
+ {
+ createAndPopulateKeyFile(file);
+ }
+ catch (IllegalAccessException e)
+ {
+ e.printStackTrace();
+ }
}
- if(!file.isFile())
+ if (!file.isFile())
{
- throw new IllegalArgumentException("File '"+fileLocation+"' is not a regular file.");
+ throw new IllegalArgumentException("File '" + fileLocation + "' is not a regular file.");
}
try
{
checkFilePermissions(fileLocation, file);
- if(Files.size(file.toPath()) != AES_KEY_SIZE_BYTES)
+ if (Files.size(file.toPath()) != AES_KEY_SIZE_BYTES)
{
- throw new IllegalArgumentException("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))
+ try (FileInputStream inputStream = new FileInputStream(file))
{
byte[] key = new byte[AES_KEY_SIZE_BYTES];
int pos = 0;
int read;
- while(pos < key.length && -1 != ( read = inputStream.read(key, pos, key.length - pos)))
+ while (pos < key.length && -1 != (read = inputStream.read(key, pos, key.length - pos)))
{
pos += read;
}
- if(pos != key.length)
+ if (pos != key.length)
{
- throw new IllegalConfigurationException("Key file '" + fileLocation + "' contained an incorrect about of data");
+ throw new IllegalConfigurationException(String.format(
+ "Key file '%s' contained an incorrect about of data",
+ fileLocation));
}
SecretKeySpec keySpec = new SecretKeySpec(key, AES_ALGORITHM);
return new AESKeyFileEncrypter(keySpec);
@@ -168,217 +128,4 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
throw new IllegalConfigurationException("Unable to get file permissions: " + e.getMessage(), e);
}
}
-
- private void checkFilePermissions(String fileLocation, File file) throws IOException
- {
- if(isPosixFileSystem(file))
- {
- Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(file.toPath());
-
- if (permissions.contains(PosixFilePermission.GROUP_READ)
- || permissions.contains(PosixFilePermission.OTHERS_READ)
- || permissions.contains(PosixFilePermission.GROUP_WRITE)
- || permissions.contains(PosixFilePermission.OTHERS_WRITE)) {
- throw new IllegalArgumentException("Key file '"
- + fileLocation
- + "' has incorrect permissions. Only the owner "
- + "should be able to read or write this file.");
- }
- }
- else if(isAclFileSystem(file))
- {
- AclFileAttributeView attributeView = Files.getFileAttributeView(file.toPath(), AclFileAttributeView.class);
- ArrayList<AclEntry> acls = new ArrayList<>(attributeView.getAcl());
- ListIterator<AclEntry> iter = acls.listIterator();
- UserPrincipal owner = Files.getOwner(file.toPath());
- while(iter.hasNext())
- {
- AclEntry acl = iter.next();
- if(acl.type() == AclEntryType.ALLOW)
- {
- Set<AclEntryPermission> originalPermissions = acl.permissions();
- Set<AclEntryPermission> updatedPermissions = EnumSet.copyOf(originalPermissions);
-
-
- if (updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.APPEND_DATA,
- AclEntryPermission.EXECUTE,
- AclEntryPermission.WRITE_ACL,
- AclEntryPermission.WRITE_DATA,
- AclEntryPermission.WRITE_OWNER))) {
- throw new IllegalArgumentException("Key file '"
- + fileLocation
- + "' has incorrect permissions. The file should not be modifiable by any user.");
- }
- if (!owner.equals(acl.principal()) && updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.READ_DATA))) {
- throw new IllegalArgumentException("Key file '"
- + fileLocation
- + "' has incorrect permissions. Only the owner should be able to read from the file.");
- }
- }
- }
- }
- else
- {
- throw new IllegalArgumentException(ILLEGAL_ARG_EXCEPTION);
- }
- }
-
- private boolean isPosixFileSystem(File file) throws IOException
- {
- return Files.getFileAttributeView(file.toPath(), PosixFileAttributeView.class) != null;
- }
-
- private boolean isAclFileSystem(File file) throws IOException
- {
- return Files.getFileAttributeView(file.toPath(), AclFileAttributeView.class) != null;
- }
-
-
- private void createAndPopulateKeyFile(final File file)
- {
- try
- {
- createEmptyKeyFile(file);
-
- KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
- keyGenerator.init(AES_KEY_SIZE_BITS);
- SecretKey key = keyGenerator.generateKey();
- try(FileOutputStream os = new FileOutputStream(file))
- {
- os.write(key.getEncoded());
- }
-
- makeKeyFileReadOnly(file);
- }
- catch (NoSuchAlgorithmException | IOException e)
- {
- throw new IllegalArgumentException("Cannot create key file: " + e.getMessage(), e);
- }
-
- }
-
- private void makeKeyFileReadOnly(File file) throws IOException
- {
- if(isPosixFileSystem(file))
- {
- Files.setPosixFilePermissions(file.toPath(), EnumSet.of(PosixFilePermission.OWNER_READ));
- }
- else if(isAclFileSystem(file))
- {
- AclFileAttributeView attributeView = Files.getFileAttributeView(file.toPath(), AclFileAttributeView.class);
- ArrayList<AclEntry> acls = new ArrayList<>(attributeView.getAcl());
- ListIterator<AclEntry> iter = acls.listIterator();
- file.setReadOnly();
- while(iter.hasNext())
- {
- AclEntry acl = iter.next();
- Set<AclEntryPermission> originalPermissions = acl.permissions();
- Set<AclEntryPermission> updatedPermissions = EnumSet.copyOf(originalPermissions);
-
- if(updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.APPEND_DATA,
- AclEntryPermission.DELETE,
- AclEntryPermission.EXECUTE,
- AclEntryPermission.WRITE_ACL,
- AclEntryPermission.WRITE_DATA,
- AclEntryPermission.WRITE_ATTRIBUTES,
- AclEntryPermission.WRITE_NAMED_ATTRS,
- AclEntryPermission.WRITE_OWNER)))
- {
- AclEntry.Builder builder = AclEntry.newBuilder(acl);
- builder.setPermissions(updatedPermissions);
- iter.set(builder.build());
- }
- }
- attributeView.setAcl(acls);
- }
- else
- {
- throw new IllegalArgumentException(ILLEGAL_ARG_EXCEPTION);
- }
- }
-
- private void createEmptyKeyFile(File file) throws IOException
- {
- final Path parentFilePath = file.getAbsoluteFile().getParentFile().toPath();
-
- if(isPosixFileSystem(file)) {
- Set<PosixFilePermission> ownerOnly = EnumSet.of(PosixFilePermission.OWNER_READ,
- PosixFilePermission.OWNER_WRITE,
- PosixFilePermission.OWNER_EXECUTE);
- Files.createDirectories(parentFilePath, PosixFilePermissions.asFileAttribute(ownerOnly));
-
- Files.createFile(file.toPath(), PosixFilePermissions.asFileAttribute(
- EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE)));
- }
- else if(isAclFileSystem(file))
- {
- Files.createDirectories(parentFilePath);
- final UserPrincipal owner = Files.getOwner(parentFilePath);
- AclFileAttributeView attributeView = Files.getFileAttributeView(parentFilePath, AclFileAttributeView.class);
- List<AclEntry> acls = new ArrayList<>(attributeView.getAcl());
- ListIterator<AclEntry> iter = acls.listIterator();
- boolean found = false;
- while(iter.hasNext())
- {
- AclEntry acl = iter.next();
- if(!owner.equals(acl.principal()))
- {
- iter.remove();
- }
- else if(acl.type() == AclEntryType.ALLOW)
- {
- found = true;
- AclEntry.Builder builder = AclEntry.newBuilder(acl);
- Set<AclEntryPermission> permissions = acl.permissions().isEmpty() ? new HashSet<AclEntryPermission>() : EnumSet.copyOf(acl.permissions());
- permissions.addAll(Arrays.asList(AclEntryPermission.ADD_FILE, AclEntryPermission.ADD_SUBDIRECTORY, AclEntryPermission.LIST_DIRECTORY));
- builder.setPermissions(permissions);
- iter.set(builder.build());
- }
- }
- if(!found)
- {
- AclEntry.Builder builder = AclEntry.newBuilder();
- builder.setPermissions(AclEntryPermission.ADD_FILE, AclEntryPermission.ADD_SUBDIRECTORY, AclEntryPermission.LIST_DIRECTORY);
- builder.setType(AclEntryType.ALLOW);
- builder.setPrincipal(owner);
- acls.add(builder.build());
- }
- attributeView.setAcl(acls);
-
- Files.createFile(file.toPath(), new FileAttribute<List<AclEntry>>()
- {
- @Override
- public String name()
- {
- return "acl:acl";
- }
-
- @Override
- public List<AclEntry> value() {
- AclEntry.Builder builder = AclEntry.newBuilder();
- builder.setType(AclEntryType.ALLOW);
- builder.setPermissions(EnumSet.allOf(AclEntryPermission.class));
- builder.setPrincipal(owner);
- return Collections.singletonList(builder.build());
- }
- });
-
- }
- else
- {
- throw new IllegalArgumentException(ILLEGAL_ARG_EXCEPTION);
- }
- }
-
- @Override
- public String getType()
- {
- return TYPE;
- }
-
- @Override
- public boolean isAvailable()
- {
- return IS_AVAILABLE;
- }
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AbstractAESKeyFileEncrypterFactory.java
similarity index 51%
copy from broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
copy to broker-core/src/main/java/org/apache/qpid/server/security/encryption/AbstractAESKeyFileEncrypterFactory.java
index c176575..cec40f3 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactory.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/AbstractAESKeyFileEncrypterFactory.java
@@ -1,5 +1,4 @@
/*
- *
* 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
@@ -18,10 +17,10 @@
* under the License.
*
*/
+
package org.apache.qpid.server.security.encryption;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
@@ -41,45 +40,38 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
-import org.apache.qpid.server.model.ConfiguredObject;
-import org.apache.qpid.server.model.SystemConfig;
-import org.apache.qpid.server.plugin.ConditionallyAvailable;
-import org.apache.qpid.server.plugin.ConfigurationSecretEncrypterFactory;
-import org.apache.qpid.server.plugin.PluggableService;
-
-@PluggableService
-public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterFactory, ConditionallyAvailable
+
+public abstract class AbstractAESKeyFileEncrypterFactory
{
- private static final Logger LOGGER = LoggerFactory.getLogger(AESKeyFileEncrypterFactory.class);
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAESKeyFileEncrypterFactory.class);
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";
+ protected static final int AES_KEY_SIZE_BYTES = AES_KEY_SIZE_BITS / 8;
+ protected static final String AES_ALGORITHM = "AES";
- public static final String TYPE = "AESKeyFile";
+ public static final String TYPE = "AESGCMKeyFile";
static final String DEFAULT_KEYS_SUBDIR_NAME = ".keys";
private static final boolean IS_AVAILABLE;
- private static final String ILLEGAL_ARG_EXCEPTION = "Unable to determine a mechanism to protect access to the key file on this filesystem";
+ private static final String GENERAL_EXCEPTION_MESSAGE =
+ "Unable to determine a mechanism to protect access to the key file on this filesystem";
static
{
@@ -87,139 +79,74 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
try
{
final int allowedKeyLength = Cipher.getMaxAllowedKeyLength(AES_ALGORITHM);
- isAvailable = allowedKeyLength >=AES_KEY_SIZE_BITS;
- if(!isAvailable)
+ isAvailable = allowedKeyLength >= AES_KEY_SIZE_BITS;
+ if (!isAvailable)
{
LOGGER.warn("The {} configuration encryption encryption mechanism is not available. "
+ "Maximum available AES key length is {} but {} is required. "
- + "Ensure the full strength JCE policy has been installed into your JVM.", TYPE, allowedKeyLength, AES_KEY_SIZE_BITS);
+ + "Ensure the full strength JCE policy has been installed into your JVM.",
+ TYPE,
+ allowedKeyLength,
+ AES_KEY_SIZE_BITS);
}
}
catch (NoSuchAlgorithmException e)
{
isAvailable = false;
- LOGGER.error("The " + TYPE + " configuration encryption encryption mechanism is not available. "
- + "The " + AES_ALGORITHM + " algorithm is not available within the JVM (despite it being a requirement).");
+ LOGGER.error("The "
+ + TYPE
+ + " configuration encryption encryption mechanism is not available. "
+ + "The "
+ + AES_ALGORITHM
+ + " algorithm is not available within the JVM (despite it being a requirement).");
}
IS_AVAILABLE = isAvailable;
}
- @Override
- public ConfigurationSecretEncrypter createEncrypter(final ConfiguredObject<?> object)
- {
- String fileLocation;
- if(object.getContextKeys(false).contains(ENCRYPTER_KEY_FILE))
- {
- fileLocation = object.getContextValue(String.class, ENCRYPTER_KEY_FILE);
- }
- else
- {
-
- fileLocation = object.getContextValue(String.class, SystemConfig.QPID_WORK_DIR)
- + File.separator + DEFAULT_KEYS_SUBDIR_NAME + File.separator
- + object.getCategoryClass().getSimpleName() + "_"
- + object.getName() + ".key";
-
- Map<String, String> context = object.getContext();
- Map<String, String> modifiedContext = new LinkedHashMap<>(context);
- modifiedContext.put(ENCRYPTER_KEY_FILE, fileLocation);
-
- object.setAttributes(Collections.<String, Object>singletonMap(ConfiguredObject.CONTEXT, modifiedContext));
- }
- File file = new File(fileLocation);
- if(!file.exists())
- {
- LOGGER.warn("Configuration encryption is enabled, but no configuration secret was found. A new configuration secret will be created at '{}'.", fileLocation);
- createAndPopulateKeyFile(file);
- }
- if(!file.isFile())
- {
- throw new IllegalArgumentException("File '"+fileLocation+"' is not a regular file.");
- }
- try
- {
- checkFilePermissions(fileLocation, file);
- if(Files.size(file.toPath()) != AES_KEY_SIZE_BYTES)
- {
- throw new IllegalArgumentException("Key file '" + fileLocation + "' contains an incorrect about of data");
- }
-
- try(FileInputStream inputStream = new FileInputStream(file))
- {
- byte[] key = new byte[AES_KEY_SIZE_BYTES];
- int pos = 0;
- int read;
- while(pos < key.length && -1 != ( read = inputStream.read(key, pos, key.length - pos)))
- {
- pos += read;
- }
- if(pos != key.length)
- {
- throw new IllegalConfigurationException("Key file '" + fileLocation + "' contained an incorrect about of data");
- }
- SecretKeySpec keySpec = new SecretKeySpec(key, AES_ALGORITHM);
- return new AESKeyFileEncrypter(keySpec);
- }
- }
- catch (IOException e)
- {
- throw new IllegalConfigurationException("Unable to get file permissions: " + e.getMessage(), e);
- }
- }
-
- private void checkFilePermissions(String fileLocation, File file) throws IOException
+ protected void checkFilePermissions(String fileLocation, File file) throws IOException
{
- if(isPosixFileSystem(file))
+ if (isPosixFileSystem(file))
{
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(file.toPath());
- if (permissions.contains(PosixFilePermission.GROUP_READ)
- || permissions.contains(PosixFilePermission.OTHERS_READ)
- || permissions.contains(PosixFilePermission.GROUP_WRITE)
- || permissions.contains(PosixFilePermission.OTHERS_WRITE)) {
+ if (permissions.contains(PosixFilePermission.OTHERS_READ)
+ || permissions.contains(PosixFilePermission.OTHERS_WRITE))
+ {
throw new IllegalArgumentException("Key file '"
- + fileLocation
- + "' has incorrect permissions. Only the owner "
- + "should be able to read or write this file.");
+ + fileLocation
+ + "' has incorrect permissions. Only the owner "
+ + "should be able to read or write this file.");
}
}
- else if(isAclFileSystem(file))
+ else if (isAclFileSystem(file))
{
AclFileAttributeView attributeView = Files.getFileAttributeView(file.toPath(), AclFileAttributeView.class);
ArrayList<AclEntry> acls = new ArrayList<>(attributeView.getAcl());
ListIterator<AclEntry> iter = acls.listIterator();
UserPrincipal owner = Files.getOwner(file.toPath());
- while(iter.hasNext())
+ while (iter.hasNext())
{
AclEntry acl = iter.next();
- if(acl.type() == AclEntryType.ALLOW)
+ if (acl.type() == AclEntryType.ALLOW)
{
Set<AclEntryPermission> originalPermissions = acl.permissions();
Set<AclEntryPermission> updatedPermissions = EnumSet.copyOf(originalPermissions);
-
- if (updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.APPEND_DATA,
- AclEntryPermission.EXECUTE,
- AclEntryPermission.WRITE_ACL,
- AclEntryPermission.WRITE_DATA,
- AclEntryPermission.WRITE_OWNER))) {
- throw new IllegalArgumentException("Key file '"
- + fileLocation
- + "' has incorrect permissions. The file should not be modifiable by any user.");
- }
- if (!owner.equals(acl.principal()) && updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.READ_DATA))) {
- throw new IllegalArgumentException("Key file '"
- + fileLocation
- + "' has incorrect permissions. Only the owner should be able to read from the file.");
+ if (!owner.equals(acl.principal())
+ && updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.READ_DATA)))
+ {
+ throw new IllegalArgumentException(String.format(
+ "Key file '%s' has incorrect permissions. Only the owner should be able to read from the file.",
+ fileLocation));
}
}
}
}
else
{
- throw new IllegalArgumentException(ILLEGAL_ARG_EXCEPTION);
+ throw new IllegalArgumentException(GENERAL_EXCEPTION_MESSAGE);
}
}
@@ -234,7 +161,7 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
}
- private void createAndPopulateKeyFile(final File file)
+ protected void createAndPopulateKeyFile(final File file) throws IllegalAccessException
{
try
{
@@ -243,7 +170,7 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
keyGenerator.init(AES_KEY_SIZE_BITS);
SecretKey key = keyGenerator.generateKey();
- try(FileOutputStream os = new FileOutputStream(file))
+ try (FileOutputStream os = new FileOutputStream(file))
{
os.write(key.getEncoded());
}
@@ -252,37 +179,40 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
}
catch (NoSuchAlgorithmException | IOException e)
{
- throw new IllegalArgumentException("Cannot create key file: " + e.getMessage(), e);
+ throw new IllegalConfigurationException("Cannot create key file: " + e.getMessage(), e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new IllegalAccessException("Cannot access key file: " + e);
}
-
}
- private void makeKeyFileReadOnly(File file) throws IOException
+ private void makeKeyFileReadOnly(File file) throws IOException, IllegalAccessException
{
- if(isPosixFileSystem(file))
+ if (isPosixFileSystem(file))
{
Files.setPosixFilePermissions(file.toPath(), EnumSet.of(PosixFilePermission.OWNER_READ));
}
- else if(isAclFileSystem(file))
+ else if (isAclFileSystem(file))
{
AclFileAttributeView attributeView = Files.getFileAttributeView(file.toPath(), AclFileAttributeView.class);
ArrayList<AclEntry> acls = new ArrayList<>(attributeView.getAcl());
ListIterator<AclEntry> iter = acls.listIterator();
file.setReadOnly();
- while(iter.hasNext())
+ while (iter.hasNext())
{
AclEntry acl = iter.next();
Set<AclEntryPermission> originalPermissions = acl.permissions();
Set<AclEntryPermission> updatedPermissions = EnumSet.copyOf(originalPermissions);
- if(updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.APPEND_DATA,
- AclEntryPermission.DELETE,
- AclEntryPermission.EXECUTE,
- AclEntryPermission.WRITE_ACL,
- AclEntryPermission.WRITE_DATA,
- AclEntryPermission.WRITE_ATTRIBUTES,
- AclEntryPermission.WRITE_NAMED_ATTRS,
- AclEntryPermission.WRITE_OWNER)))
+ if (updatedPermissions.removeAll(EnumSet.of(AclEntryPermission.APPEND_DATA,
+ AclEntryPermission.DELETE,
+ AclEntryPermission.EXECUTE,
+ AclEntryPermission.WRITE_ACL,
+ AclEntryPermission.WRITE_DATA,
+ AclEntryPermission.WRITE_ATTRIBUTES,
+ AclEntryPermission.WRITE_NAMED_ATTRS,
+ AclEntryPermission.WRITE_OWNER)))
{
AclEntry.Builder builder = AclEntry.newBuilder(acl);
builder.setPermissions(updatedPermissions);
@@ -293,7 +223,7 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
}
else
{
- throw new IllegalArgumentException(ILLEGAL_ARG_EXCEPTION);
+ throw new IllegalAccessException(GENERAL_EXCEPTION_MESSAGE);
}
}
@@ -301,16 +231,17 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
{
final Path parentFilePath = file.getAbsoluteFile().getParentFile().toPath();
- if(isPosixFileSystem(file)) {
+ if (isPosixFileSystem(file))
+ {
Set<PosixFilePermission> ownerOnly = EnumSet.of(PosixFilePermission.OWNER_READ,
- PosixFilePermission.OWNER_WRITE,
- PosixFilePermission.OWNER_EXECUTE);
+ PosixFilePermission.OWNER_WRITE,
+ PosixFilePermission.OWNER_EXECUTE);
Files.createDirectories(parentFilePath, PosixFilePermissions.asFileAttribute(ownerOnly));
Files.createFile(file.toPath(), PosixFilePermissions.asFileAttribute(
EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE)));
}
- else if(isAclFileSystem(file))
+ else if (isAclFileSystem(file))
{
Files.createDirectories(parentFilePath);
final UserPrincipal owner = Files.getOwner(parentFilePath);
@@ -318,27 +249,33 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
List<AclEntry> acls = new ArrayList<>(attributeView.getAcl());
ListIterator<AclEntry> iter = acls.listIterator();
boolean found = false;
- while(iter.hasNext())
+ while (iter.hasNext())
{
AclEntry acl = iter.next();
- if(!owner.equals(acl.principal()))
+ if (!owner.equals(acl.principal()))
{
iter.remove();
}
- else if(acl.type() == AclEntryType.ALLOW)
+ else if (acl.type() == AclEntryType.ALLOW)
{
found = true;
AclEntry.Builder builder = AclEntry.newBuilder(acl);
- Set<AclEntryPermission> permissions = acl.permissions().isEmpty() ? new HashSet<AclEntryPermission>() : EnumSet.copyOf(acl.permissions());
- permissions.addAll(Arrays.asList(AclEntryPermission.ADD_FILE, AclEntryPermission.ADD_SUBDIRECTORY, AclEntryPermission.LIST_DIRECTORY));
+ Set<AclEntryPermission> permissions = acl.permissions().isEmpty()
+ ? new HashSet<AclEntryPermission>()
+ : EnumSet.copyOf(acl.permissions());
+ permissions.addAll(Arrays.asList(AclEntryPermission.ADD_FILE,
+ AclEntryPermission.ADD_SUBDIRECTORY,
+ AclEntryPermission.LIST_DIRECTORY));
builder.setPermissions(permissions);
iter.set(builder.build());
}
}
- if(!found)
+ if (!found)
{
AclEntry.Builder builder = AclEntry.newBuilder();
- builder.setPermissions(AclEntryPermission.ADD_FILE, AclEntryPermission.ADD_SUBDIRECTORY, AclEntryPermission.LIST_DIRECTORY);
+ builder.setPermissions(AclEntryPermission.ADD_FILE,
+ AclEntryPermission.ADD_SUBDIRECTORY,
+ AclEntryPermission.LIST_DIRECTORY);
builder.setType(AclEntryType.ALLOW);
builder.setPrincipal(owner);
acls.add(builder.build());
@@ -354,7 +291,8 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
}
@Override
- public List<AclEntry> value() {
+ public List<AclEntry> value()
+ {
AclEntry.Builder builder = AclEntry.newBuilder();
builder.setType(AclEntryType.ALLOW);
builder.setPermissions(EnumSet.allOf(AclEntryPermission.class));
@@ -362,23 +300,17 @@ public class AESKeyFileEncrypterFactory implements ConfigurationSecretEncrypterF
return Collections.singletonList(builder.build());
}
});
-
}
else
{
- throw new IllegalArgumentException(ILLEGAL_ARG_EXCEPTION);
+ throw new IllegalArgumentException(GENERAL_EXCEPTION_MESSAGE);
}
}
- @Override
- public String getType()
- {
- return TYPE;
- }
-
- @Override
public boolean isAvailable()
{
return IS_AVAILABLE;
}
+
+ public abstract String getType();
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/security/encryption/EncryptionHelper.java b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/EncryptionHelper.java
new file mode 100644
index 0000000..1f95442
--- /dev/null
+++ b/broker-core/src/main/java/org/apache/qpid/server/security/encryption/EncryptionHelper.java
@@ -0,0 +1,72 @@
+/*
+ * 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.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+
+public class EncryptionHelper
+{
+
+ public static boolean isValidBase64(final String encrypted)
+ {
+ return encrypted.matches("^([\\w\\d+/]{4})*([\\w\\d+/]{2}==|[\\w\\d+/]{3}=)?$");
+ }
+
+
+ public static byte[] readFromCipherStream(final byte[] unencryptedBytes, final Cipher cipher) throws IOException
+ {
+ return readFromCipherStream(unencryptedBytes, 0, unencryptedBytes.length, cipher);
+ }
+
+ public static byte[] readFromCipherStream(final byte[] unencryptedBytes,
+ int offset,
+ int length,
+ final Cipher cipher)
+ throws IOException
+ {
+ final byte[] encryptedBytes;
+ try (CipherInputStream cipherInputStream = new CipherInputStream(new ByteArrayInputStream(unencryptedBytes,
+ offset,
+ length), cipher))
+ {
+ 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)
+ {
+ byte[] tmp = buf;
+ buf = new byte[buf.length + 512];
+ System.arraycopy(tmp, 0, buf, 0, tmp.length);
+ }
+ }
+ encryptedBytes = new byte[pos];
+ System.arraycopy(buf, 0, encryptedBytes, 0, pos);
+ }
+ return encryptedBytes;
+ }
+}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterFactoryTest.java
similarity index 95%
copy from broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java
copy to broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterFactoryTest.java
index 9b27904..b30e0c6 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterFactoryTest.java
@@ -1,5 +1,4 @@
/*
- *
* 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
@@ -60,11 +59,11 @@ import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.SystemConfig;
import org.apache.qpid.test.utils.UnitTestBase;
-public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
+public class AESGCMKeyFileEncrypterFactoryTest extends UnitTestBase
{
private Broker _broker;
private Path _tmpDir;
- private AESKeyFileEncrypterFactory _factory;
+ private AESGCMKeyFileEncrypterFactory _factory;
@Before
public void setUp() throws Exception
@@ -93,13 +92,13 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
}
}).when(_broker).setAttributes(attributesCaptor.capture());
- _factory = new AESKeyFileEncrypterFactory();
+ _factory = new AESGCMKeyFileEncrypterFactory();
}
@Test
public void testCreateKeyInDefaultLocation() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
ConfigurationSecretEncrypter encrypter = _factory.createEncrypter(_broker);
@@ -132,7 +131,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testSettingContextKeyLeadsToFileCreation() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
String filename = UUID.randomUUID().toString() + ".key";
String subdirName = getTestName() + File.separator + "test";
@@ -154,7 +153,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testUnableToCreateFileInSpecifiedLocation() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
String filename = UUID.randomUUID().toString() + ".key";
@@ -183,7 +182,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testPermissionsAreChecked() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
String filename = UUID.randomUUID().toString() + ".key";
@@ -216,7 +215,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testInvalidKey() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
String filename = UUID.randomUUID().toString() + ".key";
String subdirName = getTestName() + File.separator + "test";
@@ -279,7 +278,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
private boolean isStrongEncryptionEnabled() throws NoSuchAlgorithmException
{
- return Cipher.getMaxAllowedKeyLength("AES")>=256;
+ return Cipher.getMaxAllowedKeyLength("AES") >= 256;
}
private class KeyFilePathChecker extends SimpleFileVisitor<Path>
@@ -304,7 +303,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException
{
- if(!_inKeysSubdir && dir.endsWith(_subdirName))
+ if (!_inKeysSubdir && dir.endsWith(_subdirName))
{
_inKeysSubdir = true;
assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.OTHERS_READ));
@@ -320,15 +319,14 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
{
return _inKeysSubdir ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE;
}
-
}
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException
{
- if(_inKeysSubdir)
+ if (_inKeysSubdir)
{
- if(file.endsWith(_fileName))
+ if (file.endsWith(_fileName))
{
_keyFile = file;
@@ -357,6 +355,5 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
{
return _keyFile;
}
-
}
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterTest.java
new file mode 100644
index 0000000..15bf947
--- /dev/null
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESGCMKeyFileEncrypterTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.util.concurrent.SettableFuture;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.SystemLauncher;
+import org.apache.qpid.server.SystemLauncherListener;
+import org.apache.qpid.server.model.AuthenticationProvider;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.BrokerModel;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.JsonSystemConfigImpl;
+import org.apache.qpid.server.model.SystemConfig;
+import org.apache.qpid.server.model.User;
+import org.apache.qpid.server.util.FileUtils;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class AESGCMKeyFileEncrypterTest extends UnitTestBase
+{
+ private static final String PLAIN_SECRET = "secret";
+ public static final int TIMEOUT = 10;
+ private final SecureRandom _random = new SecureRandom();
+ private static Path _configurationLocation;
+ private static Path _workDir;
+ private Broker<?> _broker;
+ private static SystemLauncher _systemLauncher;
+ private static SecretKeySpec secretKey;
+
+ @Before
+ public void setUp()
+ {
+ final byte[] keyData = new byte[32];
+ _random.nextBytes(keyData);
+ secretKey = new SecretKeySpec(keyData, "AES");
+ }
+
+ private void createBrokerWithEncrypter(final Object encrptionType) throws Exception
+ {
+ _workDir = Files.createTempDirectory("qpid_work_dir");
+ _configurationLocation = Files.createTempFile(_workDir, "config", ".json");
+ Map<String, Object> config = new HashMap<>();
+ config.put(ConfiguredObject.NAME, getTestName());
+ config.put(Broker.MODEL_VERSION, BrokerModel.MODEL_VERSION);
+ config.put(Broker.CONFIDENTIAL_CONFIGURATION_ENCRYPTION_PROVIDER, encrptionType);
+ new ObjectMapper().writeValue(_configurationLocation.toFile(), config);
+
+ Map<String, Object> attributes = new HashMap<>();
+ attributes.put("storePath", _configurationLocation.toFile().getAbsolutePath());
+ attributes.put("preferenceStoreAttributes", "{\"type\": \"Noop\"}");
+ attributes.put(SystemConfig.TYPE, JsonSystemConfigImpl.SYSTEM_CONFIG_TYPE);
+ attributes.put(SystemConfig.STARTUP_LOGGED_TO_SYSTEM_OUT, Boolean.FALSE);
+ attributes.put(SystemConfig.CONTEXT,
+ Collections.singletonMap("qpid.work_dir", _workDir.toFile().getAbsolutePath()));
+ final SettableFuture<SystemConfig<?>> configFuture = SettableFuture.create();
+ _systemLauncher = new SystemLauncher(new SystemLauncherListener.DefaultSystemLauncherListener()
+ {
+ @Override
+ public void onContainerResolve(final SystemConfig<?> systemConfig)
+ {
+ configFuture.set(systemConfig);
+ }
+ });
+ _systemLauncher.startup(attributes);
+ final SystemConfig<?> systemConfig = configFuture.get(TIMEOUT, TimeUnit.SECONDS);
+ _broker = (Broker<?>) systemConfig.getContainer();
+
+ final Map<String, Object> authProviderAttributes = new HashMap<>();
+ authProviderAttributes.put(ConfiguredObject.NAME, "testAuthProvider");
+ authProviderAttributes.put(ConfiguredObject.TYPE, "Plain");
+
+ final AuthenticationProvider
+ authProvider = _broker.createChild(AuthenticationProvider.class, authProviderAttributes);
+
+ Map<String, Object> userAttrs = new HashMap<>();
+ userAttrs.put(User.TYPE, "managed");
+ userAttrs.put(User.NAME, "guest");
+ userAttrs.put(User.PASSWORD, PLAIN_SECRET);
+ User user = (User) authProvider.createChild(User.class, userAttrs);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception
+ {
+ _systemLauncher.shutdown();
+ FileUtils.deleteDirectory(_workDir.toFile().getAbsolutePath());
+ FileUtils.deleteDirectory(_configurationLocation.toFile().getAbsolutePath());
+ }
+
+ @Test
+ public void testRepeatedEncryptionsReturnDifferentValues() throws Exception
+ {
+ AESGCMKeyFileEncrypter encrypter = new AESGCMKeyFileEncrypter(secretKey);
+
+ Set<String> encryptions = new HashSet<>();
+
+ int iterations = 10;
+
+ for (int i = 0; i < iterations; i++)
+ {
+ encryptions.add(encrypter.encrypt(PLAIN_SECRET));
+ }
+
+ assertEquals("Not all encryptions were distinct", (long) iterations, (long) encryptions.size());
+
+ for (String encrypted : encryptions)
+ {
+ assertEquals("Not all encryptions decrypt correctly", PLAIN_SECRET, encrypter.decrypt(encrypted));
+ }
+ }
+
+ @Test
+ public void testCreationFailsOnInvalidSecret() throws Exception
+ {
+ try
+ {
+ new AESGCMKeyFileEncrypter(null);
+ fail("An encrypter should not be creatable from a null key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertTrue("Unexpected exception message:" + e.getMessage(),
+ e.getMessage().contains("A non null secret key must be supplied"));
+ }
+
+ try
+ {
+ PBEKeySpec keySpec = new PBEKeySpec(PLAIN_SECRET.toCharArray());
+ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
+ new AESGCMKeyFileEncrypter(factory.generateSecret(keySpec));
+ fail("An encrypter should not be creatable from the wrong type of secret key");
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertTrue("Unexpected exception message:" + e.getMessage(),
+ e.getMessage()
+ .contains("Provided secret key was for the algorithm: PBEWithMD5AndDES when AES was needed."));
+ }
+ }
+
+ @Test
+ public void testEncryptionOfEmptyString() throws Exception
+ {
+ doTestSimpleEncryptDecrypt("");
+ }
+
+ private void doTestSimpleEncryptDecrypt(final String text)
+ {
+ AESGCMKeyFileEncrypter encrypter = new AESGCMKeyFileEncrypter(secretKey);
+
+ String encrypted = encrypter.encrypt(text);
+ assertNotNull("Encrypter did not return a result from encryption", encrypted);
+ assertNotEquals("Plain text and encrypted version are equal", text, encrypted);
+ String decrypted = encrypter.decrypt(encrypted);
+ assertNotNull("Encrypter did not return a result from decryption", decrypted);
+ assertEquals("Encryption was not reversible", text, decrypted);
+ }
+
+ @Test
+ public void testEncryptingNullFails() throws Exception
+ {
+ try
+ {
+ AESGCMKeyFileEncrypter encrypter = new AESGCMKeyFileEncrypter(secretKey);
+
+ String encrypted = encrypter.encrypt(null);
+ fail("Attempting to encrypt null should fail");
+ }
+ catch (NullPointerException e)
+ {
+ //pass
+ }
+ }
+
+ @Test
+ public void testEncryptingVeryLargeSecret() throws Exception
+ {
+ 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));
+ }
+
+ @Test
+ public void testDecryptNonsense() throws Exception
+ {
+ AESGCMKeyFileEncrypter encrypter = new AESGCMKeyFileEncrypter(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
+ }
+ }
+
+ @Test
+ public void testChangeOfEncryptiontoGCM() throws Exception
+ {
+ createBrokerWithEncrypter(AESKeyFileEncrypterFactory.TYPE);
+ String aesEncryptedPassword = getEncryptedPasswordFromConfig();
+ byte[] keyBytes = getSecretKey();
+ final SecretKeySpec aesSecretKey = new SecretKeySpec(keyBytes, "AES");
+ AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(aesSecretKey);
+ String aesDecryptedPassword = encrypter.decrypt(aesEncryptedPassword);
+ assertEquals("Decrpted text doesnt match original", PLAIN_SECRET, aesDecryptedPassword);
+ _broker.setAttributes(Collections.singletonMap(Broker.CONFIDENTIAL_CONFIGURATION_ENCRYPTION_PROVIDER,
+ AESGCMKeyFileEncrypterFactory.TYPE));
+ String gcmencryptedPassword = getEncryptedPasswordFromConfig();
+ AESGCMKeyFileEncrypter gcmEncrypter = new AESGCMKeyFileEncrypter(aesSecretKey);
+ String gcmDecryptedPassword = gcmEncrypter.decrypt(gcmencryptedPassword);
+ assertEquals("Decrpted text doesnt match original", PLAIN_SECRET, gcmDecryptedPassword);
+ }
+
+ @Test
+ public void testChangeOfEncryptiontoAES() throws Exception
+ {
+ createBrokerWithEncrypter(AESGCMKeyFileEncrypterFactory.TYPE);
+ String gcmEncryptedPassword = getEncryptedPasswordFromConfig();
+ byte[] keyBytes = getSecretKey();
+ final SecretKeySpec aesSecretKey = new SecretKeySpec(keyBytes, "AES");
+ AESGCMKeyFileEncrypter gcmEncrypter = new AESGCMKeyFileEncrypter(aesSecretKey);
+ String gcmDecryptedPassword = gcmEncrypter.decrypt(gcmEncryptedPassword);
+ assertEquals("Decrpted text doesnt match original", PLAIN_SECRET, gcmDecryptedPassword);
+ _broker.setAttributes(Collections.singletonMap(Broker.CONFIDENTIAL_CONFIGURATION_ENCRYPTION_PROVIDER,
+ "AESKeyFile"));
+ String aesencryptedPassword = getEncryptedPasswordFromConfig();
+ AESKeyFileEncrypter aesEncrypter = new AESKeyFileEncrypter(aesSecretKey);
+ String aesDecryptedPassword = aesEncrypter.decrypt(aesencryptedPassword);
+ assertEquals("Decrpted text doesnt match original", PLAIN_SECRET, aesDecryptedPassword);
+ }
+
+ private byte[] getSecretKey() throws IOException
+ {
+ byte[] keyBytes = new byte[0];
+ boolean isSecretFound = false;
+ File[] workDir = _workDir.toFile().listFiles();
+
+ for (File file : workDir)
+ {
+ if (file.isDirectory())
+ {
+ File[] keysDir = file.listFiles();
+ for (File keysFile : keysDir)
+ {
+ File secretKeyFile = keysFile;
+ keyBytes = Files.readAllBytes(Paths.get(String.valueOf(secretKeyFile)));
+ isSecretFound = true;
+ break;
+ }
+ if (isSecretFound)
+ {
+ break;
+ }
+ }
+ }
+ return keyBytes;
+ }
+
+ private String getEncryptedPasswordFromConfig() throws java.io.IOException
+ {
+ ObjectMapper objectMapper = new ObjectMapper();
+ File configFile = _configurationLocation.toFile();
+ TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>()
+ {
+ };
+ HashMap<String, Object> configEntry = objectMapper.readValue(configFile, typeRef);
+ ArrayList configAuthProvider = (ArrayList) configEntry.get("authenticationproviders");
+ HashMap<String, Object> users = (HashMap<String, Object>) configAuthProvider.get(0);
+ ArrayList configUsers = (ArrayList) users.get("users");
+ HashMap<String, Object> configUser = (HashMap<String, Object>) configUsers.get(0);
+ return (String) configUser.get("password");
+ }
+}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java
index 9b27904..c7bbbd8 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterFactoryTest.java
@@ -99,7 +99,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testCreateKeyInDefaultLocation() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
ConfigurationSecretEncrypter encrypter = _factory.createEncrypter(_broker);
@@ -132,7 +132,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testSettingContextKeyLeadsToFileCreation() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
String filename = UUID.randomUUID().toString() + ".key";
String subdirName = getTestName() + File.separator + "test";
@@ -154,7 +154,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testUnableToCreateFileInSpecifiedLocation() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
String filename = UUID.randomUUID().toString() + ".key";
@@ -183,7 +183,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testPermissionsAreChecked() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
String filename = UUID.randomUUID().toString() + ".key";
@@ -216,7 +216,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Test
public void testInvalidKey() throws Exception
{
- if(isStrongEncryptionEnabled() && supportsPosixFileAttributes())
+ if (isStrongEncryptionEnabled() && supportsPosixFileAttributes())
{
String filename = UUID.randomUUID().toString() + ".key";
String subdirName = getTestName() + File.separator + "test";
@@ -279,7 +279,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
private boolean isStrongEncryptionEnabled() throws NoSuchAlgorithmException
{
- return Cipher.getMaxAllowedKeyLength("AES")>=256;
+ return Cipher.getMaxAllowedKeyLength("AES") >= 256;
}
private class KeyFilePathChecker extends SimpleFileVisitor<Path>
@@ -304,7 +304,7 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
@Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException
{
- if(!_inKeysSubdir && dir.endsWith(_subdirName))
+ if (!_inKeysSubdir && dir.endsWith(_subdirName))
{
_inKeysSubdir = true;
assertFalse(Files.getPosixFilePermissions(dir).contains(PosixFilePermission.OTHERS_READ));
@@ -320,15 +320,14 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
{
return _inKeysSubdir ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE;
}
-
}
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException
{
- if(_inKeysSubdir)
+ if (_inKeysSubdir)
{
- if(file.endsWith(_fileName))
+ if (file.endsWith(_fileName))
{
_keyFile = file;
@@ -357,6 +356,5 @@ public class AESKeyFileEncrypterFactoryTest extends UnitTestBase
{
return _keyFile;
}
-
}
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java
index 371b683..8f0f8a8 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/encryption/AESKeyFileEncrypterTest.java
@@ -38,6 +38,7 @@ import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
+import org.junit.Before;
import org.junit.Test;
import org.apache.qpid.test.utils.UnitTestBase;
@@ -45,12 +46,21 @@ import org.apache.qpid.test.utils.UnitTestBase;
public class AESKeyFileEncrypterTest extends UnitTestBase
{
private final SecureRandom _random = new SecureRandom();
- public static final String PLAINTEXT = "notaverygoodpassword";
+ public static final String PLAINTEXT = "secret";
+ private static SecretKeySpec secretKey;
+
+ @Before
+ public void setUp()
+ {
+ final byte[] keyData = new byte[32];
+ _random.nextBytes(keyData);
+ secretKey = new SecretKeySpec(keyData, "AES");
+ }
@Test
public void testSimpleEncryptDecrypt() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
doTestSimpleEncryptDecrypt(PLAINTEXT);
}
@@ -60,14 +70,13 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
@Test
public void testRepeatedEncryptionsReturnDifferentValues() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
- SecretKeySpec secretKey = createSecretKey();
AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
Set<String> encryptions = new HashSet<>();
- int iterations = 100;
+ int iterations = 10;
for (int i = 0; i < iterations; i++)
{
@@ -86,7 +95,7 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
@Test
public void testCreationFailsOnInvalidSecret() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
try
{
@@ -115,7 +124,7 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
@Test
public void testEncryptionOfEmptyString() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
String text = "";
doTestSimpleEncryptDecrypt(text);
@@ -124,7 +133,6 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
private void doTestSimpleEncryptDecrypt(final String text)
{
- SecretKeySpec secretKey = createSecretKey();
AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
String encrypted = encrypter.encrypt(text);
@@ -138,13 +146,12 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
@Test
public void testEncryptingNullFails() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
+ AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
+
try
{
- SecretKeySpec secretKey = createSecretKey();
- AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
-
String encrypted = encrypter.encrypt(null);
fail("Attempting to encrypt null should fail");
}
@@ -158,7 +165,7 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
@Test
public void testEncryptingVeryLargeSecret() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
Random random = new Random();
byte[] data = new byte[4096];
@@ -173,18 +180,15 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
private boolean isStrongEncryptionEnabled() throws NoSuchAlgorithmException
{
- return Cipher.getMaxAllowedKeyLength("AES")>=256;
+ return Cipher.getMaxAllowedKeyLength("AES") >= 256;
}
@Test
public void testDecryptNonsense() throws Exception
{
- if(isStrongEncryptionEnabled())
+ if (isStrongEncryptionEnabled())
{
- SecretKeySpec secretKey = createSecretKey();
AESKeyFileEncrypter encrypter = new AESKeyFileEncrypter(secretKey);
-
-
try
{
encrypter.decrypt(null);
@@ -226,11 +230,4 @@ public class AESKeyFileEncrypterTest extends UnitTestBase
}
}
}
-
- private SecretKeySpec createSecretKey()
- {
- final byte[] keyData = new byte[32];
- _random.nextBytes(keyData);
- return new SecretKeySpec(keyData, "AES");
- }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org