diff --git a/documentation/src/main/docs/config/secret-keys.md b/documentation/src/main/docs/config/secret-keys.md
index 3f00002c1..00e088a4f 100644
--- a/documentation/src/main/docs/config/secret-keys.md
+++ b/documentation/src/main/docs/config/secret-keys.md
@@ -38,9 +38,9 @@ the following dependency:
!!! example
```properties title="application.properties"
- smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key=somearbitrarycrazystringthatdoesnotmatter
+ smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key=DDne5obnfH1RSeTg71xSZg
- my.secret=${aes-gcm-nopadding::DJNrZ6LfpupFv6QbXyXhvzD8eVDnDa_kTliQBpuzTobDZxlg}
+ my.secret=${aes-gcm-nopadding::DLTb_9zxThxeT5iAQqswEl5Dn1ju4FdM9hIyVip35t5V}
```
The `${aes-gcm-nopadding::...}` `SecretKeyHandler` requires
@@ -60,9 +60,10 @@ the following dependency:
##### Configuration
-| Configuration Property | Type | Default |
-|--- |--- |--- |
-| `smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key`
The encription key to use to decode secrets encoded by the `AES/GCM/NoPadding` algorithm. | String | |
+| Configuration Property | Type | Default |
+|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|-----------|
+| `smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key`
The encryption key to use to decode secrets encoded by the `AES/GCM/NoPadding` algorithm. | String | |
+| `"smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key-decode"`
Decode the encryption key in Base64. | boolean | false |
### Jasypt
diff --git a/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfig.java b/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfig.java
index 248d26009..84b5f5a17 100644
--- a/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfig.java
+++ b/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfig.java
@@ -18,8 +18,6 @@ interface KeyStore {
@WithDefault("PKCS12")
String type();
- String password();
-
Optional handler();
Map aliases();
@@ -27,8 +25,6 @@ interface KeyStore {
interface Alias {
Optional name();
- Optional password();
-
Optional handler();
}
}
diff --git a/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfigSourceFactory.java b/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfigSourceFactory.java
index f81066321..9b4b7c97f 100644
--- a/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfigSourceFactory.java
+++ b/sources/keystore/src/main/java/io/smallrye/config/source/keystore/KeyStoreConfigSourceFactory.java
@@ -13,47 +13,116 @@
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import org.eclipse.microprofile.config.spi.ConfigSource;
import io.smallrye.config.AbstractLocationConfigSourceFactory;
+import io.smallrye.config.ConfigMessages;
import io.smallrye.config.ConfigSourceContext;
-import io.smallrye.config.ConfigSourceFactory.ConfigurableConfigSourceFactory;
-import io.smallrye.config.ConfigurableConfigSource;
+import io.smallrye.config.ConfigSourceFactory;
+import io.smallrye.config.ConfigValue;
import io.smallrye.config.PropertiesConfigSource;
+import io.smallrye.config.SmallRyeConfig;
+import io.smallrye.config.SmallRyeConfigBuilder;
import io.smallrye.config.source.keystore.KeyStoreConfig.KeyStore.Alias;
-public class KeyStoreConfigSourceFactory implements ConfigurableConfigSourceFactory {
+public class KeyStoreConfigSourceFactory implements ConfigSourceFactory {
@Override
- public Iterable getConfigSources(final ConfigSourceContext context, KeyStoreConfig keyStoreConfig) {
+ public Iterable getConfigSources(final ConfigSourceContext context) {
+ KeyStoreConfig keyStoreConfig = getKeyStoreConfig(context);
+
+ // A keystore may contain the encryption key for a handler, so we load keystore that do not have handlers
+ Map prioritized = new HashMap<>();
+ Map late = new HashMap<>();
+ for (final Map.Entry keyStoreEntry : keyStoreConfig.keystores().entrySet()) {
+ if (keyStoreEntry.getValue().handler().isEmpty()) {
+ prioritized.put(keyStoreEntry.getKey(), keyStoreEntry.getValue());
+ } else {
+ late.put(keyStoreEntry.getKey(), keyStoreEntry.getValue());
+ }
+ }
+
List keyStoreSources = new ArrayList<>();
- for (Map.Entry keyStoreEntry : keyStoreConfig.keystores().entrySet()) {
- KeyStoreConfig.KeyStore keyStore = keyStoreEntry.getValue();
- keyStoreSources.add(new ConfigurableConfigSource(new AbstractLocationConfigSourceFactory() {
- @Override
- protected String[] getFileExtensions() {
- return new String[0];
- }
+ for (Map.Entry keyStoreEntry : prioritized.entrySet()) {
+ for (final ConfigSource configSource : loadKeyStoreSources(context, keyStoreEntry.getKey(),
+ keyStoreEntry.getValue())) {
+ keyStoreSources.add(configSource);
+ }
+ }
- @Override
- protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws IOException {
- return new UrlKeyStoreConfigSource(url, ordinal).loadKeyStore(keyStore);
- }
+ ConfigSourceContext keyStoreContext = new ConfigSourceContext() {
+ final SmallRyeConfig contextConfig = new SmallRyeConfigBuilder()
+ .withSources(new ConfigSourceContext.ConfigSourceContextConfigSource(context))
+ .withSources(keyStoreSources).build();
- @Override
- public Iterable getConfigSources(final ConfigSourceContext context) {
- return loadConfigSources(keyStore.path(), 100);
- }
- }));
+ @Override
+ public ConfigValue getValue(final String name) {
+ return contextConfig.getConfigValue(name);
+ }
+
+ @Override
+ public List getProfiles() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterator iterateNames() {
+ return contextConfig.getPropertyNames().iterator();
+ }
+ };
+
+ for (Map.Entry keyStoreEntry : late.entrySet()) {
+ for (final ConfigSource configSource : loadKeyStoreSources(keyStoreContext, keyStoreEntry.getKey(),
+ keyStoreEntry.getValue())) {
+ keyStoreSources.add(configSource);
+ }
}
return keyStoreSources;
}
+ private static KeyStoreConfig getKeyStoreConfig(final ConfigSourceContext context) {
+ SmallRyeConfig config = new SmallRyeConfigBuilder()
+ .withSources(new ConfigSourceContext.ConfigSourceContextConfigSource(context))
+ .withMapping(KeyStoreConfig.class)
+ .withMappingIgnore("smallrye.config.source.keystore.*.password")
+ .build();
+ return config.getConfigMapping(KeyStoreConfig.class);
+ }
+
+ private static Iterable loadKeyStoreSources(final ConfigSourceContext context, final String name,
+ final KeyStoreConfig.KeyStore keyStore) {
+ return new AbstractLocationConfigSourceFactory() {
+ @Override
+ protected String[] getFileExtensions() {
+ return new String[0];
+ }
+
+ @Override
+ protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws IOException {
+ // Avoid caching the keystore password
+ String passwordName = "smallrye.config.source.keystore." + name + ".password";
+ ConfigValue password = context.getValue(passwordName);
+ if (password == null || password.getValue() == null) {
+ throw new NoSuchElementException(ConfigMessages.msg.propertyNotFound(name));
+ }
+ return new UrlKeyStoreConfigSource(url, ordinal).loadKeyStore(keyStore, password.getValue().toCharArray());
+ }
+
+ @Override
+ public Iterable getConfigSources(final ConfigSourceContext context) {
+ return loadConfigSources(keyStore.path(), 100);
+ }
+
+ }.getConfigSources(context);
+ }
+
private static class UrlKeyStoreConfigSource implements ConfigSource {
private final URL url;
private final int ordinal;
@@ -63,10 +132,10 @@ private static class UrlKeyStoreConfigSource implements ConfigSource {
this.ordinal = ordinal;
}
- ConfigSource loadKeyStore(KeyStoreConfig.KeyStore keyStoreConfig) throws IOException {
+ ConfigSource loadKeyStore(KeyStoreConfig.KeyStore keyStoreConfig, char[] password) throws IOException {
try {
KeyStore keyStore = KeyStore.getInstance(keyStoreConfig.type());
- keyStore.load(url.openStream(), keyStoreConfig.password().toCharArray());
+ keyStore.load(url.openStream(), password);
Map properties = new HashMap<>();
Enumeration aliases = keyStore.aliases();
@@ -78,11 +147,6 @@ public Optional name() {
return Optional.of(alias);
}
- @Override
- public Optional password() {
- return Optional.of(keyStoreConfig.password());
- }
-
@Override
public Optional handler() {
return keyStoreConfig.handler();
@@ -90,8 +154,7 @@ public Optional handler() {
});
if (keyStore.isKeyEntry(alias)) {
- Key key = keyStore.getKey(alias,
- aliasConfig.password().orElse(keyStoreConfig.password()).toCharArray());
+ Key key = keyStore.getKey(alias, password);
String encoded;
Optional handler = aliasConfig.handler();
if (handler.isPresent()) {
diff --git a/testsuite/extra/src/test/java/io/smallrye/config/test/secrets/MultipleSecretHandlersTest.java b/testsuite/extra/src/test/java/io/smallrye/config/test/secrets/MultipleSecretHandlersTest.java
index 77ee3a60c..35ffde16c 100644
--- a/testsuite/extra/src/test/java/io/smallrye/config/test/secrets/MultipleSecretHandlersTest.java
+++ b/testsuite/extra/src/test/java/io/smallrye/config/test/secrets/MultipleSecretHandlersTest.java
@@ -16,6 +16,7 @@ void multipleHandlers() {
Map properties = Map.of(
"smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key",
"c29tZWFyYml0cmFyeWNyYXp5c3RyaW5ndGhhdGRvZXNub3RtYXR0ZXI",
+ "smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key-decode", "true",
"aes-gcm-nopadding.secret", "${aes-gcm-nopadding::DJNrZ6LfpupFv6QbXyXhvzD8eVDnDa_kTliQBpuzTobDZxlg}",
"smallrye.config.secret-handler.jasypt.password", "jasypt",
"smallrye.config.secret-handler.jasypt.algorithm", "PBEWithHMACSHA512AndAES_256",
diff --git a/utils/crypto/src/main/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerFactory.java b/utils/crypto/src/main/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerFactory.java
index dd1fcc50e..390184bf7 100644
--- a/utils/crypto/src/main/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerFactory.java
+++ b/utils/crypto/src/main/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerFactory.java
@@ -24,7 +24,7 @@ public SecretKeysHandler getSecretKeysHandler(final ConfigSourceContext context)
throw new NoSuchElementException(ConfigMessages.msg.propertyNotFound(ENCRYPTION_KEY));
}
- boolean decode = true;
+ boolean decode = false;
ConfigValue plain = context.getValue("smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key-decode");
if (plain != null && plain.getValue() != null) {
decode = Converters.getImplicitConverter(Boolean.class).convert(plain.getValue());
diff --git a/utils/crypto/src/test/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerTest.java b/utils/crypto/src/test/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerTest.java
index ebd521895..43a7ff3c6 100644
--- a/utils/crypto/src/test/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerTest.java
+++ b/utils/crypto/src/test/java/io/smallrye/config/crypto/AESGCMNoPaddingSecretKeysHandlerTest.java
@@ -19,13 +19,13 @@
class AESGCMNoPaddingSecretKeysHandlerTest {
@Test
void handler() {
-
SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultInterceptors()
.addDiscoveredSecretKeysHandlers()
.withDefaultValues(Map.of(
"smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key",
"c29tZWFyYml0cmFyeWNyYXp5c3RyaW5ndGhhdGRvZXNub3RtYXR0ZXI",
+ "smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key-decode", "true",
"my.secret", "${aes-gcm-nopadding::DJNrZ6LfpupFv6QbXyXhvzD8eVDnDa_kTliQBpuzTobDZxlg}",
"my.expression", "${not.found:default}",
"another.expression", "${my.expression}"))
@@ -43,16 +43,15 @@ void keystore() {
.addDiscoveredSources()
.addDiscoveredSecretKeysHandlers()
.withDefaultValues(Map.of(
- "smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key",
- "somearbitrarycrazystringthatdoesnotmatter",
- "smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key-decode", "false",
- "smallrye.config.source.keystore.test.path", "keystore",
- "smallrye.config.source.keystore.test.password", "secret",
- "smallrye.config.source.keystore.test.handler", "aes-gcm-nopadding"))
+ "smallrye.config.source.keystore.\"properties\".path", "properties",
+ "smallrye.config.source.keystore.\"properties\".password", "arealpassword",
+ "smallrye.config.source.keystore.\"properties\".handler", "aes-gcm-nopadding",
+ "smallrye.config.source.keystore.\"key\".path", "key",
+ "smallrye.config.source.keystore.\"key\".password", "anotherpassword"))
.build();
ConfigValue secret = config.getConfigValue("my.secret");
- assertEquals("decoded", secret.getValue());
+ assertEquals("1234", secret.getValue());
}
@Test
@@ -86,8 +85,10 @@ void configurableSource() {
"smallrye.config.source.keystore.test.password", "secret",
"smallrye.config.source.keystore.test.handler", "aes-gcm-nopadding"))
.withSources((ConfigSourceFactory) context -> List.of(
- new PropertiesConfigSource(Map.of("smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key",
- "c29tZWFyYml0cmFyeWNyYXp5c3RyaW5ndGhhdGRvZXNub3RtYXR0ZXI"), "", 0)))
+ new PropertiesConfigSource(Map.of(
+ "smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key",
+ "c29tZWFyYml0cmFyeWNyYXp5c3RyaW5ndGhhdGRvZXNub3RtYXR0ZXI",
+ "smallrye.config.secret-handler.aes-gcm-nopadding.encryption-key-decode", "true"), "", 0)))
.build();
ConfigValue secret = config.getConfigValue("my.secret");
diff --git a/utils/crypto/src/test/resources/key b/utils/crypto/src/test/resources/key
new file mode 100644
index 000000000..cfa938083
Binary files /dev/null and b/utils/crypto/src/test/resources/key differ
diff --git a/utils/crypto/src/test/resources/properties b/utils/crypto/src/test/resources/properties
new file mode 100644
index 000000000..679464277
Binary files /dev/null and b/utils/crypto/src/test/resources/properties differ