From 579a2e2e354887adc67f7ad12a334230b980e875 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Wed, 4 Jun 2025 21:07:16 +0800 Subject: [PATCH 01/10] feat: remove deprecated module `property-guard-spring-boot-starter` --- property-guard-spring-boot-starter/README.md | 37 ----- .../build.gradle.kts | 132 ------------------ .../autoconfiguration/PropertyGuard.java | 122 ---------------- .../main/resources/META-INF/spring.factories | 1 - .../src/main/resources/logback.xml | 32 ----- settings.gradle.kts | 1 - 6 files changed, 325 deletions(-) delete mode 100644 property-guard-spring-boot-starter/README.md delete mode 100644 property-guard-spring-boot-starter/build.gradle.kts delete mode 100644 property-guard-spring-boot-starter/src/main/java/com/onixbyte/propertyguard/autoconfiguration/PropertyGuard.java delete mode 100644 property-guard-spring-boot-starter/src/main/resources/META-INF/spring.factories delete mode 100644 property-guard-spring-boot-starter/src/main/resources/logback.xml diff --git a/property-guard-spring-boot-starter/README.md b/property-guard-spring-boot-starter/README.md deleted file mode 100644 index 32cc31f..0000000 --- a/property-guard-spring-boot-starter/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Property Guard - -`property-guard-spring-boot-starter` is a utility that can help you protect secret values in Spring Boot configurations. - -## Example usage - -### 1. Implementation this module - -```kotlin -dependencies { - implementation(platform("com.onixbyte:devkit-bom:$devKitVersion")) - implementation("com.onixbyte:devkit-utils") - implementation("com.onixbyte:property-guard-spring-boot-starter") -} -``` - -### 2. Generate a secret - -Use the following codes to get a random secret. - -```java -@SpringBootTest -class SpringBootApplicationTest { - - @Test - void contextLoads() { - System.out.println(AesUtil.generateRandomSecret()); // Output: a 16-char long secret - } -} -``` - -Or you can write a 16-char long secret by yourself. - -### 3. Encrypt your secret properties and place them into your configuration file - -### 4. Run application with parameter `--pg.key=$your_secret` - diff --git a/property-guard-spring-boot-starter/build.gradle.kts b/property-guard-spring-boot-starter/build.gradle.kts deleted file mode 100644 index da71a45..0000000 --- a/property-guard-spring-boot-starter/build.gradle.kts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2024-2025 OnixByte. - * - * Licensed 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. - */ -import java.net.URI - -plugins { - java - id("java-library") - id("maven-publish") - id("signing") -} - -val artefactVersion: String by project -val projectUrl: String by project -val projectGithubUrl: String by project -val licenseName: String by project -val licenseUrl: String by project - -group = "com.onixbyte" -version = artefactVersion - -repositories { - mavenCentral() -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - withSourcesJar() - withJavadocJar() -} - -tasks.withType { - options.encoding = "UTF-8" -} - -tasks.withType { - exclude("logback.xml") -} - -dependencies { - compileOnly(libs.slf4j) - implementation(libs.logback) - api(project(":devkit-core")) - api(project(":devkit-utils")) - implementation(libs.springBoot.autoconfigure) - implementation(libs.springBoot.starter.logging) - implementation(libs.springBoot.configurationProcessor) - - testImplementation(platform(libs.junit.bom)) - testImplementation(libs.junit.jupiter) - testImplementation(libs.springBoot.starter.test) -} - -tasks.test { - useJUnitPlatform() -} - -publishing { - publications { - create("propertyGuardSpringBootStarter") { - groupId = group.toString() - artifactId = "property-guard-spring-boot-starter" - version = artefactVersion - - pom { - name = "Property Guard Spring Boot Starter" - description = "This module is made for protecting your secret properties in your spring application properties." - url = projectUrl - - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - scm { - connection = "scm:git:git://github.com:OnixByte/JDevKit.git" - developerConnection = "scm:git:git://github.com:OnixByte/JDevKit.git" - url = projectGithubUrl - } - - developers { - developer { - id = "zihluwang" - name = "Zihlu Wang" - email = "really@zihlu.wang" - timezone = "Asia/Hong_Kong" - } - - developer { - id = "siujamo" - name = "Siu Jam'o" - email = "jamo.siu@outlook.com" - timezone = "Asia/Shanghai" - } - } - } - - from(components["java"]) - - signing { - sign(publishing.publications["propertyGuardSpringBootStarter"]) - } - } - - repositories { - maven { - name = "sonatypeNexus" - url = URI(providers.gradleProperty("repo.maven-central.host").get()) - credentials { - username = providers.gradleProperty("repo.maven-central.username").get() - password = providers.gradleProperty("repo.maven-central.password").get() - } - } - } - } -} \ No newline at end of file diff --git a/property-guard-spring-boot-starter/src/main/java/com/onixbyte/propertyguard/autoconfiguration/PropertyGuard.java b/property-guard-spring-boot-starter/src/main/java/com/onixbyte/propertyguard/autoconfiguration/PropertyGuard.java deleted file mode 100644 index 7a182d0..0000000 --- a/property-guard-spring-boot-starter/src/main/java/com/onixbyte/propertyguard/autoconfiguration/PropertyGuard.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2024-2025 OnixByte. - * - * Licensed 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 com.onixbyte.propertyguard.autoconfiguration; - -import com.onixbyte.devkit.utils.AesUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.boot.env.OriginTrackedMapPropertySource; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.SimpleCommandLinePropertySource; - -import java.security.GeneralSecurityException; -import java.util.HashMap; -import java.util.Optional; - -/** - * {@code PropertyGuard} is a utility class designed for encrypting configuration properties in - * Spring Boot applications. - *

