diff --git a/pom.xml b/pom.xml
index 319719f..19f158d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,11 +45,6 @@
slf4j-api
${version.slf4j}
-
- org.codehaus.plexus
- plexus-cipher
- 3.0.0
-
javax.inject
diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/Cipher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/Cipher.java
new file mode 100644
index 0000000..4f2bb47
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/Cipher.java
@@ -0,0 +1,44 @@
+/*
+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.codehaus.plexus.components.secdispatcher;
+
+/**
+ * Cipher interface.
+ *
+ * @since 4.0.1
+ */
+public interface Cipher {
+ /**
+ * Encrypts the clear text data with password and returns result. No argument is allowed to be {@code null}.
+ *
+ * @throws CipherException if encryption failed (is unexpected to happen, as it would mean that Java Runtime
+ * lacks some Crypto elements).
+ */
+ String encrypt(final String clearText, final String password) throws CipherException;
+
+ /**
+ * Decrypts the encrypted text with password and returns clear text result. No argument is allowed to be {@code null}.
+ *
+ * @throws CipherException if decryption failed. It may happen as with {@link #encrypt(String, String)} due Java
+ * Runtime lacking some Crypto elements (less likely). Most likely decrypt will fail due wrong provided password
+ * or maybe corrupted encrypted text.
+ */
+ String decrypt(final String encryptedText, final String password) throws CipherException;
+}
diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/CipherException.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/CipherException.java
new file mode 100644
index 0000000..781ba37
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/CipherException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2008 Sonatype, Inc. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.codehaus.plexus.components.secdispatcher;
+
+/**
+ * Exception thrown by {@link Cipher}.
+ *
+ * @since 4.0.1
+ */
+public class CipherException extends SecDispatcherException {
+ public CipherException(String message) {
+ super(message);
+ }
+
+ public CipherException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java
index fde9fa9..3361e96 100644
--- a/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java
+++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/SecDispatcher.java
@@ -64,9 +64,23 @@ public interface SecDispatcher {
String decrypt(String str) throws SecDispatcherException, IOException;
/**
- * Returns {@code true} if passed in string contains "legacy" password (Maven3 kind).
+ * Returns {@code true} if passed in string adheres to "encrypted string" format (current or legacy).
+ *
+ * @since 4.0.1
+ */
+ default boolean isAnyEncryptedString(String str) {
+ return isEncryptedString(str) || isLegacyEncryptedString(str);
+ }
+
+ /**
+ * Returns {@code true} if passed in string adheres "encrypted string" format.
+ */
+ boolean isEncryptedString(String str);
+
+ /**
+ * Returns {@code true} if passed in string adheres to "legacy encrypted string" format.
*/
- boolean isLegacyPassword(String str);
+ boolean isLegacyEncryptedString(String str);
/**
* Reads the effective configuration, eventually creating new instance if not present.
diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java
index 37f4dbd..8cf5668 100644
--- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java
+++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcher.java
@@ -25,8 +25,6 @@
import java.util.StringTokenizer;
import java.util.stream.Collectors;
-import org.codehaus.plexus.components.cipher.PlexusCipher;
-import org.codehaus.plexus.components.cipher.PlexusCipherException;
import org.codehaus.plexus.components.secdispatcher.Dispatcher;
import org.codehaus.plexus.components.secdispatcher.DispatcherMeta;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
@@ -48,15 +46,15 @@
* @author Oleg Gusakov
*/
public class DefaultSecDispatcher implements SecDispatcher {
+ public static final String SHIELD_BEGIN = "{";
+ public static final String SHIELD_END = "}";
public static final String ATTR_START = "[";
public static final String ATTR_STOP = "]";
- protected final PlexusCipher cipher;
protected final Map dispatchers;
protected final Path configurationFile;
- public DefaultSecDispatcher(PlexusCipher cipher, Map dispatchers, Path configurationFile) {
- this.cipher = requireNonNull(cipher);
+ public DefaultSecDispatcher(Map dispatchers, Path configurationFile) {
this.dispatchers = requireNonNull(dispatchers);
this.configurationFile = requireNonNull(configurationFile);
@@ -100,66 +98,90 @@ public Collection fields() {
@Override
public String encrypt(String str, Map attr) throws SecDispatcherException, IOException {
if (isEncryptedString(str)) return str;
-
- try {
- if (attr == null) {
- attr = new HashMap<>();
- } else {
- attr = new HashMap<>(attr);
+ if (attr == null) {
+ attr = new HashMap<>();
+ } else {
+ attr = new HashMap<>(attr);
+ }
+ if (attr.get(DISPATCHER_NAME_ATTR) == null) {
+ SettingsSecurity conf = readConfiguration(false);
+ if (conf == null) {
+ throw new SecDispatcherException("No configuration found");
}
- if (attr.get(DISPATCHER_NAME_ATTR) == null) {
- SettingsSecurity conf = readConfiguration(false);
- if (conf == null) {
- throw new SecDispatcherException("No configuration found");
- }
- String defaultDispatcher = conf.getDefaultDispatcher();
- if (defaultDispatcher == null) {
- throw new SecDispatcherException("No defaultDispatcher set in configuration");
- }
- attr.put(DISPATCHER_NAME_ATTR, defaultDispatcher);
+ String defaultDispatcher = conf.getDefaultDispatcher();
+ if (defaultDispatcher == null) {
+ throw new SecDispatcherException("No defaultDispatcher set in configuration");
}
- String name = attr.get(DISPATCHER_NAME_ATTR);
- Dispatcher dispatcher = dispatchers.get(name);
- if (dispatcher == null) throw new SecDispatcherException("No dispatcher exist with name " + name);
- Dispatcher.EncryptPayload payload = dispatcher.encrypt(str, attr, prepareDispatcherConfig(name));
- HashMap resultAttributes = new HashMap<>(payload.getAttributes());
- resultAttributes.put(SecDispatcher.DISPATCHER_NAME_ATTR, name);
- resultAttributes.put(SecDispatcher.DISPATCHER_VERSION_ATTR, SecUtil.specVersion());
- String res = ATTR_START
- + resultAttributes.entrySet().stream()
- .map(e -> e.getKey() + "=" + e.getValue())
- .collect(Collectors.joining(","))
- + ATTR_STOP;
- res += payload.getEncrypted();
- return cipher.decorate(res);
- } catch (PlexusCipherException e) {
- throw new SecDispatcherException(e.getMessage(), e);
+ attr.put(DISPATCHER_NAME_ATTR, defaultDispatcher);
}
+ String name = attr.get(DISPATCHER_NAME_ATTR);
+ Dispatcher dispatcher = dispatchers.get(name);
+ if (dispatcher == null) throw new SecDispatcherException("No dispatcher exist with name " + name);
+ Dispatcher.EncryptPayload payload = dispatcher.encrypt(str, attr, prepareDispatcherConfig(name));
+ HashMap resultAttributes = new HashMap<>(payload.getAttributes());
+ resultAttributes.put(SecDispatcher.DISPATCHER_NAME_ATTR, name);
+ resultAttributes.put(SecDispatcher.DISPATCHER_VERSION_ATTR, SecUtil.specVersion());
+ return SHIELD_BEGIN
+ + ATTR_START
+ + resultAttributes.entrySet().stream()
+ .map(e -> e.getKey() + "=" + e.getValue())
+ .collect(Collectors.joining(","))
+ + ATTR_STOP
+ + payload.getEncrypted()
+ + SHIELD_END;
}
@Override
public String decrypt(String str) throws SecDispatcherException, IOException {
if (!isEncryptedString(str)) return str;
- try {
- String bare = cipher.unDecorate(str);
- Map attr = requireNonNull(stripAttributes(bare));
- if (isLegacyPassword(str)) {
- attr.put(DISPATCHER_NAME_ATTR, LegacyDispatcher.NAME);
- }
- String name = attr.get(DISPATCHER_NAME_ATTR);
- Dispatcher dispatcher = dispatchers.get(name);
- if (dispatcher == null) throw new SecDispatcherException("No dispatcher exist with name " + name);
- return dispatcher.decrypt(strip(bare), attr, prepareDispatcherConfig(name));
- } catch (PlexusCipherException e) {
- throw new SecDispatcherException(e.getMessage(), e);
+ String bare = unDecorate(str);
+ Map attr = requireNonNull(stripAttributes(bare));
+ if (isLegacyEncryptedString(str)) {
+ attr.put(DISPATCHER_NAME_ATTR, LegacyDispatcher.NAME);
}
+ String name = attr.get(DISPATCHER_NAME_ATTR);
+ Dispatcher dispatcher = dispatchers.get(name);
+ if (dispatcher == null) throw new SecDispatcherException("No dispatcher exist with name " + name);
+ return dispatcher.decrypt(strip(bare), attr, prepareDispatcherConfig(name));
}
+ /**
+ *
+ * - Current: {[name=master,cipher=AES/GCM/NoPadding,version=4.0]vvq66pZ7rkvzSPStGTI9q4QDnsmuDwo+LtjraRel2b0XpcGJFdXcYAHAS75HUA6GLpcVtEkmyQ==}
+ *
+ */
@Override
- public boolean isLegacyPassword(String str) {
- if (!isEncryptedString(str)) return false;
- Map attr = requireNonNull(stripAttributes(cipher.unDecorate(str)));
- return !attr.containsKey(DISPATCHER_NAME_ATTR);
+ public boolean isEncryptedString(String str) {
+ boolean looksLike = str != null
+ && !str.isBlank()
+ && str.startsWith(SHIELD_BEGIN)
+ && str.endsWith(SHIELD_END)
+ && !unDecorate(str).contains(SHIELD_BEGIN)
+ && !unDecorate(str).contains(SHIELD_END);
+ if (looksLike) {
+ Map attributes = stripAttributes(unDecorate(str));
+ return attributes.containsKey(DISPATCHER_NAME_ATTR) && attributes.containsKey(DISPATCHER_VERSION_ATTR);
+ }
+ return false;
+ }
+
+ /**
+ *
+ * - Legacy: {jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}
+ *
+ */
+ @Override
+ public boolean isLegacyEncryptedString(String str) {
+ boolean looksLike = str != null
+ && !str.isBlank()
+ && str.startsWith(SHIELD_BEGIN)
+ && str.endsWith(SHIELD_END)
+ && !unDecorate(str).contains(SHIELD_BEGIN)
+ && !unDecorate(str).contains(SHIELD_END);
+ if (looksLike) {
+ return stripAttributes(unDecorate(str)).isEmpty();
+ }
+ return false;
}
@Override
@@ -284,7 +306,7 @@ protected Map stripAttributes(String str) {
return result;
}
- protected boolean isEncryptedString(String str) {
- return cipher.isEncryptedString(str);
+ protected String unDecorate(String str) {
+ return str.substring(SHIELD_BEGIN.length(), str.length() - SHIELD_END.length());
}
}
diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/AESGCMNoPadding.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/AESGCMNoPadding.java
new file mode 100644
index 0000000..5acf1f6
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/AESGCMNoPadding.java
@@ -0,0 +1,107 @@
+/*
+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.codehaus.plexus.components.secdispatcher.internal.cipher;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Base64;
+
+import org.codehaus.plexus.components.secdispatcher.CipherException;
+
+@Singleton
+@Named(AESGCMNoPadding.CIPHER_ALG)
+public class AESGCMNoPadding implements org.codehaus.plexus.components.secdispatcher.Cipher {
+ public static final String CIPHER_ALG = "AES/GCM/NoPadding";
+
+ private static final int TAG_LENGTH_BIT = 128;
+ private static final int IV_LENGTH_BYTE = 12;
+ private static final int SALT_LENGTH_BYTE = 16;
+ private static final int PBE_ITERATIONS = 310000;
+ private static final int PBE_KEY_SIZE = SALT_LENGTH_BYTE * 16;
+ private static final String KEY_FACTORY = "PBKDF2WithHmacSHA512";
+ private static final String KEY_ALGORITHM = "AES";
+
+ @Override
+ public String encrypt(String clearText, String password) throws CipherException {
+ try {
+ byte[] salt = getRandomNonce(SALT_LENGTH_BYTE);
+ byte[] iv = getRandomNonce(IV_LENGTH_BYTE);
+ SecretKey secretKey = getAESKeyFromPassword(password.toCharArray(), salt);
+ Cipher cipher = Cipher.getInstance(CIPHER_ALG);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
+ byte[] cipherText = cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8));
+ byte[] cipherTextWithIvSalt = ByteBuffer.allocate(iv.length + salt.length + cipherText.length)
+ .put(iv)
+ .put(salt)
+ .put(cipherText)
+ .array();
+ return Base64.getEncoder().encodeToString(cipherTextWithIvSalt);
+ } catch (Exception e) {
+ throw new CipherException("Failed encrypting", e);
+ }
+ }
+
+ @Override
+ public String decrypt(String encryptedText, String password) throws CipherException {
+ try {
+ byte[] material = Base64.getDecoder().decode(encryptedText.getBytes(StandardCharsets.UTF_8));
+ ByteBuffer buffer = ByteBuffer.wrap(material);
+ byte[] iv = new byte[IV_LENGTH_BYTE];
+ buffer.get(iv);
+ byte[] salt = new byte[SALT_LENGTH_BYTE];
+ buffer.get(salt);
+ byte[] cipherText = new byte[buffer.remaining()];
+ buffer.get(cipherText);
+ SecretKey secretKey = getAESKeyFromPassword(password.toCharArray(), salt);
+ Cipher cipher = Cipher.getInstance(CIPHER_ALG);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
+ byte[] plainText = cipher.doFinal(cipherText);
+ return new String(plainText, StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ throw new CipherException("Failed decrypting", e);
+ }
+ }
+
+ private static byte[] getRandomNonce(int numBytes) throws NoSuchAlgorithmException {
+ byte[] nonce = new byte[numBytes];
+ SecureRandom.getInstanceStrong().nextBytes(nonce);
+ return nonce;
+ }
+
+ private static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+ SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_FACTORY);
+ KeySpec spec = new PBEKeySpec(password, salt, PBE_ITERATIONS, PBE_KEY_SIZE);
+ return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), KEY_ALGORITHM);
+ }
+}
diff --git a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcher.java b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcher.java
index 74daabe..942ff22 100644
--- a/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcher.java
+++ b/src/main/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcher.java
@@ -38,8 +38,7 @@
import java.util.List;
import java.util.Map;
-import org.codehaus.plexus.components.cipher.PlexusCipher;
-import org.codehaus.plexus.components.cipher.PlexusCipherException;
+import org.codehaus.plexus.components.secdispatcher.CipherException;
import org.codehaus.plexus.components.secdispatcher.Dispatcher;
import org.codehaus.plexus.components.secdispatcher.DispatcherMeta;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
@@ -56,12 +55,10 @@ public class LegacyDispatcher implements Dispatcher, DispatcherMeta {
private static final String MASTER_MASTER_PASSWORD = "settings.security";
- private final PlexusCipher plexusCipher;
private final LegacyCipher legacyCipher;
@Inject
- public LegacyDispatcher(PlexusCipher plexusCipher) {
- this.plexusCipher = plexusCipher;
+ public LegacyDispatcher() {
this.legacyCipher = new LegacyCipher();
}
@@ -95,15 +92,11 @@ public EncryptPayload encrypt(String str, Map attributes, Map attributes, Map config)
throws SecDispatcherException {
- try {
- String masterPassword = getMasterPassword();
- if (masterPassword == null) {
- throw new SecDispatcherException("Master password could not be obtained");
- }
- return legacyCipher.decrypt64(str, masterPassword);
- } catch (PlexusCipherException e) {
- throw new SecDispatcherException("Decrypt failed", e);
+ String masterPassword = getMasterPassword();
+ if (masterPassword == null) {
+ throw new SecDispatcherException("Master password could not be obtained");
}
+ return legacyCipher.decrypt64(str, masterPassword);
}
@Override
@@ -129,7 +122,7 @@ public SecDispatcher.ValidationResponse validateConfiguration(Map new ArrayList<>())
.add("Legacy master password decryption failed");
}
@@ -137,11 +130,16 @@ public SecDispatcher.ValidationResponse validateConfiguration(Map masterCiphers;
protected final Map masterSources;
@Inject
- public MasterDispatcher(PlexusCipher cipher, Map masterSources) {
- this.cipher = cipher;
+ public MasterDispatcher(Map masterCiphers, Map masterSources) {
+ this.masterCiphers = masterCiphers;
this.masterSources = masterSources;
}
@@ -94,7 +93,7 @@ public Collection fields() {
Field.builder(CONF_MASTER_CIPHER)
.optional(false)
.description("Cipher to use with master password")
- .options(cipher.availableCiphers().stream()
+ .options(masterCiphers.keySet().stream()
.map(c -> Field.builder(c).description(c).build())
.toList())
.build());
@@ -103,26 +102,18 @@ public Collection fields() {
@Override
public EncryptPayload encrypt(String str, Map attributes, Map config)
throws SecDispatcherException {
- try {
- String masterCipher = getMasterCipher(config, true);
- String encrypted = cipher.encrypt(masterCipher, str, getMasterPassword(config));
- HashMap attr = new HashMap<>(attributes);
- attr.put(MASTER_CIPHER_ATTR, masterCipher);
- return new EncryptPayload(attr, encrypted);
- } catch (PlexusCipherException e) {
- throw new SecDispatcherException("Encrypt failed", e);
- }
+ String masterCipher = getMasterCipher(config, true);
+ String encrypted = requireCipher(masterCipher).encrypt(str, getMasterPassword(config));
+ HashMap attr = new HashMap<>(attributes);
+ attr.put(MASTER_CIPHER_ATTR, masterCipher);
+ return new EncryptPayload(attr, encrypted);
}
@Override
public String decrypt(String str, Map attributes, Map config)
throws SecDispatcherException {
- try {
- String masterCipher = getMasterCipher(attributes, false);
- return cipher.decrypt(masterCipher, str, getMasterPassword(config));
- } catch (PlexusCipherException e) {
- throw new SecDispatcherException("Decrypt failed", e);
- }
+ String masterCipher = getMasterCipher(attributes, false);
+ return requireCipher(masterCipher).decrypt(str, getMasterPassword(config));
}
@Override
@@ -135,7 +126,7 @@ public SecDispatcher.ValidationResponse validateConfiguration(Map new ArrayList<>())
.add("Cipher configuration missing");
} else {
- if (!cipher.availableCiphers().contains(masterCipher)) {
+ if (!masterCiphers.containsKey(masterCipher)) {
report.computeIfAbsent(SecDispatcher.ValidationResponse.Level.ERROR, k -> new ArrayList<>())
.add("Configured Cipher not supported");
} else {
@@ -173,7 +164,7 @@ public SecDispatcher.ValidationResponse validateConfiguration(Map config) throws SecDispatcherException {
+ protected String getMasterPassword(Map config) throws SecDispatcherException {
String masterSource = config.get(CONF_MASTER_SOURCE);
if (masterSource == null) {
throw new SecDispatcherException("Invalid configuration: Missing configuration " + CONF_MASTER_SOURCE);
@@ -185,7 +176,7 @@ private String getMasterPassword(Map config) throws SecDispatche
throw new SecDispatcherException("No source handled the given masterSource: " + masterSource);
}
- private String getMasterCipher(Map source, boolean config) throws SecDispatcherException {
+ protected String getMasterCipher(Map source, boolean config) throws SecDispatcherException {
if (config) {
String masterCipher = source.get(CONF_MASTER_CIPHER);
if (masterCipher == null) {
@@ -200,4 +191,12 @@ private String getMasterCipher(Map source, boolean config) throw
return masterCipher;
}
}
+
+ protected Cipher requireCipher(String name) {
+ Cipher masterCipher = masterCiphers.get(name);
+ if (masterCipher == null) {
+ throw new SecDispatcherException("No cipher exist with name " + name);
+ }
+ return masterCipher;
+ }
}
diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java
index d1e947b..c520b4e 100644
--- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java
+++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/DefaultSecDispatcherTest.java
@@ -20,9 +20,8 @@
import java.nio.file.Paths;
import java.util.Map;
-import org.codehaus.plexus.components.cipher.internal.AESGCMNoPadding;
-import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher;
import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
+import org.codehaus.plexus.components.secdispatcher.internal.cipher.AESGCMNoPadding;
import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.LegacyDispatcher;
import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.MasterDispatcher;
import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterSource;
@@ -35,6 +34,7 @@
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DefaultSecDispatcherTest {
@@ -93,20 +93,50 @@ void validate() throws Exception {
assertEquals(1, response.getSubsystems().get(0).getReport().size());
assertEquals(1, response.getSubsystems().get(0).getSubsystems().size());
// master source
- assertTrue(response.getSubsystems()
+ assertEquals(
+ 1,
+ response.getSubsystems()
.get(0)
.getSubsystems()
.get(0)
.getReport()
- .size()
- == 1);
- assertTrue(response.getSubsystems()
+ .size());
+ assertEquals(
+ 0,
+ response.getSubsystems()
.get(0)
.getSubsystems()
.get(0)
.getSubsystems()
- .size()
- == 0);
+ .size());
+ }
+
+ @Test
+ void detection() {
+ SecDispatcher secDispatcher = construct();
+ assertFalse(secDispatcher.isAnyEncryptedString(null));
+ assertFalse(secDispatcher.isAnyEncryptedString(""));
+ assertFalse(secDispatcher.isAnyEncryptedString("foo"));
+
+ assertFalse(secDispatcher.isEncryptedString("{foo}"));
+ assertTrue(secDispatcher.isLegacyEncryptedString("{foo}"));
+
+ assertFalse(secDispatcher.isEncryptedString("{12345678901234567890123456789012345678901234567890}"));
+ assertTrue(secDispatcher.isLegacyEncryptedString("{12345678901234567890123456789012345678901234567890}"));
+
+ // contains {} in the middle
+ assertFalse(secDispatcher.isEncryptedString("{KDvsYOFLlX{}gH4LU8tvpzAGg5otiosZXvfdQq0yO86LU=}"));
+ assertFalse(secDispatcher.isLegacyEncryptedString("{KDvsYOFLlX{}gH4LU8tvpzAGg5otiosZXvfdQq0yO86LU=}"));
+
+ assertFalse(secDispatcher.isEncryptedString("{KDvsYOFLlXgH4LU8tvpzAGg5otiosZXvfdQq0yO86LU=}"));
+ assertTrue(secDispatcher.isLegacyEncryptedString("{KDvsYOFLlXgH4LU8tvpzAGg5otiosZXvfdQq0yO86LU=}"));
+
+ assertTrue(
+ secDispatcher.isEncryptedString(
+ "{[name=master,cipher=AES/GCM/NoPadding,version=4.0,a=b]vvq66pZ7rkvzSPStGTI9q4QDnsmuDwo+LtjraRel2b0XpcGJFdXcYAHAS75HUA6GLpcVtEkmyQ==}"));
+ assertFalse(
+ secDispatcher.isLegacyEncryptedString(
+ "{[name=master,cipher=AES/GCM/NoPadding,version=4.0,a=b]vvq66pZ7rkvzSPStGTI9q4QDnsmuDwo+LtjraRel2b0XpcGJFdXcYAHAS75HUA6GLpcVtEkmyQ==}"));
}
protected void roundtrip() throws Exception {
@@ -126,13 +156,11 @@ protected void roundtrip() throws Exception {
}
protected DefaultSecDispatcher construct() {
- DefaultPlexusCipher dpc = new DefaultPlexusCipher(Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding()));
return new DefaultSecDispatcher(
- dpc,
Map.of(
"master",
new MasterDispatcher(
- dpc,
+ Map.of(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding()),
Map.of(
EnvMasterSource.NAME,
new EnvMasterSource(),
@@ -141,7 +169,7 @@ protected DefaultSecDispatcher construct() {
GpgAgentMasterSource.NAME,
new GpgAgentMasterSource())),
"legacy",
- new LegacyDispatcher(dpc)),
+ new LegacyDispatcher()),
CONFIG_PATH);
}
}
diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/AESGCMNoPaddingTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/AESGCMNoPaddingTest.java
new file mode 100644
index 0000000..e711b87
--- /dev/null
+++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/AESGCMNoPaddingTest.java
@@ -0,0 +1,10 @@
+package org.codehaus.plexus.components.secdispatcher.internal.cipher;
+
+import org.codehaus.plexus.components.secdispatcher.Cipher;
+
+public class AESGCMNoPaddingTest extends CipherTestSupport {
+ @Override
+ Cipher getCipher() {
+ return new AESGCMNoPadding();
+ }
+}
diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/CipherTestSupport.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/CipherTestSupport.java
new file mode 100644
index 0000000..9e8693f
--- /dev/null
+++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/cipher/CipherTestSupport.java
@@ -0,0 +1,66 @@
+/*
+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.codehaus.plexus.components.secdispatcher.internal.cipher;
+
+import org.codehaus.plexus.components.secdispatcher.Cipher;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public abstract class CipherTestSupport {
+ final String clearText = "veryOpenText";
+ final String password = "testtest";
+
+ Cipher pbeCipher;
+
+ @BeforeEach
+ void prepare() {
+ pbeCipher = getCipher();
+ }
+
+ abstract Cipher getCipher();
+
+ @Test
+ void testEncrypt() throws Exception {
+ String enc = pbeCipher.encrypt(clearText, password);
+ assertNotNull(enc);
+ String enc2 = pbeCipher.encrypt(clearText, password);
+ assertNotNull(enc2);
+ assertNotEquals(enc, enc2);
+ }
+
+ @Test
+ void testDecrypt() throws Exception {
+ String enc = pbeCipher.encrypt(clearText, password);
+ String clear = pbeCipher.decrypt(enc, password);
+ assertEquals(clearText, clear);
+ }
+
+ @Test
+ void testEncoding() throws Exception {
+ String pwd = "äüöÜÖÄæøåčćžšđß\"§$%&/()=?é";
+ String encPwd = pbeCipher.encrypt(pwd, pwd);
+ String decPwd = pbeCipher.decrypt(encPwd, pwd);
+ assertEquals(pwd, decPwd);
+ }
+}
diff --git a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcherTest.java b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcherTest.java
index f02e758..62336ea 100644
--- a/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcherTest.java
+++ b/src/test/java/org/codehaus/plexus/components/secdispatcher/internal/dispatchers/LegacyDispatcherTest.java
@@ -15,7 +15,6 @@
import java.util.Map;
-import org.codehaus.plexus.components.cipher.internal.DefaultPlexusCipher;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -36,7 +35,7 @@ public class LegacyDispatcherTest {
})
void smoke(String xml) {
System.setProperty("settings.security", xml);
- LegacyDispatcher legacyDispatcher = new LegacyDispatcher(new DefaultPlexusCipher(Map.of()));
+ LegacyDispatcher legacyDispatcher = new LegacyDispatcher();
// SecDispatcher "un decorates" the PW
String cleartext = legacyDispatcher.decrypt("L6L/HbmrY+cH+sNkphnq3fguYepTpM04WlIXb8nB1pk=", Map.of(), Map.of());
assertEquals("password", cleartext);