Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
<version>2.0.16</version>
<optional>true</optional>
</dependency>
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/codelibs/core/collection/ArrayMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ public V getValue() {

@Override
public V setValue(final V value) {
final V oldValue = value;
final V oldValue = this.value;
this.value = value;
return oldValue;
}
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/codelibs/core/collection/LruHashMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@

/**
* {@link HashMap} with an upper limit on the number of entries. When a new entry is added, the oldest entry is discarded using LRU if the limit is exceeded.
* <p>
* <strong>Thread-Safety:</strong> This class is <strong>NOT thread-safe</strong>.
* It extends {@link LinkedHashMap} without synchronization. If multiple threads access
* an instance concurrently, and at least one thread modifies the map structurally,
* it must be synchronized externally.
* </p>
* <p>
* For thread-safe usage, wrap with {@link java.util.Collections#synchronizedMap(Map)}:
* </p>
* <pre>
* Map&lt;K, V&gt; syncMap = Collections.synchronizedMap(new LruHashMap&lt;&gt;(100));
* </pre>
* <p>
* Alternatively, for high-concurrency scenarios, consider implementing a custom
* LRU cache using {@link java.util.concurrent.ConcurrentHashMap} with an eviction strategy.
* </p>
*
* @author koichik
* @param <K> the key type
Expand Down
163 changes: 149 additions & 14 deletions src/main/java/org/codelibs/core/crypto/CachedCipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,51 @@
import org.codelibs.core.misc.Base64Util;

/**
* A utility class for encrypting and decrypting data using a cached {@link Cipher} instance.
* A high-performance utility class for encrypting and decrypting data using cached {@link Cipher} instances.
* <p>
* This class provides efficient encryption/decryption by pooling and reusing cipher instances,
* reducing the overhead of repeated cipher initialization. It supports both string-based keys
* and {@link Key} objects, with configurable algorithms and character encodings.
* </p>
* <p>
* <strong>Key Features:</strong>
* </p>
* <ul>
* <li>Thread-safe cipher pooling using {@link ConcurrentLinkedQueue}</li>
* <li>Configurable encryption algorithms (default: Blowfish)</li>
* <li>Proper charset handling for key generation (UTF-8 by default)</li>
* <li>Base64 encoding for text operations</li>
* </ul>
* <p>
* <strong>Security Considerations:</strong>
* </p>
* <ul>
* <li>Default Blowfish algorithm is suitable for general-purpose encryption</li>
* <li>For high-security applications, consider using AES with GCM mode via {@link #setAlgorithm(String)} and {@link #setTransformation(String)}</li>
* <li>Ensure keys are securely generated and stored</li>
* <li>For production systems with stringent security requirements, consider using authenticated encryption modes</li>
* </ul>
* <p>
* <strong>Usage Example:</strong>
* </p>
* <pre>
* CachedCipher cipher = new CachedCipher();
* cipher.setKey("mySecretKey");
*
* // Encrypt text
* String encrypted = cipher.encryptText("Hello World");
*
* // Decrypt text
* String decrypted = cipher.decryptText(encrypted);
*
* // For AES encryption
* CachedCipher aesCipher = new CachedCipher();
* aesCipher.setAlgorithm("AES");
* aesCipher.setTransformation("AES");
* aesCipher.setKey("0123456789abcdef"); // 16-byte key for AES-128
* </pre>
*
* @author higa
*/
public class CachedCipher {

Expand All @@ -50,17 +94,22 @@ public CachedCipher() {

private static final String BLOWFISH = "Blowfish";

private static final String RSA = "RSA";
private static final String AES = "AES";

/**
* The algorithm to use for the cipher.
* Default is Blowfish for backward compatibility.
*/
protected String algorithm = BLOWFISH;

/**
* The transformation to use for the cipher.
* The transformation to use for the cipher when using Key objects.
* Default is Blowfish to match the algorithm default.
* <p>
* Note: For better security, consider using "AES/GCM/NoPadding" with proper IV handling.
* </p>
*/
protected String transformation = RSA;
protected String transformation = BLOWFISH;

/**
* The key to use for encryption/decryption.
Expand Down Expand Up @@ -89,7 +138,7 @@ public CachedCipher() {
* the data to encrypt
* @return the encrypted data
*/
public byte[] encrypto(final byte[] data) {
public byte[] encrypt(final byte[] data) {
final Cipher cipher = pollEncryptoCipher();
byte[] encrypted;
try {
Expand All @@ -104,6 +153,19 @@ public byte[] encrypto(final byte[] data) {
return encrypted;
}

/**
* Encrypts the given data.
*
* @param data
* the data to encrypt
* @return the encrypted data
* @deprecated Use {@link #encrypt(byte[])} instead. This method name contains a typo.
*/
@Deprecated
public byte[] encrypto(final byte[] data) {
return encrypt(data);
}

/**
* Encrypts the given data with the specified key.
*
Expand All @@ -113,7 +175,7 @@ public byte[] encrypto(final byte[] data) {
* the key to use for encryption
* @return the encrypted data
*/
public byte[] encrypto(final byte[] data, final Key key) {
public byte[] encrypt(final byte[] data, final Key key) {
final Cipher cipher = pollEncryptoCipher(key);
byte[] encrypted;
try {
Expand All @@ -128,29 +190,57 @@ public byte[] encrypto(final byte[] data, final Key key) {
return encrypted;
}

/**
* Encrypts the given data with the specified key.
*
* @param data
* the data to encrypt
* @param key
* the key to use for encryption
* @return the encrypted data
* @deprecated Use {@link #encrypt(byte[], Key)} instead. This method name contains a typo.
*/
@Deprecated
public byte[] encrypto(final byte[] data, final Key key) {
return encrypt(data, key);
}

/**
* Encrypts the given text.
*
* @param text
* the text to encrypt
* @return the encrypted text
*/
public String encryptoText(final String text) {
public String encryptText(final String text) {
try {
return Base64Util.encode(encrypto(text.getBytes(charsetName)));
return Base64Util.encode(encrypt(text.getBytes(charsetName)));
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedEncodingRuntimeException(e);
}
}

/**
* Encrypts the given text.
*
* @param text
* the text to encrypt
* @return the encrypted text
* @deprecated Use {@link #encryptText(String)} instead. This method name contains a typo.
*/
@Deprecated
public String encryptoText(final String text) {
return encryptText(text);
}

/**
* Decrypts the given data.
*
* @param data
* the data to decrypt
* @return the decrypted data
*/
public byte[] decrypto(final byte[] data) {
public byte[] decrypt(final byte[] data) {
final Cipher cipher = pollDecryptoCipher();
byte[] decrypted;
try {
Expand All @@ -165,6 +255,19 @@ public byte[] decrypto(final byte[] data) {
return decrypted;
}

/**
* Decrypts the given data.
*
* @param data
* the data to decrypt
* @return the decrypted data
* @deprecated Use {@link #decrypt(byte[])} instead. This method name contains a typo.
*/
@Deprecated
public byte[] decrypto(final byte[] data) {
return decrypt(data);
}

/**
* Decrypts the given data with the specified key.
*
Expand All @@ -174,7 +277,7 @@ public byte[] decrypto(final byte[] data) {
* the key to use for decryption
* @return the decrypted data
*/
public byte[] decrypto(final byte[] data, final Key key) {
public byte[] decrypt(final byte[] data, final Key key) {
final Cipher cipher = pollDecryptoCipher(key);
byte[] decrypted;
try {
Expand All @@ -189,21 +292,49 @@ public byte[] decrypto(final byte[] data, final Key key) {
return decrypted;
}

/**
* Decrypts the given data with the specified key.
*
* @param data
* the data to decrypt
* @param key
* the key to use for decryption
* @return the decrypted data
* @deprecated Use {@link #decrypt(byte[], Key)} instead. This method name contains a typo.
*/
@Deprecated
public byte[] decrypto(final byte[] data, final Key key) {
return decrypt(data, key);
}

/**
* Decrypts the given text.
*
* @param text
* the text to decrypt
* @return the decrypted text
*/
public String decryptoText(final String text) {
public String decryptText(final String text) {
try {
return new String(decrypto(Base64Util.decode(text)), charsetName);
return new String(decrypt(Base64Util.decode(text)), charsetName);
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedEncodingRuntimeException(e);
}
}

/**
* Decrypts the given text.
*
* @param text
* the text to decrypt
* @return the decrypted text
* @deprecated Use {@link #decryptText(String)} instead. This method name contains a typo.
*/
@Deprecated
public String decryptoText(final String text) {
return decryptText(text);
}

/**
* Polls an encryption cipher from the queue, creating a new one if none are available.
*
Expand All @@ -212,8 +343,8 @@ public String decryptoText(final String text) {
protected Cipher pollEncryptoCipher() {
Cipher cipher = encryptoQueue.poll();
if (cipher == null) {
final SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), algorithm);
try {
final SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(charsetName), algorithm);
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, sksSpec);
} catch (final InvalidKeyException e) {
Expand All @@ -222,6 +353,8 @@ protected Cipher pollEncryptoCipher() {
throw new NoSuchAlgorithmRuntimeException(e);
} catch (final NoSuchPaddingException e) {
throw new NoSuchPaddingRuntimeException(e);
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedEncodingRuntimeException(e);
}
}
return cipher;
Expand Down Expand Up @@ -269,8 +402,8 @@ protected void offerEncryptoCipher(final Cipher cipher) {
protected Cipher pollDecryptoCipher() {
Cipher cipher = decryptoQueue.poll();
if (cipher == null) {
final SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), algorithm);
try {
final SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(charsetName), algorithm);
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, sksSpec);
} catch (final InvalidKeyException e) {
Expand All @@ -279,6 +412,8 @@ protected Cipher pollDecryptoCipher() {
throw new NoSuchAlgorithmRuntimeException(e);
} catch (final NoSuchPaddingException e) {
throw new NoSuchPaddingRuntimeException(e);
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedEncodingRuntimeException(e);
}
}
return cipher;
Expand Down
Loading