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/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