Browse Source

Ensure that TextEncryptor can be excluded.

Moves usage of TextEncryptor to TextEncryptorUtils.

Fixes gh-912
pull/928/head
spencergibb 4 years ago
parent
commit
5928ccd168
No known key found for this signature in database
GPG Key ID: 7788A47380690861
  1. 4
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/TextEncryptorBindHandler.java
  2. 88
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/TextEncryptorConfigBootstrapper.java
  3. 29
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/DecryptEnvironmentPostProcessor.java
  4. 43
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java
  5. 193
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/TextEncryptorUtils.java

4
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/TextEncryptorBindHandler.java

@ -33,7 +33,7 @@ import org.springframework.security.crypto.encrypt.TextEncryptor; @@ -33,7 +33,7 @@ import org.springframework.security.crypto.encrypt.TextEncryptor;
* @author Marcin Grzejszczak
* @since 3.0.0
*/
class TextEncryptorBindHandler extends AbstractBindHandler {
public class TextEncryptorBindHandler extends AbstractBindHandler {
private static final Log logger = LogFactory.getLog(TextEncryptorBindHandler.class);
@ -46,7 +46,7 @@ class TextEncryptorBindHandler extends AbstractBindHandler { @@ -46,7 +46,7 @@ class TextEncryptorBindHandler extends AbstractBindHandler {
private final KeyProperties keyProperties;
TextEncryptorBindHandler(TextEncryptor textEncryptor, KeyProperties keyProperties) {
public TextEncryptorBindHandler(TextEncryptor textEncryptor, KeyProperties keyProperties) {
this.textEncryptor = textEncryptor;
this.keyProperties = keyProperties;
}

88
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/TextEncryptorConfigBootstrapper.java

@ -20,17 +20,11 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -20,17 +20,11 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.Bootstrapper;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.cloud.bootstrap.encrypt.RsaProperties;
import org.springframework.cloud.context.encrypt.EncryptorFactory;
import org.springframework.cloud.util.PropertyUtils;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.cloud.bootstrap.encrypt.TextEncryptorUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Bootstrapper.
@ -40,7 +34,10 @@ import org.springframework.util.StringUtils; @@ -40,7 +34,10 @@ import org.springframework.util.StringUtils;
*/
public class TextEncryptorConfigBootstrapper implements Bootstrapper {
private static final boolean RSA_IS_PRESENT = ClassUtils
/**
* RsaSecretEncryptor present.
*/
public static final boolean RSA_IS_PRESENT = ClassUtils
.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null);
@Override
@ -55,30 +52,11 @@ public class TextEncryptorConfigBootstrapper implements Bootstrapper { @@ -55,30 +52,11 @@ public class TextEncryptorConfigBootstrapper implements Bootstrapper {
registry.registerIfAbsent(RsaProperties.class, context -> context.get(Binder.class)
.bind(RsaProperties.PREFIX, RsaProperties.class).orElseGet(RsaProperties::new));
}
registry.registerIfAbsent(TextEncryptor.class, context -> {
KeyProperties keyProperties = context.get(KeyProperties.class);
if (keysConfigured(keyProperties)) {
if (RSA_IS_PRESENT) {
RsaProperties rsaProperties = context.get(RsaProperties.class);
return EncryptionBootstrapConfiguration.createTextEncryptor(keyProperties, rsaProperties);
}
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
}
// no keys configured
return new FailsafeTextEncryptor();
});
registry.registerIfAbsent(BindHandler.class, context -> {
TextEncryptor textEncryptor = context.get(TextEncryptor.class);
if (textEncryptor != null) {
KeyProperties keyProperties = context.get(KeyProperties.class);
return new TextEncryptorBindHandler(textEncryptor, keyProperties);
}
return null;
});
TextEncryptorUtils.register(registry);
// promote beans to context
registry.addCloseListener(event -> {
if (isLegacyBootstrap(event.getApplicationContext().getEnvironment())) {
if (TextEncryptorUtils.isLegacyBootstrap(event.getApplicationContext().getEnvironment())) {
return;
}
BootstrapContext bootstrapContext = event.getBootstrapContext();
@ -93,59 +71,17 @@ public class TextEncryptorConfigBootstrapper implements Bootstrapper { @@ -93,59 +71,17 @@ public class TextEncryptorConfigBootstrapper implements Bootstrapper {
beanFactory.registerSingleton("rsaProperties", rsaProperties);
}
}
TextEncryptor textEncryptor = bootstrapContext.get(TextEncryptor.class);
if (textEncryptor != null) {
beanFactory.registerSingleton("textEncryptor", textEncryptor);
}
TextEncryptorUtils.promote(bootstrapContext, beanFactory);
});
}
@Deprecated
public static boolean keysConfigured(KeyProperties properties) {
if (hasProperty(properties.getKeyStore().getLocation())) {
if (hasProperty(properties.getKeyStore().getPassword())) {
return true;
}
return false;
}
else if (hasProperty(properties.getKey())) {
return true;
}
return false;
return TextEncryptorUtils.keysConfigured(properties);
}
static boolean hasProperty(Object value) {
if (value instanceof String) {
return StringUtils.hasText((String) value);
}
return value != null;
}
static boolean isLegacyBootstrap(Environment environment) {
boolean isLegacy = PropertyUtils.useLegacyProcessing(environment);
boolean isBootstrapEnabled = PropertyUtils.bootstrapEnabled(environment);
return isLegacy || isBootstrapEnabled;
}
/**
* TextEncryptor that just fails, so that users don't get a false sense of security
* adding ciphers to config files and not getting them decrypted.
*
* @author Dave Syer
*
*/
public static class FailsafeTextEncryptor implements TextEncryptor {
@Override
public String encrypt(String text) {
throw new UnsupportedOperationException(
"No encryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}
@Override
public String decrypt(String encryptedText) {
throw new UnsupportedOperationException(
"No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}
@Deprecated
public static class FailsafeTextEncryptor extends TextEncryptorUtils.FailsafeTextEncryptor {
}

29
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/DecryptEnvironmentPostProcessor.java

@ -19,18 +19,13 @@ package org.springframework.cloud.bootstrap.encrypt; @@ -19,18 +19,13 @@ package org.springframework.cloud.bootstrap.encrypt;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper.FailsafeTextEncryptor;
import org.springframework.cloud.context.encrypt.EncryptorFactory;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.SystemEnvironmentPropertySource;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.util.ClassUtils;
import static org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper.keysConfigured;
import static org.springframework.cloud.util.PropertyUtils.bootstrapEnabled;
import static org.springframework.cloud.util.PropertyUtils.useLegacyProcessing;
@ -60,14 +55,15 @@ public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt @@ -60,14 +55,15 @@ public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt
if (bootstrapEnabled(environment) || useLegacyProcessing(environment)) {
return;
}
if (!ClassUtils.isPresent("org.springframework.security.crypto.encrypt.TextEncryptor", null)) {
return;
}
MutablePropertySources propertySources = environment.getPropertySources();
environment.getPropertySources().remove(DECRYPTED_PROPERTY_SOURCE_NAME);
TextEncryptor encryptor = getTextEncryptor(environment);
Map<String, Object> map = decrypt(encryptor, propertySources);
Map<String, Object> map = TextEncryptorUtils.decrypt(this, environment, propertySources);
if (!map.isEmpty()) {
// We have some decrypted properties
propertySources.addFirst(new SystemEnvironmentPropertySource(DECRYPTED_PROPERTY_SOURCE_NAME, map));
@ -75,21 +71,4 @@ public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt @@ -75,21 +71,4 @@ public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt
}
protected TextEncryptor getTextEncryptor(ConfigurableEnvironment environment) {
Binder binder = Binder.get(environment);
KeyProperties keyProperties = binder.bind(KeyProperties.PREFIX, KeyProperties.class)
.orElseGet(KeyProperties::new);
if (keysConfigured(keyProperties)) {
setFailOnError(keyProperties.isFailOnError());
if (ClassUtils.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null)) {
RsaProperties rsaProperties = binder.bind(RsaProperties.PREFIX, RsaProperties.class)
.orElseGet(RsaProperties::new);
return EncryptionBootstrapConfiguration.createTextEncryptor(keyProperties, rsaProperties);
}
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
}
// no keys configured
return new FailsafeTextEncryptor();
}
}

43
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java

@ -24,7 +24,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean @@ -24,7 +24,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties.KeyStore;
import org.springframework.cloud.context.encrypt.EncryptorFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@ -34,7 +33,6 @@ import org.springframework.context.annotation.Configuration; @@ -34,7 +33,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
import org.springframework.util.StringUtils;
@ -61,27 +59,16 @@ public class EncryptionBootstrapConfiguration { @@ -61,27 +59,16 @@ public class EncryptionBootstrapConfiguration {
encryptor = context.getBean(TextEncryptor.class);
}
catch (NoSuchBeanDefinitionException e) {
encryptor = new FailsafeTextEncryptor();
encryptor = new TextEncryptorUtils.FailsafeTextEncryptor();
}
EnvironmentDecryptApplicationInitializer listener = new EnvironmentDecryptApplicationInitializer(encryptor);
listener.setFailOnError(keyProperties.isFailOnError());
return listener;
}
@Deprecated
public static TextEncryptor createTextEncryptor(KeyProperties keyProperties, RsaProperties rsaProperties) {
KeyStore keyStore = keyProperties.getKeyStore();
if (keyStore.getLocation() != null) {
if (keyStore.getLocation().exists()) {
return new RsaSecretEncryptor(
new KeyStoreKeyFactory(keyStore.getLocation(), keyStore.getPassword().toCharArray())
.getKeyPair(keyStore.getAlias(), keyStore.getSecret().toCharArray()),
rsaProperties.getAlgorithm(), rsaProperties.getSalt(), rsaProperties.isStrong());
}
throw new IllegalStateException("Invalid keystore location");
}
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
return TextEncryptorUtils.createTextEncryptor(keyProperties, rsaProperties);
}
@Configuration(proxyBeanMethods = false)
@ -99,7 +86,7 @@ public class EncryptionBootstrapConfiguration { @@ -99,7 +86,7 @@ public class EncryptionBootstrapConfiguration {
@Bean
@ConditionalOnMissingBean(TextEncryptor.class)
public TextEncryptor textEncryptor(KeyProperties keyProperties, RsaProperties rsaProperties) {
return createTextEncryptor(keyProperties, rsaProperties);
return TextEncryptorUtils.createTextEncryptor(keyProperties, rsaProperties);
}
}
@ -150,26 +137,8 @@ public class EncryptionBootstrapConfiguration { @@ -150,26 +137,8 @@ public class EncryptionBootstrapConfiguration {
}
/**
* TextEncryptor that just fails, so that users don't get a false sense of security
* adding ciphers to config files and not getting them decrypted.
*
* @author Dave Syer
*
*/
protected static class FailsafeTextEncryptor implements TextEncryptor {
@Override
public String encrypt(String text) {
throw new UnsupportedOperationException(
"No encryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}
@Override
public String decrypt(String encryptedText) {
throw new UnsupportedOperationException(
"No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}
@Deprecated
protected static class FailsafeTextEncryptor extends TextEncryptorUtils.FailsafeTextEncryptor {
}

193
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/TextEncryptorUtils.java

@ -0,0 +1,193 @@ @@ -0,0 +1,193 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.cloud.bootstrap.encrypt;
import java.util.Map;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.bootstrap.TextEncryptorBindHandler;
import org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper;
import org.springframework.cloud.context.encrypt.EncryptorFactory;
import org.springframework.cloud.util.PropertyUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
public abstract class TextEncryptorUtils {
/**
* Decrypt environment. See {@link DecryptEnvironmentPostProcessor}.
* @param decryptor the {@link AbstractEnvironmentDecrypt}
* @param environment the environment to get key properties from.
* @param propertySources the property sources to decrypt.
* @return the decrypted properties.
*/
static Map<String, Object> decrypt(AbstractEnvironmentDecrypt decryptor, ConfigurableEnvironment environment,
MutablePropertySources propertySources) {
TextEncryptor encryptor = getTextEncryptor(decryptor, environment);
return decryptor.decrypt(encryptor, propertySources);
}
static TextEncryptor getTextEncryptor(AbstractEnvironmentDecrypt decryptor, ConfigurableEnvironment environment) {
Binder binder = Binder.get(environment);
KeyProperties keyProperties = binder.bind(KeyProperties.PREFIX, KeyProperties.class)
.orElseGet(KeyProperties::new);
if (TextEncryptorUtils.keysConfigured(keyProperties)) {
decryptor.setFailOnError(keyProperties.isFailOnError());
if (ClassUtils.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null)) {
RsaProperties rsaProperties = binder.bind(RsaProperties.PREFIX, RsaProperties.class)
.orElseGet(RsaProperties::new);
return TextEncryptorUtils.createTextEncryptor(keyProperties, rsaProperties);
}
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
}
// no keys configured
return new TextEncryptorUtils.FailsafeTextEncryptor();
}
/**
* Register all classes that need a {@link TextEncryptor} in {@link TextEncryptorConfigBootstrapper}.
* @param registry the BootstrapRegistry.
*/
public static void register(BootstrapRegistry registry) {
registry.registerIfAbsent(TextEncryptor.class, context -> {
KeyProperties keyProperties = context.get(KeyProperties.class);
if (TextEncryptorConfigBootstrapper.keysConfigured(keyProperties)) {
if (TextEncryptorConfigBootstrapper.RSA_IS_PRESENT) {
RsaProperties rsaProperties = context.get(RsaProperties.class);
return createTextEncryptor(keyProperties, rsaProperties);
}
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
}
// no keys configured
return new FailsafeTextEncryptor();
});
registry.registerIfAbsent(BindHandler.class, context -> {
TextEncryptor textEncryptor = context.get(TextEncryptor.class);
if (textEncryptor != null) {
KeyProperties keyProperties = context.get(KeyProperties.class);
return new TextEncryptorBindHandler(textEncryptor, keyProperties);
}
return null;
});
}
/**
* Promote the {@link TextEncryptor} to the {@link ApplicationContext}.
* @param bootstrapContext the Context.
* @param beanFactory the bean factory.
*/
public static void promote(BootstrapContext bootstrapContext, ConfigurableListableBeanFactory beanFactory) {
TextEncryptor textEncryptor = bootstrapContext.get(TextEncryptor.class);
if (textEncryptor != null) {
beanFactory.registerSingleton("textEncryptor", textEncryptor);
}
}
/**
* Utility to create a {@link TextEncryptor} via properties.
* @param keyProperties the Key properties.
* @param rsaProperties RSA properties.
* @return created {@link TextEncryptor}.
*/
public static TextEncryptor createTextEncryptor(KeyProperties keyProperties, RsaProperties rsaProperties) {
KeyProperties.KeyStore keyStore = keyProperties.getKeyStore();
if (keyStore.getLocation() != null) {
if (keyStore.getLocation().exists()) {
return new RsaSecretEncryptor(
new KeyStoreKeyFactory(keyStore.getLocation(), keyStore.getPassword().toCharArray())
.getKeyPair(keyStore.getAlias(), keyStore.getSecret().toCharArray()),
rsaProperties.getAlgorithm(), rsaProperties.getSalt(), rsaProperties.isStrong());
}
throw new IllegalStateException("Invalid keystore location");
}
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
}
/**
* Is a key configured.
* @param properties the Key properties.
* @return true if configured.
*/
public static boolean keysConfigured(KeyProperties properties) {
if (hasProperty(properties.getKeyStore().getLocation())) {
if (hasProperty(properties.getKeyStore().getPassword())) {
return true;
}
return false;
}
else if (hasProperty(properties.getKey())) {
return true;
}
return false;
}
static boolean hasProperty(Object value) {
if (value instanceof String) {
return StringUtils.hasText((String) value);
}
return value != null;
}
/**
* Method to check if legacy bootstrap mode is enabled. This is either if the
* boot legacy processing property is set or spring.cloud.bootstrap.enabled=true.
* @param environment where to check properties.
* @return true if bootstrap enabled.
*/
public static boolean isLegacyBootstrap(Environment environment) {
boolean isLegacy = PropertyUtils.useLegacyProcessing(environment);
boolean isBootstrapEnabled = PropertyUtils.bootstrapEnabled(environment);
return isLegacy || isBootstrapEnabled;
}
/**
* TextEncryptor that just fails, so that users don't get a false sense of security
* adding ciphers to config files and not getting them decrypted.
*
* @author Dave Syer
*
*/
public static class FailsafeTextEncryptor implements TextEncryptor {
@Override
public String encrypt(String text) {
throw new UnsupportedOperationException(
"No encryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}
@Override
public String decrypt(String encryptedText) {
throw new UnsupportedOperationException(
"No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}
}
}
Loading…
Cancel
Save