diff --git a/gradle.properties b/gradle.properties index cf8a979..d47b7fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,8 +15,8 @@ # limitations under the License. # -artefactVersion=2.2.0 -projectUrl=https://onixbyte.com/java-dev-kit +artefactVersion=2.3.0 +projectUrl=https://onixbyte.com/projects/java-dev-kit projectGithubUrl=https://github.com/onixbyte/java-dev-kit licenseName=The Apache License, Version 2.0 -licenseUrl=https://www.apache.org/licenses/LICENSE-2.0.txt \ No newline at end of file +licenseUrl=https://onixbyte.com/projects/java-dev-kit/LICENSE.txt \ No newline at end of file diff --git a/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java b/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java index b299d5a..3bb267c 100644 --- a/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java +++ b/key-pair-loader/src/main/java/com/onixbyte/security/KeyLoader.java @@ -17,8 +17,14 @@ package com.onixbyte.security; +import com.onixbyte.security.exception.KeyLoadingException; + +import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.KeySpec; /** * The {@code KeyLoader} class provides utility methods for loading keys pairs from PEM-formatted @@ -49,6 +55,41 @@ public interface KeyLoader { */ PublicKey loadPublicKey(String pemKeyText); + /** + * Loads an RSA public key using the provided modulus and exponent. + *
+ * This default implementation throws a {@link KeyLoadingException} to signify that this key loader does not support + * loading an RSA public key. Implementing classes are expected to override this method to supply their own + * loading logic. + * + * @param modulus the modulus value of the RSA public key, usually represented in hexadecimal or Base64 + * string format + * @param exponent the public exponent value of the RSA public key, usually represented in hexadecimal or Base64 + * string format + * @return the loaded {@link RSAPublicKey} instance + * @throws KeyLoadingException if loading is not supported or fails + */ + default RSAPublicKey loadPublicKey(String modulus, String exponent) { + throw new KeyLoadingException("This key loader does not support loading an RSA public key."); + } + + /** + * Loads an EC public key using the provided x and y coordinates together with the curve name. + *
+ * This default implementation throws a {@link KeyLoadingException} to signify that this key loader does not support
+ * loading an EC public key. Implementing classes are expected to override this method to supply their own
+ * loading logic.
+ *
+ * @param xHex the hexadecimal string representing the x coordinate of the EC point
+ * @param yHex the hexadecimal string representing the y coordinate of the EC point
+ * @param curveName the name of the elliptic curve
+ * @return the loaded {@link ECPublicKey} instance
+ * @throws KeyLoadingException if loading is not supported or fails
+ */
+ default ECPublicKey loadPublicKey(String xHex, String yHex, String curveName) {
+ throw new KeyLoadingException("This key loader does not support loading an EC public key.");
+ }
+
/**
* Retrieves the raw content of a PEM formatted key by removing unnecessary headers, footers,
* and new line characters.
diff --git a/key-pair-loader/src/main/java/com/onixbyte/security/impl/EcKeyLoader.java b/key-pair-loader/src/main/java/com/onixbyte/security/impl/ECKeyLoader.java
similarity index 57%
rename from key-pair-loader/src/main/java/com/onixbyte/security/impl/EcKeyLoader.java
rename to key-pair-loader/src/main/java/com/onixbyte/security/impl/ECKeyLoader.java
index 96daa58..e482c0f 100644
--- a/key-pair-loader/src/main/java/com/onixbyte/security/impl/EcKeyLoader.java
+++ b/key-pair-loader/src/main/java/com/onixbyte/security/impl/ECKeyLoader.java
@@ -20,14 +20,16 @@ package com.onixbyte.security.impl;
import com.onixbyte.security.KeyLoader;
import com.onixbyte.security.exception.KeyLoadingException;
+import java.math.BigInteger;
+import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
+import java.security.spec.*;
import java.util.Base64;
+import java.util.HashSet;
+import java.util.Set;
/**
* Key pair loader for loading key pairs for ECDSA-based algorithms.
@@ -53,16 +55,23 @@ import java.util.Base64;
* @version 2.0.0
* @since 2.0.0
*/
-public class EcKeyLoader implements KeyLoader {
+public class ECKeyLoader implements KeyLoader {
private final KeyFactory keyFactory;
private final Base64.Decoder decoder;
+ /**
+ * Supported curves.
+ */
+ public static final Set
+ * This method converts the hexadecimal string representations of the EC point coordinates into {@link BigInteger}
+ * instances, then constructs an {@link ECPoint} and retrieves the corresponding {@link ECParameterSpec} for the
+ * named curve. Subsequently, it utilises the {@link KeyFactory} to generate an {@link ECPublicKey}.
+ *
+ * Only curves listed in {@link #SUPPORTED_CURVES} are supported. Should the specified curve name be unsupported,
+ * or if key construction fails due to invalid parameters or unsupported algorithms, a {@link KeyLoadingException}
+ * will be thrown.
+ *
+ * @param xHex the hexadecimal string representing the x-coordinate of the EC point
+ * @param yHex the hexadecimal string representing the y-coordinate of the EC point
+ * @param curveName the name of the elliptic curve
+ * @return the {@link ECPublicKey} generated from the specified coordinates and curve
+ * @throws KeyLoadingException if the curve is unsupported or key generation fails
+ */
+ @Override
+ public ECPublicKey loadPublicKey(String xHex, String yHex, String curveName) {
+ if (!SUPPORTED_CURVES.contains(curveName)) {
+ throw new KeyLoadingException("Given curve is not supported yet.");
+ }
+
+ try {
+ // Convert hex string coordinates to BigInteger
+ var x = new BigInteger(xHex, 16);
+ var y = new BigInteger(yHex, 16);
+
+ // Create ECPoint with (x, y)
+ var ecPoint = new ECPoint(x, y);
+
+ // Get EC parameter spec for the named curve
+ var parameters = AlgorithmParameters.getInstance("EC");
+ parameters.init(new ECGenParameterSpec(curveName));
+ var ecParameterSpec = parameters.getParameterSpec(ECParameterSpec.class);
+
+ // Create ECPublicKeySpec with point and curve params
+ var pubSpec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
+
+ // Generate public key using KeyFactory
+ var publicKey = keyFactory.generatePublic(pubSpec);
+
+ if (publicKey instanceof ECPublicKey ecPublicKey) {
+ return ecPublicKey;
+ } else {
+ throw new KeyLoadingException("Cannot load EC public key with given x, y and curve name.");
+ }
+ } catch (NoSuchAlgorithmException | InvalidParameterSpecException | InvalidKeySpecException e) {
+ throw new KeyLoadingException("Cannot load EC public key with given x, y and curve name.", e);
+ }
+ }
}
diff --git a/key-pair-loader/src/main/java/com/onixbyte/security/impl/RsaKeyLoader.java b/key-pair-loader/src/main/java/com/onixbyte/security/impl/RSAKeyLoader.java
similarity index 78%
rename from key-pair-loader/src/main/java/com/onixbyte/security/impl/RsaKeyLoader.java
rename to key-pair-loader/src/main/java/com/onixbyte/security/impl/RSAKeyLoader.java
index 7f1b6e4..3cf9324 100644
--- a/key-pair-loader/src/main/java/com/onixbyte/security/impl/RsaKeyLoader.java
+++ b/key-pair-loader/src/main/java/com/onixbyte/security/impl/RSAKeyLoader.java
@@ -20,13 +20,12 @@ package com.onixbyte.security.impl;
import com.onixbyte.security.KeyLoader;
import com.onixbyte.security.exception.KeyLoadingException;
+import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
+import java.security.spec.*;
import java.util.Base64;
/**
@@ -44,9 +43,12 @@ import java.util.Base64;
* @see KeyLoader
* @see KeyLoadingException
*/
-public class RsaKeyLoader implements KeyLoader {
+public class RSAKeyLoader implements KeyLoader {
private final Base64.Decoder decoder;
+
+ private final Base64.Decoder urlDecoder;
+
private final KeyFactory keyFactory;
/**
@@ -55,9 +57,10 @@ public class RsaKeyLoader implements KeyLoader {
* This constructor initialises the Base64 decoder and the RSA {@link KeyFactory}. It may throw
* a {@link KeyLoadingException} if the RSA algorithm is not available.
*/
- public RsaKeyLoader() {
+ public RSAKeyLoader() {
try {
this.decoder = Base64.getDecoder();
+ this.urlDecoder = Base64.getUrlDecoder();
this.keyFactory = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new KeyLoadingException(e);
@@ -133,4 +136,31 @@ public class RsaKeyLoader implements KeyLoader {
throw new KeyLoadingException("Key spec is invalid.", e);
}
}
+
+ /**
+ * Get the public key with given modulus and public exponent.
+ *
+ * @param modulus the modulus
+ * @param exponent the public exponent
+ * @return generated public key object from the provided key specification
+ * @see KeyFactory#getInstance(String)
+ * @see KeyFactory#generatePublic(KeySpec)
+ */
+ @Override
+ public RSAPublicKey loadPublicKey(String modulus, String exponent) {
+ try {
+ var _modulus = new BigInteger(1, urlDecoder.decode(modulus));
+ var _exponent = new BigInteger(1, urlDecoder.decode(exponent));
+
+ var keySpec = new RSAPublicKeySpec(_modulus, _exponent);
+ var kf = KeyFactory.getInstance("RSA");
+ if (kf.generatePublic(keySpec) instanceof RSAPublicKey rsaPublicKey) {
+ return rsaPublicKey;
+ } else {
+ throw new KeyLoadingException("Cannot generate RSA public key with given modulus and exponent.");
+ }
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new KeyLoadingException("Cannot generate RSA public key with given modulus and exponent.", e);
+ }
+ }
}
diff --git a/map-util-unsafe/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java b/map-util-unsafe/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java
index 3d98e15..73fe722 100644
--- a/map-util-unsafe/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java
+++ b/map-util-unsafe/src/main/java/com/onixbyte/devkit/utils/unsafe/ReflectMapUtil.java
@@ -33,27 +33,27 @@ import java.util.Map;
* as maps for serialization, deserialization, or other purposes, and where the structure of the
* objects is not known at compile time.
* Example usage:
- * Spring Boot applications often need to store sensitive configuration details such as database
- * passwords, API keys, etc. To ensure that these sensitive pieces of information are not exposed
- * to the public, developers can utilize the {@code PropertyGuard} class to encrypt and store them
- * within configuration files.
- *
- * Usage
- * You need a 16-char long secret for encrypting a configuration property. You can get this secret
- * on your own, or use the helper utility class by the following code:
- *
- * Then, in {@code application.yml} or {@code application.properties}, change the original value
- * from plain text to encrypted value with the prefix "
- * This class is extracted from MyBatis-Plus.
- *
- * @author hubin@baomidou
- * @version 1.1.0
- * @see EnvironmentPostProcessor
- * @since 1.1.0 (3.3.2 of MyBatis-Plus)
- */
-@Deprecated(forRemoval = true)
-public class PropertyGuard implements EnvironmentPostProcessor {
-
- private final static Logger log = LoggerFactory.getLogger(PropertyGuard.class);
-
- /**
- * Create a {@link PropertyGuard} instance.
- */
- public PropertyGuard() {
- }
-
- /**
- * Process the encryption environment variables.
- *
- * @param environment the environment to post-process
- * @param application the application to which the environment belongs
- */
- @Override
- public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
- // Get the key for encryption from command line.
- var encryptionKey = "";
- for (var ps : environment.getPropertySources()) {
- if (ps instanceof SimpleCommandLinePropertySource source) {
- encryptionKey = source.getProperty("%s.key".formatted(PREFIX));
- break;
- }
- }
-
- if (Optional.ofNullable(encryptionKey).map((key) -> !key.isEmpty()).orElse(false)) {
- var map = new HashMap
* {@code
* public class User {
* private String name;
* private int age;
- *
+ *
* // getters and setters
* }
- *
+ *
* public class Example {
* public static void main(String[] args) throws IllegalAccessException {
* User user = new User();
* user.setName("John");
* user.setAge(30);
- *
+ *
* // Convert object to map
* Map{@code
- * var secret = AesUtil.generateRandomSecret(); // Let's presume the result is "3856faef0d2d4f33"
- * }
- * pg:".
- * {@code
- * # original
- * app.example-properties=Sample Value
- *
- * # encrypted with key 3856faef0d2d4f33
- * app.example-properties=pg:t4YBfv8M9ZmTzWgTi2gJqg==
- * }
- * After that, before running, you need to add the command line arguments "pg.key" as the following
- * codes: {@code --pg.key=