Skip to content
This repository was archived by the owner on Nov 24, 2024. It is now read-only.

Commit c30abd2

Browse files
authored
Componentize (#48)
Make `Cipher` (plexus, not Java) pluggable components, `PlexusCipher` component is just "shell" gathering then and having some extra functions like "decorating". Dropped insecure AES/CBC/PKCS5Padding and introduced AES/GCM/NoPadding instead.
1 parent b8d5b1f commit c30abd2

File tree

9 files changed

+292
-363
lines changed

9 files changed

+292
-363
lines changed

.github/workflows/codeql-analysis.yml

-38
This file was deleted.

src/main/java/org/codehaus/plexus/components/cipher/PlexusCipher.java

+38-36
Original file line numberDiff line numberDiff line change
@@ -12,74 +12,76 @@
1212
*/
1313
package org.codehaus.plexus.components.cipher;
1414

15+
import java.util.Set;
16+
1517
/**
1618
* @author Oleg Gusakov
1719
*/
1820
public interface PlexusCipher {
21+
22+
/**
23+
* Returns the available cipher algorithms, never {@code null}.
24+
*/
25+
Set<String> availableCiphers();
26+
1927
/**
20-
* encrypt given string with the given passPhrase and encode it into base64
28+
* Encrypt given string with the given alg and passPhrase and encode it into Base64 string.
2129
*
22-
* @param str string to encrypt
23-
* @param passPhrase pass phrase
24-
* @return encrypted str
30+
* @param alg cipher alg to use, never {@code null}
31+
* @param str string to encrypt, never {@code null}
32+
* @param passPhrase pass phrase, never {@code null}
33+
* @return encrypted str, never {@code null}
2534
* @throws PlexusCipherException if encryption fails
2635
*/
27-
String encrypt(String str, String passPhrase) throws PlexusCipherException;
36+
String encrypt(String alg, String str, String passPhrase) throws PlexusCipherException;
2837

2938
/**
30-
* encrypt given string with the given passPhrase, encode it into base64 and return result, wrapped into { }
31-
* decorations
39+
* Encrypt given string with the given alg and passPhrase and encode it into Base64 decorated string.
3240
*
33-
* @param str string to encrypt
34-
* @param passPhrase pass phrase
35-
* @return encrypted and decorated str
41+
* @param alg cipher alg to use, never {@code null}
42+
* @param str string to encrypt, never {@code null}
43+
* @param passPhrase pass phrase, never {@code null}
44+
* @return encrypted and decorated str, never {@code null}
3645
* @throws PlexusCipherException if encryption fails
3746
*/
38-
String encryptAndDecorate(String str, String passPhrase) throws PlexusCipherException;
47+
String encryptAndDecorate(String alg, String str, String passPhrase) throws PlexusCipherException;
3948

4049
/**
41-
* decrypt given base64 encrypted string
50+
* Decrypt given Base64 encoded string with the given alg and passPhrase and return resulting string.
4251
*
43-
* @param str base64 encoded string
44-
* @param passPhrase pass phrase
45-
* @return decrypted str
46-
* @throws PlexusCipherException if decryption fails
52+
* @param alg cipher alg to use, never {@code null}
53+
* @param str string to encrypt, never {@code null}
54+
* @param passPhrase pass phrase, never {@code null}
55+
* @return encrypted and decorated str, never {@code null}
56+
* @throws PlexusCipherException if encryption fails
4757
*/
48-
String decrypt(String str, String passPhrase) throws PlexusCipherException;
58+
String decrypt(String alg, String str, String passPhrase) throws PlexusCipherException;
4959

5060
/**
51-
* decrypt given base64 encoded encrypted string. If string is decorated, decrypt base64 encoded string inside
52-
* decorations
61+
* Decrypt given decorated Base64 encoded string with the given alg and passPhrase and return resulting string.
5362
*
54-
* @param str base64 encoded string
55-
* @param passPhrase pass phrase
56-
* @return decrypted decorated str
57-
* @throws PlexusCipherException if decryption fails
63+
* @param alg cipher alg to use, never {@code null}
64+
* @param str string to encrypt, never {@code null}
65+
* @param passPhrase pass phrase, never {@code null}
66+
* @return encrypted and decorated str, never {@code null}
67+
* @throws PlexusCipherException if encryption fails
5868
*/
59-
String decryptDecorated(String str, String passPhrase) throws PlexusCipherException;
69+
String decryptDecorated(String alg, String str, String passPhrase) throws PlexusCipherException;
6070

6171
/**
62-
* check if given string is decorated
63-
*
64-
* @param str string to check
65-
* @return true if string is encrypted
72+
* Check if given string is decorated.
6673
*/
6774
boolean isEncryptedString(String str);
6875

6976
/**
70-
* return string inside decorations
77+
* Remove decorations from string, if it was decorated.
7178
*
72-
* @param str decorated string
73-
* @return undecorated str
74-
* @throws PlexusCipherException if decryption fails
79+
* @throws PlexusCipherException is string is malformed
7580
*/
7681
String unDecorate(String str) throws PlexusCipherException;
7782

7883
/**
79-
* decorated given string with { and }
80-
*
81-
* @param str string to decorate
82-
* @return decorated str
84+
* Decorates given string.
8385
*/
8486
String decorate(String str);
8587
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
*/
19+
20+
package org.codehaus.plexus.components.cipher.internal;
21+
22+
import javax.crypto.Cipher;
23+
import javax.crypto.SecretKey;
24+
import javax.crypto.SecretKeyFactory;
25+
import javax.crypto.spec.GCMParameterSpec;
26+
import javax.crypto.spec.PBEKeySpec;
27+
import javax.crypto.spec.SecretKeySpec;
28+
import javax.inject.Named;
29+
import javax.inject.Singleton;
30+
31+
import java.nio.ByteBuffer;
32+
import java.nio.charset.StandardCharsets;
33+
import java.security.NoSuchAlgorithmException;
34+
import java.security.SecureRandom;
35+
import java.security.spec.InvalidKeySpecException;
36+
import java.security.spec.KeySpec;
37+
import java.util.Base64;
38+
39+
import org.codehaus.plexus.components.cipher.PlexusCipherException;
40+
41+
@Singleton
42+
@Named(AESGCMNoPadding.CIPHER_ALG)
43+
public class AESGCMNoPadding implements org.codehaus.plexus.components.cipher.internal.Cipher {
44+
public static final String CIPHER_ALG = "AES/GCM/NoPadding";
45+
46+
private static final int TAG_LENGTH_BIT = 128;
47+
private static final int IV_LENGTH_BYTE = 12;
48+
private static final int SALT_LENGTH_BYTE = 16;
49+
private static final int PBE_ITERATIONS = 310000;
50+
private static final int PBE_KEY_SIZE = SALT_LENGTH_BYTE * 16;
51+
private static final String KEY_FACTORY = "PBKDF2WithHmacSHA512";
52+
private static final String KEY_ALGORITHM = "AES";
53+
54+
@Override
55+
public String encrypt(String clearText, String password) throws PlexusCipherException {
56+
try {
57+
byte[] salt = getRandomNonce(SALT_LENGTH_BYTE);
58+
byte[] iv = getRandomNonce(IV_LENGTH_BYTE);
59+
SecretKey secretKey = getAESKeyFromPassword(password.toCharArray(), salt);
60+
Cipher cipher = Cipher.getInstance(CIPHER_ALG);
61+
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
62+
byte[] cipherText = cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8));
63+
byte[] cipherTextWithIvSalt = ByteBuffer.allocate(iv.length + salt.length + cipherText.length)
64+
.put(iv)
65+
.put(salt)
66+
.put(cipherText)
67+
.array();
68+
return Base64.getEncoder().encodeToString(cipherTextWithIvSalt);
69+
} catch (Exception e) {
70+
throw new PlexusCipherException("Failed encrypting", e);
71+
}
72+
}
73+
74+
@Override
75+
public String decrypt(String encryptedText, String password) throws PlexusCipherException {
76+
try {
77+
byte[] material = Base64.getDecoder().decode(encryptedText.getBytes(StandardCharsets.UTF_8));
78+
ByteBuffer buffer = ByteBuffer.wrap(material);
79+
byte[] iv = new byte[IV_LENGTH_BYTE];
80+
buffer.get(iv);
81+
byte[] salt = new byte[SALT_LENGTH_BYTE];
82+
buffer.get(salt);
83+
byte[] cipherText = new byte[buffer.remaining()];
84+
buffer.get(cipherText);
85+
SecretKey secretKey = getAESKeyFromPassword(password.toCharArray(), salt);
86+
Cipher cipher = Cipher.getInstance(CIPHER_ALG);
87+
cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
88+
byte[] plainText = cipher.doFinal(cipherText);
89+
return new String(plainText, StandardCharsets.UTF_8);
90+
} catch (Exception e) {
91+
throw new PlexusCipherException("Failed decrypting", e);
92+
}
93+
}
94+
95+
private static byte[] getRandomNonce(int numBytes) throws NoSuchAlgorithmException {
96+
byte[] nonce = new byte[numBytes];
97+
SecureRandom.getInstanceStrong().nextBytes(nonce);
98+
return nonce;
99+
}
100+
101+
private static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
102+
throws NoSuchAlgorithmException, InvalidKeySpecException {
103+
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_FACTORY);
104+
KeySpec spec = new PBEKeySpec(password, salt, PBE_ITERATIONS, PBE_KEY_SIZE);
105+
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), KEY_ALGORITHM);
106+
}
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
*/
19+
20+
package org.codehaus.plexus.components.cipher.internal;
21+
22+
import org.codehaus.plexus.components.cipher.PlexusCipherException;
23+
24+
/**
25+
* Cipher interface.
26+
*/
27+
public interface Cipher {
28+
/**
29+
* Encrypts the clear text data with password and returns result.
30+
*/
31+
String encrypt(final String clearText, final String password) throws PlexusCipherException;
32+
33+
/**
34+
* Decrypts the encrypted text with password and returns clear text result.
35+
*/
36+
String decrypt(final String encryptedText, final String password) throws PlexusCipherException;
37+
}

0 commit comments

Comments
 (0)