- * 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: - *

{@code
- * var secret = AesUtil.generateRandomSecret(); // Let's presume the result is "3856faef0d2d4f33"
- * }
- *

- * Then, in {@code application.yml} or {@code application.properties}, change the original value - * from plain text to encrypted value with the prefix "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=}. - *

- * 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(); - for (var propertySources : environment.getPropertySources()) { - if (propertySources instanceof OriginTrackedMapPropertySource source) { - for (var name : source.getPropertyNames()) { - if (source.getProperty(name) instanceof String str) { - if (str.startsWith("%s:".formatted(PREFIX))) { - try { - map.put(name, AesUtil.decrypt(str.substring(3), encryptionKey)); - } catch (GeneralSecurityException e) { - log.error("Unable to decrypt param {}", name); - } - } - } - } - } - } - // Put the decrypted data into environment variables, and made them at top-level. - if (!map.isEmpty()) { - environment.getPropertySources().addFirst(new MapPropertySource("custom-encrypt", map)); - } - } - } - - private static final String PREFIX = "pg"; -} diff --git a/property-guard-spring-boot-starter/src/main/resources/META-INF/spring.factories b/property-guard-spring-boot-starter/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 4545f73..0000000 --- a/property-guard-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.env.EnvironmentPostProcessor=com.onixbyte.propertyguard.autoconfiguration.PropertyGuard \ No newline at end of file diff --git a/property-guard-spring-boot-starter/src/main/resources/logback.xml b/property-guard-spring-boot-starter/src/main/resources/logback.xml deleted file mode 100644 index 6d26440..0000000 --- a/property-guard-spring-boot-starter/src/main/resources/logback.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - ${COLOURFUL_OUTPUT} - - - - - - \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 383643d..7d5b157 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,6 +29,5 @@ include( "simple-jwt-facade", "simple-jwt-authzero", "simple-jwt-spring-boot-starter", - "property-guard-spring-boot-starter", "simple-serial-spring-boot-starter" ) From 6ac9f1ae491684a294166c20939a3c6f89623654 Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 11:49:27 +0800 Subject: [PATCH 02/10] refactor: renamed key pair loader --- .../security/impl/{EcKeyLoader.java => ECKeyLoader.java} | 4 ++-- .../security/impl/{RsaKeyLoader.java => RSAKeyLoader.java} | 4 ++-- .../onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java | 6 ++---- 3 files changed, 6 insertions(+), 8 deletions(-) rename key-pair-loader/src/main/java/com/onixbyte/security/impl/{EcKeyLoader.java => ECKeyLoader.java} (98%) rename key-pair-loader/src/main/java/com/onixbyte/security/impl/{RsaKeyLoader.java => RSAKeyLoader.java} (98%) 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 98% 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..cdb2124 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 @@ -53,7 +53,7 @@ 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; @@ -62,7 +62,7 @@ public class EcKeyLoader implements KeyLoader { /** * Initialise a key loader for EC-based algorithms. */ - public EcKeyLoader() { + public ECKeyLoader() { try { this.keyFactory = KeyFactory.getInstance("EC"); this.decoder = Base64.getDecoder(); 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 98% 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..9520867 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 @@ -44,7 +44,7 @@ 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 KeyFactory keyFactory; @@ -55,7 +55,7 @@ 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.keyFactory = KeyFactory.getInstance("RSA"); diff --git a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java index aa5f2d3..790700c 100644 --- a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java +++ b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/AuthzeroTokenResolver.java @@ -19,8 +19,7 @@ package com.onixbyte.simplejwt.authzero; import com.onixbyte.devkit.utils.Base64Util; import com.onixbyte.guid.GuidCreator; -import com.onixbyte.security.KeyLoader; -import com.onixbyte.security.impl.EcKeyLoader; +import com.onixbyte.security.impl.ECKeyLoader; import com.onixbyte.simplejwt.TokenPayload; import com.onixbyte.simplejwt.TokenResolver; import com.onixbyte.simplejwt.annotations.ExcludeFromPayload; @@ -43,7 +42,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; -import java.security.NoSuchAlgorithmException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.time.Duration; @@ -179,7 +177,7 @@ public class AuthzeroTokenResolver implements TokenResolver { * @return the builder instance */ public Builder keyPair(String publicKey, String privateKey) { - var keyLoader = new EcKeyLoader(); + var keyLoader = new ECKeyLoader(); this.publicKey = keyLoader.loadPublicKey(publicKey); this.privateKey = keyLoader.loadPrivateKey(privateKey); return this; From e03cc180c9a97b4a7d69b329bb1583e68ea0f58a Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 11:49:57 +0800 Subject: [PATCH 03/10] feat: load RSA public key via modulus and exponent --- .../java/com/onixbyte/security/KeyLoader.java | 7 ++++++ .../onixbyte/security/impl/RSAKeyLoader.java | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+) 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..0c7a4a5 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,11 @@ package com.onixbyte.security; +import com.onixbyte.security.exception.KeyLoadingException; + import java.security.PrivateKey; import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; /** * The {@code KeyLoader} class provides utility methods for loading keys pairs from PEM-formatted @@ -49,6 +52,10 @@ public interface KeyLoader { */ PublicKey loadPublicKey(String pemKeyText); + default RSAPublicKey loadPublicKey(String modulus, String exponent) { + throw new KeyLoadingException("This key loader does not support RSA Public key loading."); + } + /** * 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/RSAKeyLoader.java b/key-pair-loader/src/main/java/com/onixbyte/security/impl/RSAKeyLoader.java index 9520867..b0f5dc2 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,12 +20,14 @@ 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.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; @@ -47,6 +49,9 @@ import java.util.Base64; public class RSAKeyLoader implements KeyLoader { private final Base64.Decoder decoder; + + private final Base64.Decoder urlDecoder; + private final KeyFactory keyFactory; /** @@ -58,6 +63,7 @@ public class RSAKeyLoader implements KeyLoader { 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 +139,22 @@ public class RSAKeyLoader implements KeyLoader { throw new KeyLoadingException("Key spec is invalid.", e); } } + + @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); + } + } } From a4d42722ad40e3f85397334f718bc86781b542a5 Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 11:52:12 +0800 Subject: [PATCH 04/10] docs: add docs for loading RSA public key with modulus and exponent --- .../main/java/com/onixbyte/security/KeyLoader.java | 11 +++++++++++ .../com/onixbyte/security/impl/RSAKeyLoader.java | 14 ++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) 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 0c7a4a5..a367ac1 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 @@ -19,9 +19,11 @@ 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.RSAPublicKey; +import java.security.spec.KeySpec; /** * The {@code KeyLoader} class provides utility methods for loading keys pairs from PEM-formatted @@ -52,6 +54,15 @@ public interface KeyLoader { */ PublicKey loadPublicKey(String pemKeyText); + /** + * 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) + */ default RSAPublicKey loadPublicKey(String modulus, String exponent) { throw new KeyLoadingException("This key loader does not support RSA Public key loading."); } 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 index b0f5dc2..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 @@ -25,10 +25,7 @@ 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.RSAPublicKeySpec; -import java.security.spec.X509EncodedKeySpec; +import java.security.spec.*; import java.util.Base64; /** @@ -140,6 +137,15 @@ public class RSAKeyLoader implements KeyLoader { } } + /** + * 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 { From 7dfd02f11e7fd24ad0a7494050a62fbd359528ed Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 12:00:43 +0800 Subject: [PATCH 05/10] refactor: fix exception message typo --- .../src/main/java/com/onixbyte/security/KeyLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a367ac1..8f3be18 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 @@ -64,7 +64,7 @@ public interface KeyLoader { * @see KeyFactory#generatePublic(KeySpec) */ default RSAPublicKey loadPublicKey(String modulus, String exponent) { - throw new KeyLoadingException("This key loader does not support RSA Public key loading."); + throw new KeyLoadingException("This key loader does not support loading an RSA public key."); } /** From 4965a9529772e67d461c4ec6e1ce14c397a95e9c Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 12:40:29 +0800 Subject: [PATCH 06/10] feat: load EC public key with given x, y and curve name --- .../java/com/onixbyte/security/KeyLoader.java | 5 ++ .../onixbyte/security/impl/ECKeyLoader.java | 49 +++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) 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 8f3be18..7464a9d 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 @@ -22,6 +22,7 @@ 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; @@ -67,6 +68,10 @@ public interface KeyLoader { throw new KeyLoadingException("This key loader does not support loading an RSA public key."); } + 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 index cdb2124..ffc45ef 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. @@ -59,6 +61,13 @@ public class ECKeyLoader implements KeyLoader { private final Base64.Decoder decoder; + /** + * Supported curves. + */ + public static final Set SUPPORTED_CURVES = new HashSet<>(Set.of( + "secp256r1", "secp384r1", "secp521r1", "secp224r1" + )); + /** * Initialise a key loader for EC-based algorithms. */ @@ -122,4 +131,38 @@ public class ECKeyLoader implements KeyLoader { } } + @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); + } + } } From 003cb5a086bcaed9c764cdb47b43abe30ddb5671 Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 12:51:21 +0800 Subject: [PATCH 07/10] docs: add javadoc --- .../java/com/onixbyte/security/KeyLoader.java | 13 +++++++++++++ .../com/onixbyte/security/impl/ECKeyLoader.java | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) 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 7464a9d..d8806a0 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 @@ -68,6 +68,19 @@ public interface KeyLoader { 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."); } 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 index ffc45ef..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 @@ -131,6 +131,23 @@ public class ECKeyLoader implements KeyLoader { } } + /** + * Loads an EC public key from the given hexadecimal x and y coordinates alongside the curve name. + *

