diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 32cbb59..0000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,38 +0,0 @@ - -name: "CodeQL" - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - workflow_dispatch: - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: 'java' - - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - - diff --git a/src/main/java/org/codehaus/plexus/components/cipher/PlexusCipher.java b/src/main/java/org/codehaus/plexus/components/cipher/PlexusCipher.java index 390633b..53971ab 100644 --- a/src/main/java/org/codehaus/plexus/components/cipher/PlexusCipher.java +++ b/src/main/java/org/codehaus/plexus/components/cipher/PlexusCipher.java @@ -12,74 +12,76 @@ */ package org.codehaus.plexus.components.cipher; +import java.util.Set; + /** * @author Oleg Gusakov */ public interface PlexusCipher { + + /** + * Returns the available cipher algorithms, never {@code null}. + */ + Set availableCiphers(); + /** - * encrypt given string with the given passPhrase and encode it into base64 + * Encrypt given string with the given alg and passPhrase and encode it into Base64 string. * - * @param str string to encrypt - * @param passPhrase pass phrase - * @return encrypted str + * @param alg cipher alg to use, never {@code null} + * @param str string to encrypt, never {@code null} + * @param passPhrase pass phrase, never {@code null} + * @return encrypted str, never {@code null} * @throws PlexusCipherException if encryption fails */ - String encrypt(String str, String passPhrase) throws PlexusCipherException; + String encrypt(String alg, String str, String passPhrase) throws PlexusCipherException; /** - * encrypt given string with the given passPhrase, encode it into base64 and return result, wrapped into { } - * decorations + * Encrypt given string with the given alg and passPhrase and encode it into Base64 decorated string. * - * @param str string to encrypt - * @param passPhrase pass phrase - * @return encrypted and decorated str + * @param alg cipher alg to use, never {@code null} + * @param str string to encrypt, never {@code null} + * @param passPhrase pass phrase, never {@code null} + * @return encrypted and decorated str, never {@code null} * @throws PlexusCipherException if encryption fails */ - String encryptAndDecorate(String str, String passPhrase) throws PlexusCipherException; + String encryptAndDecorate(String alg, String str, String passPhrase) throws PlexusCipherException; /** - * decrypt given base64 encrypted string + * Decrypt given Base64 encoded string with the given alg and passPhrase and return resulting string. * - * @param str base64 encoded string - * @param passPhrase pass phrase - * @return decrypted str - * @throws PlexusCipherException if decryption fails + * @param alg cipher alg to use, never {@code null} + * @param str string to encrypt, never {@code null} + * @param passPhrase pass phrase, never {@code null} + * @return encrypted and decorated str, never {@code null} + * @throws PlexusCipherException if encryption fails */ - String decrypt(String str, String passPhrase) throws PlexusCipherException; + String decrypt(String alg, String str, String passPhrase) throws PlexusCipherException; /** - * decrypt given base64 encoded encrypted string. If string is decorated, decrypt base64 encoded string inside - * decorations + * Decrypt given decorated Base64 encoded string with the given alg and passPhrase and return resulting string. * - * @param str base64 encoded string - * @param passPhrase pass phrase - * @return decrypted decorated str - * @throws PlexusCipherException if decryption fails + * @param alg cipher alg to use, never {@code null} + * @param str string to encrypt, never {@code null} + * @param passPhrase pass phrase, never {@code null} + * @return encrypted and decorated str, never {@code null} + * @throws PlexusCipherException if encryption fails */ - String decryptDecorated(String str, String passPhrase) throws PlexusCipherException; + String decryptDecorated(String alg, String str, String passPhrase) throws PlexusCipherException; /** - * check if given string is decorated - * - * @param str string to check - * @return true if string is encrypted + * Check if given string is decorated. */ boolean isEncryptedString(String str); /** - * return string inside decorations + * Remove decorations from string, if it was decorated. * - * @param str decorated string - * @return undecorated str - * @throws PlexusCipherException if decryption fails + * @throws PlexusCipherException is string is malformed */ String unDecorate(String str) throws PlexusCipherException; /** - * decorated given string with { and } - * - * @param str string to decorate - * @return decorated str + * Decorates given string. */ String decorate(String str); } diff --git a/src/main/java/org/codehaus/plexus/components/cipher/internal/AESGCMNoPadding.java b/src/main/java/org/codehaus/plexus/components/cipher/internal/AESGCMNoPadding.java new file mode 100644 index 0000000..e9b1e32 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/cipher/internal/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.cipher.internal; + +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.cipher.PlexusCipherException; + +@Singleton +@Named(AESGCMNoPadding.CIPHER_ALG) +public class AESGCMNoPadding implements org.codehaus.plexus.components.cipher.internal.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 PlexusCipherException { + 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 PlexusCipherException("Failed encrypting", e); + } + } + + @Override + public String decrypt(String encryptedText, String password) throws PlexusCipherException { + 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 PlexusCipherException("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/cipher/internal/Cipher.java b/src/main/java/org/codehaus/plexus/components/cipher/internal/Cipher.java new file mode 100644 index 0000000..970c53f --- /dev/null +++ b/src/main/java/org/codehaus/plexus/components/cipher/internal/Cipher.java @@ -0,0 +1,37 @@ +/* +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.cipher.internal; + +import org.codehaus.plexus.components.cipher.PlexusCipherException; + +/** + * Cipher interface. + */ +public interface Cipher { + /** + * Encrypts the clear text data with password and returns result. + */ + String encrypt(final String clearText, final String password) throws PlexusCipherException; + + /** + * Decrypts the encrypted text with password and returns clear text result. + */ + String decrypt(final String encryptedText, final String password) throws PlexusCipherException; +} diff --git a/src/main/java/org/codehaus/plexus/components/cipher/internal/DefaultPlexusCipher.java b/src/main/java/org/codehaus/plexus/components/cipher/internal/DefaultPlexusCipher.java index 328abf5..b56bedc 100644 --- a/src/main/java/org/codehaus/plexus/components/cipher/internal/DefaultPlexusCipher.java +++ b/src/main/java/org/codehaus/plexus/components/cipher/internal/DefaultPlexusCipher.java @@ -12,12 +12,15 @@ */ package org.codehaus.plexus.components.cipher.internal; +import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import java.security.Provider; import java.security.Security; +import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -25,6 +28,8 @@ import org.codehaus.plexus.components.cipher.PlexusCipher; import org.codehaus.plexus.components.cipher.PlexusCipherException; +import static java.util.Objects.requireNonNull; + /** * Default implementation of {@link PlexusCipher}. This class is thread safe. * @@ -33,72 +38,75 @@ @Singleton @Named public class DefaultPlexusCipher implements PlexusCipher { - private static final Pattern ENCRYPTED_STRING_PATTERN = Pattern.compile(".*?[^\\\\]?\\{(.*?[^\\\\])\\}.*"); + private static final Pattern ENCRYPTED_STRING_PATTERN = Pattern.compile(".*?[^\\\\]?\\{(.*?[^\\\\])}.*"); private static final String ENCRYPTED_STRING_DECORATION_START = "{"; private static final String ENCRYPTED_STRING_DECORATION_STOP = "}"; - private final PBECipher _cipher; + private final Map ciphers; - // --------------------------------------------------------------- - public DefaultPlexusCipher() { - _cipher = new PBECipher(); + @Inject + public DefaultPlexusCipher(Map ciphers) { + this.ciphers = requireNonNull(ciphers); } - // --------------------------------------------------------------- @Override - public String encrypt(final String str, final String passPhrase) throws PlexusCipherException { - if (str == null || str.isEmpty()) { + public Set availableCiphers() { + return Collections.unmodifiableSet(ciphers.keySet()); + } + + @Override + public String encrypt(String alg, String str, String passPhrase) throws PlexusCipherException { + requireNonNull(alg); + requireNonNull(str); + requireNonNull(passPhrase); + if (str.isEmpty()) { return str; } - - return _cipher.encrypt64(str, passPhrase); + return requireCipher(alg).encrypt(str, passPhrase); } - // --------------------------------------------------------------- @Override - public String encryptAndDecorate(final String str, final String passPhrase) throws PlexusCipherException { - return decorate(encrypt(str, passPhrase)); + public String encryptAndDecorate(String alg, String str, String passPhrase) throws PlexusCipherException { + return decorate(encrypt(alg, str, passPhrase)); } - // --------------------------------------------------------------- @Override - public String decrypt(final String str, final String passPhrase) throws PlexusCipherException { - if (str == null || str.isEmpty()) { + public String decrypt(String alg, String str, String passPhrase) throws PlexusCipherException { + requireNonNull(alg); + requireNonNull(str); + requireNonNull(passPhrase); + if (str.isEmpty()) { return str; } - - return _cipher.decrypt64(str, passPhrase); + return requireCipher(alg).decrypt(str, passPhrase); } - // --------------------------------------------------------------- @Override - public String decryptDecorated(final String str, final String passPhrase) throws PlexusCipherException { - if (str == null || str.isEmpty()) { + public String decryptDecorated(String alg, String str, String passPhrase) throws PlexusCipherException { + requireNonNull(alg); + requireNonNull(str); + requireNonNull(passPhrase); + if (str.isEmpty()) { return str; } - if (isEncryptedString(str)) { - return decrypt(unDecorate(str), passPhrase); + str = unDecorate(str); } - - return decrypt(str, passPhrase); + return decrypt(alg, str, passPhrase); } - // ---------------------------------------------------------------------------- @Override - public boolean isEncryptedString(final String str) { + public boolean isEncryptedString(String str) { if (str == null || str.isEmpty()) { return false; } - Matcher matcher = ENCRYPTED_STRING_PATTERN.matcher(str); - return matcher.matches() || matcher.find(); } - // ---------------------------------------------------------------------------- @Override - public String unDecorate(final String str) throws PlexusCipherException { + public String unDecorate(String str) throws PlexusCipherException { + requireNonNull(str); Matcher matcher = ENCRYPTED_STRING_PATTERN.matcher(str); if (matcher.matches() || matcher.find()) { return matcher.group(1); @@ -107,12 +115,19 @@ public String unDecorate(final String str) throws PlexusCipherException { } } - // ---------------------------------------------------------------------------- @Override - public String decorate(final String str) { + public String decorate(String str) { return ENCRYPTED_STRING_DECORATION_START + (str == null ? "" : str) + ENCRYPTED_STRING_DECORATION_STOP; } + private Cipher requireCipher(String alg) throws PlexusCipherException { + Cipher cipher = ciphers.get(alg); + if (cipher == null) { + throw new PlexusCipherException("Unsupported alg: " + alg); + } + return cipher; + } + // --------------------------------------------------------------- /** @@ -166,7 +181,6 @@ public static String[] getCryptoImpls(final String serviceType) { return result.toArray(new String[0]); } - // --------------------------------------------------------------- public static void main(final String[] args) { String[] serviceTypes = getServiceTypes(); for (String serviceType : serviceTypes) { diff --git a/src/main/java/org/codehaus/plexus/components/cipher/internal/PBECipher.java b/src/main/java/org/codehaus/plexus/components/cipher/internal/PBECipher.java deleted file mode 100644 index c8a3838..0000000 --- a/src/main/java/org/codehaus/plexus/components/cipher/internal/PBECipher.java +++ /dev/null @@ -1,144 +0,0 @@ -/* -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.cipher.internal; - -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.SecretKeySpec; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -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.cipher.PlexusCipherException; - -/** - * This class is thread-safe. - * - * @author Oleg Gusakov - */ -public class PBECipher { - protected static final Charset STRING_ENCODING = StandardCharsets.UTF_8; - protected static final int SPICE_SIZE = 16; - protected static final int SALT_SIZE = 8; - protected static final int CHUNK_SIZE = 16; - protected static final String KEY_ALG = "AES"; - protected static final String CIPHER_ALG = "AES/CBC/PKCS5Padding"; - protected static final int PBE_ITERATIONS = 310000; - private static final SecureRandom _secureRandom = new SecureRandom(); - - // --------------------------------------------------------------- - private byte[] getSalt(final int sz) { - byte[] res = new byte[sz]; - - _secureRandom.nextBytes(res); - - return res; - } - // ------------------------------------------------------------------------------- - public String encrypt64(final String clearText, final String password) throws PlexusCipherException { - try { - byte[] clearBytes = clearText.getBytes(STRING_ENCODING); - - byte[] salt = getSalt(SALT_SIZE); - - Cipher cipher = createCipher(password.toCharArray(), salt, Cipher.ENCRYPT_MODE); - - byte[] encryptedBytes = cipher.doFinal(clearBytes); - - int len = encryptedBytes.length; - - byte padLen = (byte) (CHUNK_SIZE - (SALT_SIZE + len + 1) % CHUNK_SIZE); - - int totalLen = SALT_SIZE + len + padLen + 1; - - byte[] allEncryptedBytes = getSalt(totalLen); - - System.arraycopy(salt, 0, allEncryptedBytes, 0, SALT_SIZE); - - allEncryptedBytes[SALT_SIZE] = padLen; - - System.arraycopy(encryptedBytes, 0, allEncryptedBytes, SALT_SIZE + 1, len); - - return Base64.getEncoder().encodeToString(allEncryptedBytes); - } catch (Exception e) { - throw new PlexusCipherException(e.getMessage(), e); - } - } - - // ------------------------------------------------------------------------------- - public String decrypt64(final String encryptedText, final String password) throws PlexusCipherException { - try { - byte[] allEncryptedBytes = Base64.getDecoder().decode(encryptedText.getBytes()); - - int totalLen = allEncryptedBytes.length; - - byte[] salt = new byte[SALT_SIZE]; - - System.arraycopy(allEncryptedBytes, 0, salt, 0, SALT_SIZE); - - byte padLen = allEncryptedBytes[SALT_SIZE]; - - byte[] encryptedBytes = new byte[totalLen - SALT_SIZE - 1 - padLen]; - - System.arraycopy(allEncryptedBytes, SALT_SIZE + 1, encryptedBytes, 0, encryptedBytes.length); - - Cipher cipher = createCipher(password.toCharArray(), salt, Cipher.DECRYPT_MODE); - - byte[] clearBytes = cipher.doFinal(encryptedBytes); - - return new String(clearBytes, STRING_ENCODING); - } catch (Exception e) { - throw new PlexusCipherException(e.getMessage(), e); - } - } - // ------------------------------------------------------------------------------- - private Cipher createCipher(final char[] pwd, byte[] salt, final int mode) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, - InvalidAlgorithmParameterException, InvalidKeySpecException { - - KeySpec spec = new PBEKeySpec(pwd, salt, PBE_ITERATIONS, SPICE_SIZE * 16); - SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); - byte[] keyAndIv = factory.generateSecret(spec).getEncoded(); - - byte[] key = new byte[SPICE_SIZE]; - - byte[] iv = new byte[SPICE_SIZE]; - - System.arraycopy(keyAndIv, 0, key, 0, key.length); - - System.arraycopy(keyAndIv, key.length, iv, 0, iv.length); - - Cipher cipher = Cipher.getInstance(CIPHER_ALG); - - cipher.init(mode, new SecretKeySpec(key, KEY_ALG), new IvParameterSpec(iv)); - - return cipher; - } -} diff --git a/src/test/java/org/codehaus/plexus/components/cipher/internal/AESGCMNoPaddingTest.java b/src/test/java/org/codehaus/plexus/components/cipher/internal/AESGCMNoPaddingTest.java new file mode 100644 index 0000000..7d2294b --- /dev/null +++ b/src/test/java/org/codehaus/plexus/components/cipher/internal/AESGCMNoPaddingTest.java @@ -0,0 +1,8 @@ +package org.codehaus.plexus.components.cipher.internal; + +public class AESGCMNoPaddingTest extends CipherTestSupport { + @Override + Cipher getCipher() { + return new AESGCMNoPadding(); + } +} diff --git a/src/test/java/org/codehaus/plexus/components/cipher/internal/PBECipherTest.java b/src/test/java/org/codehaus/plexus/components/cipher/internal/CipherTestSupport.java similarity index 73% rename from src/test/java/org/codehaus/plexus/components/cipher/internal/PBECipherTest.java rename to src/test/java/org/codehaus/plexus/components/cipher/internal/CipherTestSupport.java index e263005..6ac820b 100644 --- a/src/test/java/org/codehaus/plexus/components/cipher/internal/PBECipherTest.java +++ b/src/test/java/org/codehaus/plexus/components/cipher/internal/CipherTestSupport.java @@ -19,6 +19,8 @@ Licensed to the Apache Software Foundation (ASF) under one package org.codehaus.plexus.components.cipher.internal; +import java.nio.charset.Charset; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,54 +28,43 @@ Licensed to the Apache Software Foundation (ASF) under one import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -/** - * @author Oleg Gusakov - */ -class PBECipherTest { - PBECipher pbeCipher; - +public abstract class CipherTestSupport { final String clearText = "veryOpenText"; - - final String encryptedText = "xnQ1RvJFoJsHoTZKyv76ej3XTGKt99ShUt/kPv4yHjw="; - final String password = "testtest"; + Cipher pbeCipher; + @BeforeEach void prepare() { - pbeCipher = new PBECipher(); + pbeCipher = getCipher(); } + abstract Cipher getCipher(); + @Test void testEncrypt() throws Exception { - String enc = pbeCipher.encrypt64(clearText, password); - + String enc = pbeCipher.encrypt(clearText, password); assertNotNull(enc); - System.out.println(enc); - - String enc2 = pbeCipher.encrypt64(clearText, password); - + String enc2 = pbeCipher.encrypt(clearText, password); assertNotNull(enc2); - System.out.println(enc2); - assertNotEquals(enc, enc2); } @Test void testDecrypt() throws Exception { - String clear = pbeCipher.decrypt64(encryptedText, password); - + String enc = pbeCipher.encrypt(clearText, password); + String clear = pbeCipher.decrypt(enc, password); assertEquals(clearText, clear); } @Test void testEncoding() throws Exception { - System.out.println("file.encoding=" + System.getProperty("file.encoding")); - + System.out.println("file.encoding=" + Charset.defaultCharset().displayName()); String pwd = "äüöÜÖÄß\"§$%&/()=?é"; - String encPwd = pbeCipher.encrypt64(pwd, pwd); - String decPwd = pbeCipher.decrypt64(encPwd, 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/cipher/internal/DefaultPlexusCipherTest.java b/src/test/java/org/codehaus/plexus/components/cipher/internal/DefaultPlexusCipherTest.java index 100acda..46f1c2c 100644 --- a/src/test/java/org/codehaus/plexus/components/cipher/internal/DefaultPlexusCipherTest.java +++ b/src/test/java/org/codehaus/plexus/components/cipher/internal/DefaultPlexusCipherTest.java @@ -12,16 +12,20 @@ */ package org.codehaus.plexus.components.cipher.internal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + import org.codehaus.plexus.components.cipher.PlexusCipher; import org.codehaus.plexus.components.cipher.PlexusCipherException; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.FieldSource; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -31,15 +35,22 @@ */ class DefaultPlexusCipherTest { private final String passPhrase = "testtest"; - final String str = "my testing phrase"; - - final String encStr = "RRvejxJ+wksH/kWnYfun/GeFoPKh6JHcA2dmxMOIraZiIuLISplmdyvl2Sq04rpP"; PlexusCipher pc; + static String[] ALG = new String[] {AESGCMNoPadding.CIPHER_ALG}; + @BeforeEach void prepare() { - pc = new DefaultPlexusCipher(); + HashMap ciphers = new HashMap<>(); + ciphers.put(AESGCMNoPadding.CIPHER_ALG, new AESGCMNoPadding()); + pc = new DefaultPlexusCipher(ciphers); + } + + @Test + void testAvailableCiphers() { + HashSet wanted = new HashSet<>(Arrays.asList(ALG)); + assertEquals(wanted, pc.availableCiphers()); } @Test @@ -48,13 +59,9 @@ void testIsEncryptedString() { String normalBraces = "Comment {This is a test} other comment with a: }"; String escapedBraces = "\\{This is a test\\}"; String mixedBraces = "Comment {foo\\{This is a test\\}} other comment with a: }"; - assertFalse(pc.isEncryptedString(noBraces)); - assertTrue(pc.isEncryptedString(normalBraces)); - assertFalse(pc.isEncryptedString(escapedBraces)); - assertTrue(pc.isEncryptedString(mixedBraces)); } @@ -63,116 +70,61 @@ void testUnDecorate_BracesPermutations() throws PlexusCipherException { String noBraces = "This is a test"; String normalBraces = "Comment {This is a test} other comment with a: }"; String mixedBraces = "Comment {foo\\{This is a test\\}} other comment with a: }"; - assertEquals(noBraces, pc.unDecorate(normalBraces)); - assertEquals("foo\\{" + noBraces + "\\}", pc.unDecorate(mixedBraces)); } - // ------------------------------------------------------------- - - @Test - void testDefaultAlgorithmExists() throws Exception { - String[] res = DefaultPlexusCipher.getCryptoImpls("Cipher"); - assertNotNull(res, "No Cipher providers found in the current environment"); - - System.out.println("\n=== Available ciphers :"); - for (String re : res) { - System.out.println(re); - } - System.out.println("===================="); - - for (String provider : res) { - if (PBECipher.KEY_ALG.equalsIgnoreCase(provider)) return; - } - - throw new Exception("Cannot find default algorithm " + PBECipher.KEY_ALG + " in the current environment."); - } - - // ------------------------------------------------------------- - - @Disabled("This test is not really a test") - @Test - void stestFindDefaultAlgorithm() { - String[] res = DefaultPlexusCipher.getServiceTypes(); - assertNotNull(res, "No service types found in the current environment"); - - String[] impls = DefaultPlexusCipher.getCryptoImpls("Cipher"); - assertNotNull(impls, "No Cipher providers found in the current environment"); - - for (String impl : impls) - try { - System.out.print(impl); - pc.encrypt(str, passPhrase); - System.out.println("------------------> Success !!!!!!"); - } catch (Exception e) { - System.out.println(e.getMessage()); - } - } - - // ------------------------------------------------------------- - @Test - void testEncrypt() throws Exception { - String xRes = pc.encrypt(str, passPhrase); - - System.out.println(xRes); - - String res = pc.decrypt(xRes, passPhrase); - + @ParameterizedTest + @FieldSource("ALG") + void testEncrypt(String alg) throws Exception { + String xRes = pc.encrypt(alg, str, passPhrase); + // System.out.println(xRes); + String res = pc.decrypt(alg, xRes, passPhrase); assertEquals(str, res, "Encryption/Decryption did not produce desired result"); } - // ------------------------------------------------------------- - - @Test - void testEncryptVariableLengths() throws Exception { + @ParameterizedTest + @FieldSource("ALG") + void testEncryptVariableLengths(String alg) throws Exception { String pass = "g"; - for (int i = 0; i < 64; i++) { pass = pass + 'a'; - - String xRes = pc.encrypt(str, pass); - - System.out.println(pass.length() + ": " + xRes); - - String res = pc.decrypt(xRes, pass); - + String xRes = pc.encrypt(alg, str, pass); + // System.out.println(pass.length() + ": " + xRes); + String res = pc.decrypt(alg, xRes, pass); assertEquals(str, res, "Encryption/Decryption did not produce desired result"); } } - @Test - void testDecrypt() { + @ParameterizedTest + @FieldSource("ALG") + void testDecrypt(String alg) { assertDoesNotThrow( () -> { - String res = pc.decrypt(encStr, passPhrase); + String encStr = pc.encrypt(alg, str, passPhrase); + String res = pc.decrypt(alg, encStr, passPhrase); assertEquals(str, res, "Decryption did not produce desired result"); }, "Decryption failed: "); } - // ------------------------------------------------------------- - @Test void testDecorate() { String res = pc.decorate("aaa"); assertEquals("{aaa}", res, "Decoration failed"); } - // ------------------------------------------------------------- - @Test void testUnDecorate() throws Exception { String res = pc.unDecorate("{aaa}"); assertEquals("aaa", res, "Decoration failed"); } - // ------------------------------------------------------------- - - @Test - void testEncryptAndDecorate() throws Exception { - String res = pc.encryptAndDecorate("my-password", "12345678"); - + @ParameterizedTest + @FieldSource("ALG") + void testEncryptAndDecorate(String alg) throws Exception { + String res = pc.encryptAndDecorate(alg, "my-password", "12345678"); assertEquals('{', res.charAt(0)); + assertEquals('}', res.charAt(res.length() - 1)); } }