-
Notifications
You must be signed in to change notification settings - Fork 42
Enabling using secret values during a deployment #1755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| package org.cloudfoundry.multiapps.controller.core.security.encryption; | ||
|
|
||
| import java.nio.charset.StandardCharsets; | ||
| import java.security.InvalidAlgorithmParameterException; | ||
| import java.security.InvalidKeyException; | ||
| import java.security.NoSuchAlgorithmException; | ||
| import java.security.NoSuchProviderException; | ||
| import java.security.SecureRandom; | ||
| import java.text.MessageFormat; | ||
| import javax.crypto.Cipher; | ||
| import javax.crypto.NoSuchPaddingException; | ||
| import javax.crypto.spec.GCMParameterSpec; | ||
| import javax.crypto.spec.SecretKeySpec; | ||
|
|
||
| import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; | ||
| import org.cloudfoundry.multiapps.common.SLException; | ||
| import org.cloudfoundry.multiapps.controller.core.Constants; | ||
| import org.cloudfoundry.multiapps.controller.core.Messages; | ||
|
|
||
| public class AesEncryptionUtil { | ||
|
|
||
| public static byte[] encrypt(String plainText, byte[] encryptionKey) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor spelling issue: the variable name uses “Cypher” instead of “Cipher”. Consider updating the variable names to use “Cipher” for clarity and consistency.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for pointing out! |
||
| try { | ||
| if (plainText == null) { | ||
| plainText = ""; | ||
| } | ||
| byte[] gcmInitialisationVector = new byte[Constants.INITIALISATION_VECTOR_LENGTH]; | ||
| new SecureRandom().nextBytes(gcmInitialisationVector); | ||
|
|
||
| Cipher cipherObject = setUpCipherObject(encryptionKey, gcmInitialisationVector, true); | ||
|
|
||
| byte[] cipherValue = cipherObject.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); | ||
|
|
||
| byte[] combinedCipherValueAndInitialisationVector = new byte[gcmInitialisationVector.length + cipherValue.length]; | ||
|
|
||
| System.arraycopy(gcmInitialisationVector, 0, combinedCipherValueAndInitialisationVector, 0, gcmInitialisationVector.length); | ||
| System.arraycopy(cipherValue, 0, combinedCipherValueAndInitialisationVector, gcmInitialisationVector.length, | ||
| cipherValue.length); | ||
|
|
||
| return combinedCipherValueAndInitialisationVector; | ||
| } catch (Exception e) { | ||
| throw new SLException(MessageFormat.format(Messages.ENCRYPTION_HAS_FAILED, e.getMessage()), e); | ||
| } | ||
| } | ||
|
|
||
| public static String decrypt(byte[] encryptedValue, byte[] encryptionKey) { | ||
| try { | ||
| byte[] gcmInitialisationVector = new byte[Constants.INITIALISATION_VECTOR_LENGTH]; | ||
| System.arraycopy(encryptedValue, 0, gcmInitialisationVector, 0, | ||
| gcmInitialisationVector.length); | ||
|
|
||
| byte[] cipherValue = new byte[encryptedValue.length - Constants.INITIALISATION_VECTOR_LENGTH]; | ||
| System.arraycopy(encryptedValue, Constants.INITIALIZATION_VECTOR_POSITION, cipherValue, 0, cipherValue.length); | ||
|
|
||
| Cipher cipherObject = setUpCipherObject(encryptionKey, gcmInitialisationVector, false); | ||
|
|
||
| byte[] resultInBytes = cipherObject.doFinal(cipherValue); | ||
| return new String(resultInBytes, StandardCharsets.UTF_8); | ||
| } catch (Exception e) { | ||
| throw new SLException(MessageFormat.format(Messages.DECRYPTION_HAS_FAILED, e.getMessage()), e); | ||
| } | ||
| } | ||
|
|
||
| private static Cipher setUpCipherObject(byte[] encryptionKey, byte[] gcmInitialisationVector, boolean shouldEncrypt) | ||
| throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, | ||
| NoSuchProviderException { | ||
| Cipher cipherObject = Cipher.getInstance(Constants.CIPHER_TRANSFORMATION_NAME, BouncyCastleFipsProvider.PROVIDER_NAME); | ||
| SecretKeySpec secretKeySpec = new SecretKeySpec(encryptionKey, Constants.ENCRYPTION_DECRYPTION_ALGORITHM_NAME); | ||
| GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(Constants.GCM_AUTHENTICATION_TAG_LENGTH, gcmInitialisationVector); | ||
|
|
||
| if (shouldEncrypt) { | ||
| cipherObject.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec); | ||
| } else { | ||
| cipherObject.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec); | ||
| } | ||
|
|
||
| return cipherObject; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| package org.cloudfoundry.multiapps.controller.core.security.serialization; | ||
|
|
||
| import org.cloudfoundry.multiapps.controller.core.security.serialization.model.DeploymentDescriptorSerializer; | ||
| import org.cloudfoundry.multiapps.controller.core.security.serialization.model.ModuleSerializer; | ||
| import org.cloudfoundry.multiapps.controller.core.security.serialization.model.ProvidedDependencySerializer; | ||
| import org.cloudfoundry.multiapps.controller.core.security.serialization.model.RequiredDependencySerializer; | ||
| import org.cloudfoundry.multiapps.controller.core.security.serialization.model.ResourceSerializer; | ||
| import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; | ||
| import org.cloudfoundry.multiapps.mta.model.Module; | ||
| import org.cloudfoundry.multiapps.mta.model.ProvidedDependency; | ||
| import org.cloudfoundry.multiapps.mta.model.RequiredDependency; | ||
| import org.cloudfoundry.multiapps.mta.model.Resource; | ||
| import org.cloudfoundry.multiapps.mta.model.VersionedEntity; | ||
|
|
||
| public final class DynamicSecureSerialization { | ||
IvanBorislavovDimitrov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private final SecureSerializerConfiguration secureSerializerConfiguration; | ||
|
|
||
| public DynamicSecureSerialization(SecureSerializerConfiguration secureSerializerConfiguration) { | ||
| this.secureSerializerConfiguration = secureSerializerConfiguration; | ||
| } | ||
|
|
||
| public String toJson(Object object) { | ||
| SecureJsonSerializer secureJsonSerializer = createDynamicJsonSerializer(object); | ||
| return secureJsonSerializer.serialize(object); | ||
| } | ||
|
|
||
| private SecureJsonSerializer createDynamicJsonSerializer(Object object) { | ||
| SecureJsonSerializer secureJsonSerializer = createDynamicJsonSerializerForVersionedEntity(object); | ||
| if (secureJsonSerializer == null) { | ||
| return new SecureJsonSerializer(secureSerializerConfiguration); | ||
| } | ||
|
|
||
| return secureJsonSerializer; | ||
| } | ||
|
|
||
| private SecureJsonSerializer createDynamicJsonSerializerForVersionedEntity(Object object) { | ||
| if (object instanceof VersionedEntity) { | ||
| return createDynamicJsonSerializerForVersionedEntity((VersionedEntity) object); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| private SecureJsonSerializer createDynamicJsonSerializerForVersionedEntity(VersionedEntity versionedEntity) { | ||
| if (versionedEntity.getMajorSchemaVersion() < 3) { | ||
| return null; | ||
| } | ||
|
|
||
| if (versionedEntity instanceof DeploymentDescriptor) { | ||
| return new DeploymentDescriptorSerializer(secureSerializerConfiguration); | ||
| } | ||
|
|
||
| if (versionedEntity instanceof Module) { | ||
| return new ModuleSerializer(secureSerializerConfiguration); | ||
| } | ||
|
|
||
| if (versionedEntity instanceof ProvidedDependency) { | ||
| return new ProvidedDependencySerializer(secureSerializerConfiguration); | ||
| } | ||
|
|
||
| if (versionedEntity instanceof RequiredDependency) { | ||
| return new RequiredDependencySerializer(secureSerializerConfiguration); | ||
| } | ||
|
|
||
| if (versionedEntity instanceof Resource) { | ||
| return new ResourceSerializer(secureSerializerConfiguration); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package org.cloudfoundry.multiapps.controller.core.security.serialization; | ||
|
|
||
| import java.util.Collection; | ||
|
|
||
| public final class SecureSerializationFactory { | ||
|
|
||
| private SecureSerializationFactory() { | ||
|
|
||
| } | ||
|
|
||
| public static DynamicSecureSerialization ofAdditionalValues(Collection<String> additionalSensitiveElementNames) { | ||
| SecureSerializerConfiguration secureSerializerConfigurationWithAdditionalValues = new SecureSerializerConfiguration(); | ||
|
|
||
| secureSerializerConfigurationWithAdditionalValues.setAdditionalSensitiveElementNames(additionalSensitiveElementNames); | ||
| return new DynamicSecureSerialization(secureSerializerConfigurationWithAdditionalValues); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,12 @@ | |
|
|
||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.springframework.util.CollectionUtils; | ||
|
|
||
| public class SecureSerializerConfiguration { | ||
|
|
||
|
|
@@ -17,8 +20,25 @@ public class SecureSerializerConfiguration { | |
| private Collection<String> sensitiveElementNames = DEFAULT_SENSITIVE_NAMES; | ||
| private Collection<String> sensitiveElementPaths = Collections.emptyList(); | ||
|
|
||
| private Collection<String> additionalSensitiveElementNames = Collections.emptyList(); | ||
|
|
||
| public Collection<String> getSensitiveElementNames() { | ||
| return sensitiveElementNames; | ||
| if (CollectionUtils.isEmpty(additionalSensitiveElementNames)) { | ||
| return sensitiveElementNames; | ||
| } | ||
|
|
||
| Set<String> mergedSensitiveElementNames = new HashSet<>(sensitiveElementNames); | ||
|
|
||
| for (String additionalSensitiveElement : additionalSensitiveElementNames) { | ||
| boolean isNotExistent = mergedSensitiveElementNames.stream() | ||
| .noneMatch(sensitiveElement -> sensitiveElement.equalsIgnoreCase( | ||
| additionalSensitiveElement)); | ||
|
Comment on lines
+32
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it needed to have this check? Why not adding them as they are unique in the set? |
||
| if (isNotExistent) { | ||
| mergedSensitiveElementNames.add(additionalSensitiveElement); | ||
| } | ||
| } | ||
|
|
||
| return mergedSensitiveElementNames; | ||
| } | ||
|
|
||
| public Collection<String> getSensitiveElementPaths() { | ||
|
|
@@ -33,6 +53,10 @@ public void setSensitiveElementNames(Collection<String> sensitiveElementNames) { | |
| this.sensitiveElementNames = sensitiveElementNames; | ||
| } | ||
|
|
||
| public void setAdditionalSensitiveElementNames(Collection<String> additionalSensitiveElementNames) { | ||
| this.additionalSensitiveElementNames = additionalSensitiveElementNames; | ||
| } | ||
|
|
||
| public void setSensitiveElementPaths(Collection<String> sensitiveElementPaths) { | ||
| this.sensitiveElementPaths = sensitiveElementPaths; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not able to run the test in IDE? Could you check. I had to exclude:
com.aliyun.oss
aliyun-sdk-oss
org.ini4j
ini4j
javax.xml.bind
jaxb-api
org.bouncycastle
bcprov-jdk18on
${aliyun-sdk-oss.version}