+ * 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)) { From 8f844457134893568a4770b451f1717b017f565c Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 12:52:11 +0800 Subject: [PATCH 08/10] docs: change javadoc expressions --- .../java/com/onixbyte/security/KeyLoader.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 d8806a0..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 @@ -56,13 +56,18 @@ public interface KeyLoader { PublicKey loadPublicKey(String pemKeyText); /** - * Get the public key with given modulus and public exponent. + * 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 - * @param exponent the public exponent - * @return generated public key object from the provided key specification - * @see KeyFactory#getInstance(String) - * @see KeyFactory#generatePublic(KeySpec) + * @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."); From fad6916262843897b750e85e2585966d0b3f207a Mon Sep 17 00:00:00 2001 From: siujamo Date: Mon, 9 Jun 2025 15:00:39 +0800 Subject: [PATCH 09/10] refactor: mark `ReflectMapUtil` as deprecated This utility class is no longer maintained and will be removed in a future release. If you still need to use `ReflectMapUtil`, please stick to the last available version (2.2.0). --- .../devkit/utils/unsafe/ReflectMapUtil.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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:

*
  * {@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 userMap = ReflectMapUtil.objectToMap(user);
  *         System.out.println(userMap); // Output: {name=John, age=30}
- *         
+ *
  *         // Convert map to object
  *         User newUser = ReflectMapUtil.mapToObject(userMap, User.class);
  *         System.out.println(newUser.getName()); // Output: John
@@ -66,7 +66,10 @@ import java.util.Map;
  * @author zihluwang
  * @version 1.4.2
  * @since 1.4.2
+ * @deprecated This utility class is no longer maintained and will be removed in a future release. If you still need to
+ * use {@link ReflectMapUtil}, please stick to the last available version (2.2.0).
  */
+@Deprecated(forRemoval = true, since = "2.2.0")
 public final class ReflectMapUtil {
 
     private final static Logger log = LoggerFactory.getLogger(ReflectMapUtil.class);

From d15adfdb97655fbb3285d24ce437f78e74d612c7 Mon Sep 17 00:00:00 2001
From: zihluwang 
Date: Mon, 9 Jun 2025 19:25:02 +0800
Subject: [PATCH 10/10] build: upgrade to version 2.3.0

---
 gradle.properties | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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