From ee5034ee0d9213746b4fe8ef5d04da8b34120526 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 21 Mar 2018 10:35:24 -0400 Subject: [PATCH] Move RSA properties to its own class and make it conditional (#342) Avoids ClassNotFoundException when RSA is not on the classpath. Fixes gh-334. --- .../EncryptionBootstrapConfiguration.java | 10 ++- .../bootstrap/encrypt/KeyProperties.java | 65 ----------------- .../bootstrap/encrypt/RsaProperties.java | 73 +++++++++++++++++++ ...EncryptionBootstrapConfigurationTests.java | 37 ++++++++++ .../bootstrap/encrypt/RsaDisabledTests.java | 61 ++++++++++++++++ 5 files changed, 178 insertions(+), 68 deletions(-) create mode 100644 spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/RsaProperties.java create mode 100644 spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/RsaDisabledTests.java diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java index 0ab56be6..7fa912a0 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java @@ -42,7 +42,7 @@ import org.springframework.util.StringUtils; */ @Configuration @ConditionalOnClass({ TextEncryptor.class }) -@EnableConfigurationProperties(KeyProperties.class) +@EnableConfigurationProperties({KeyProperties.class}) public class EncryptionBootstrapConfiguration { @Autowired(required = false) @@ -54,11 +54,15 @@ public class EncryptionBootstrapConfiguration { @Configuration @Conditional(KeyCondition.class) @ConditionalOnClass(RsaSecretEncryptor.class) + @EnableConfigurationProperties({RsaProperties.class}) protected static class RsaEncryptionConfiguration { @Autowired private KeyProperties key; + @Autowired + private RsaProperties rsaProperties; + @Bean @ConditionalOnMissingBean(TextEncryptor.class) public TextEncryptor textEncryptor() { @@ -70,8 +74,8 @@ public class EncryptionBootstrapConfiguration { keyStore.getPassword().toCharArray()).getKeyPair( keyStore.getAlias(), keyStore.getSecret().toCharArray()), - this.key.getRsa().getAlgorithm(), this.key.getRsa().getSalt(), - this.key.getRsa().isStrong()); + this.rsaProperties.getAlgorithm(), this.rsaProperties.getSalt(), + this.rsaProperties.isStrong()); } throw new IllegalStateException("Invalid keystore location"); diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java index df2e5fcc..9becea2a 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java @@ -17,8 +17,6 @@ package org.springframework.cloud.bootstrap.encrypt; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.io.Resource; -import org.springframework.security.rsa.crypto.RsaAlgorithm; -import org.springframework.util.ClassUtils; @ConfigurationProperties("encrypt") public class KeyProperties { @@ -40,21 +38,6 @@ public class KeyProperties { */ private KeyStore keyStore = new KeyStore(); - /** - * Rsa algorithm properties when using asymmetric encryption. - */ - private Rsa rsa; - - { - if (ClassUtils.isPresent("org.springframework.security.rsa.crypto.RsaAlgorithm", null)) { - this.rsa = new Rsa(); - } - } - - public Rsa getRsa() { - return this.rsa; - } - public boolean isFailOnError() { return this.failOnError; } @@ -134,52 +117,4 @@ public class KeyProperties { } } - - public static class Rsa { - - /** - * The RSA algorithm to use (DEFAULT or OEAP). Once it is set do not change it (or - * existing ciphers will not a decryptable). - */ - private RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT; - - /** - * Flag to indicate that "strong" AES encryption should be used internally. If - * true then the GCM algorithm is applied to the AES encrypted bytes. Default is - * false (in which case "standard" CBC is used instead). Once it is set do not - * change it (or existing ciphers will not a decryptable). - */ - private boolean strong = false; - - /** - * Salt for the random secret used to encrypt cipher text. Once it is set do not - * change it (or existing ciphers will not a decryptable). - */ - private String salt = "deadbeef"; - - public RsaAlgorithm getAlgorithm() { - return this.algorithm; - } - - public void setAlgorithm(RsaAlgorithm algorithm) { - this.algorithm = algorithm; - } - - public boolean isStrong() { - return this.strong; - } - - public void setStrong(boolean strong) { - this.strong = strong; - } - - public String getSalt() { - return this.salt; - } - - public void setSalt(String salt) { - this.salt = salt; - } - - } } \ No newline at end of file diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/RsaProperties.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/RsaProperties.java new file mode 100644 index 00000000..38b05550 --- /dev/null +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/RsaProperties.java @@ -0,0 +1,73 @@ +package org.springframework.cloud.bootstrap.encrypt; +/* + * Copyright 2013-2018 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 + * + * 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. + */ + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.security.rsa.crypto.RsaAlgorithm; + +/** + * @author Ryan Baxter + */ +@ConditionalOnClass(RsaAlgorithm.class) +@ConfigurationProperties("encrypt.rsa") +public class RsaProperties { + + /** + * The RSA algorithm to use (DEFAULT or OEAP). Once it is set do not change it (or + * existing ciphers will not a decryptable). + */ + private RsaAlgorithm algorithm = RsaAlgorithm.DEFAULT; + + /** + * Flag to indicate that "strong" AES encryption should be used internally. If + * true then the GCM algorithm is applied to the AES encrypted bytes. Default is + * false (in which case "standard" CBC is used instead). Once it is set do not + * change it (or existing ciphers will not a decryptable). + */ + private boolean strong = false; + + /** + * Salt for the random secret used to encrypt cipher text. Once it is set do not + * change it (or existing ciphers will not a decryptable). + */ + private String salt = "deadbeef"; + + public RsaAlgorithm getAlgorithm() { + return this.algorithm; + } + + public void setAlgorithm(RsaAlgorithm algorithm) { + this.algorithm = algorithm; + } + + public boolean isStrong() { + return this.strong; + } + + public void setStrong(boolean strong) { + this.strong = strong; + } + + public String getSalt() { + return this.salt; + } + + public void setSalt(String salt) { + this.salt = salt; + } + +} diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfigurationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfigurationTests.java index 5097109e..4802038f 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfigurationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfigurationTests.java @@ -1,12 +1,30 @@ package org.springframework.cloud.bootstrap.encrypt; +/* + * Copyright 2013-2018 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 + * + * 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. + */ + import org.junit.Test; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.security.crypto.encrypt.TextEncryptor; +import org.springframework.security.rsa.crypto.RsaAlgorithm; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class EncryptionBootstrapConfigurationTests { @@ -36,6 +54,25 @@ public class EncryptionBootstrapConfigurationTests { context.close(); } + + @Test + public void rsaProperties() { + ConfigurableApplicationContext context = new SpringApplicationBuilder( + EncryptionBootstrapConfiguration.class).web(false).properties( + "encrypt.key-store.location:classpath:/server.jks", + "encrypt.key-store.password:letmein", + "encrypt.key-store.alias:mytestkey", "encrypt.key-store.secret:changeme", + "encrypt.rsa.strong:true", + "encrypt.rsa.salt:foobar") + .run(); + RsaProperties properties = context.getBean(RsaProperties.class); + assertEquals("foobar", properties.getSalt()); + assertTrue(properties.isStrong()); + assertEquals(RsaAlgorithm.DEFAULT, properties.getAlgorithm()); + context.close(); + } + + @Test public void nonExistentKeystoreLocationShouldNotBeAllowed() { try { diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/RsaDisabledTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/RsaDisabledTests.java new file mode 100644 index 00000000..faae8fe7 --- /dev/null +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/RsaDisabledTests.java @@ -0,0 +1,61 @@ +package org.springframework.cloud.bootstrap.encrypt; +/* + * Copyright 2013-2018 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 + * + * 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. + */ + +import java.util.Map; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.ClassPathExclusions; +import org.springframework.cloud.FilteredClassPathRunner; +import org.springframework.context.ConfigurableApplicationContext; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +/** + * @author Ryan Baxter + */ +@RunWith(FilteredClassPathRunner.class) +@ClassPathExclusions({"spring-security-rsa*.jar"}) +public class RsaDisabledTests { + + private ConfigurableApplicationContext context; + + @Before + public void setUp() { + context = new SpringApplicationBuilder().web(false) + .sources(EncryptionBootstrapConfiguration.class).web(false).properties( + "encrypt.key:mykey", + "encrypt.rsa.strong:true", + "encrypt.rsa.salt:foobar").run(); + } + + @After + public void tearDown() { + if(context != null) { + context.close(); + } + } + + @Test + public void testLoadBalancedRetryFactoryBean() throws Exception { + Map properties = context.getBeansOfType(RsaProperties.class); + assertThat(properties.values(), hasSize(0)); + } +}