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 b8fb3f1..5cbd52f 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 @@ -18,105 +18,38 @@ package com.onixbyte.security; import com.onixbyte.security.exception.KeyLoadingException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; 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.util.Base64; /** - * The {@code KeyLoader} class provides utility methods for loading ECDSA keys from PEM-formatted + * The {@code KeyLoader} class provides utility methods for loading keys pairs from PEM-formatted * key text. This class supports loading both private and public keys. *
* The utility methods in this class are useful for scenarios where ECDSA keys need to be loaded * from PEM-formatted strings for cryptographic operations. - *
- * - *Example usage:
- *{@code
- * String pemPrivateKey = """
- * -----BEGIN PRIVATE KEY-----
- * ...
- * -----END PRIVATE KEY-----""";
- * ECPrivateKey privateKey = KeyLoader.loadEcdsaPrivateKey(pemPrivateKey);
- *
- * String pemPublicKey = """
- * -----BEGIN PUBLIC KEY-----
- * ...
- * -----END PUBLIC KEY-----""";
- * ECPublicKey publicKey = KeyLoader.loadEcdsaPublicKey(pemPublicKey);
- * }
*
* @author zihluwang
* @version 1.6.0
* @since 1.6.0
*/
-public class KeyLoader {
-
- private final static Logger log = LoggerFactory.getLogger(KeyLoader.class);
+public interface KeyLoader {
/**
- * Private constructor prevents from being initialised.
- */
- private KeyLoader() {
- }
-
- /**
- * Load ECDSA private key from pem-formatted key text.
+ * Load private key from pem-formatted key text.
*
* @param pemKeyText pem-formatted key text
* @return loaded private key
- * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
- * or EC Key Factory is not loaded, or key spec is invalid
*/
- public static ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) {
- try {
- var decodedKeyString = Base64.getDecoder().decode(pemKeyText);
- var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
- var keyFactory = KeyFactory.getInstance("EC");
- var _key = keyFactory.generatePrivate(keySpec);
- if (_key instanceof ECPrivateKey privateKey) {
- return privateKey;
- } else {
- throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
- }
- } catch (NoSuchAlgorithmException e) {
- throw new KeyLoadingException("Cannot get EC Key Factory.", e);
- } catch (InvalidKeySpecException e) {
- throw new KeyLoadingException("Key spec is invalid.", e);
- }
- }
+ PrivateKey loadPrivateKey(String pemKeyText);
/**
- * Load ECDSA public key from pem-formatted key text.
+ * Load public key from pem-formatted key text.
*
* @param pemKeyText pem-formatted key text
* @return loaded private key
- * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
- * or EC Key Factory is not loaded, or key spec is invalid
*/
- public static ECPublicKey loadEcdsaPublicKey(String pemKeyText) {
- try {
- var keyBytes = Base64.getDecoder().decode(pemKeyText);
- var spec = new X509EncodedKeySpec(keyBytes);
- var keyFactory = KeyFactory.getInstance("EC");
- var key = keyFactory.generatePublic(spec);
- if (key instanceof ECPublicKey publicKey) {
- return publicKey;
- } else {
- throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
- }
- } catch (NoSuchAlgorithmException e) {
- throw new KeyLoadingException("Cannot get EC Key Factory.", e);
- } catch (InvalidKeySpecException e) {
- throw new KeyLoadingException("Key spec is invalid.", e);
- }
- }
+ PublicKey loadPublicKey(String pemKeyText);
}
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
new file mode 100644
index 0000000..9704214
--- /dev/null
+++ b/key-pair-loader/src/main/java/com/onixbyte/security/impl/EcKeyLoader.java
@@ -0,0 +1,125 @@
+/*
+ * 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.security.impl;
+
+import com.onixbyte.security.KeyLoader;
+import com.onixbyte.security.exception.KeyLoadingException;
+
+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.util.Base64;
+
+/**
+ * Key pair loader for loading key pairs for ECDSA-based algorithms.
+ * + * + * Example usage for ECDSA: + *
{@code
+ * KeyLoader keyLoader = new EcKeyLoader();
+ * String pemPrivateKey = """
+ * -----BEGIN EC PRIVATE KEY-----
+ * ...
+ * -----END EC PRIVATE KEY-----""";
+ * ECPrivateKey privateKey = KeyLoader.loadEcdsaPrivateKey(pemPrivateKey);
+ *
+ * String pemPublicKey = """
+ * -----BEGIN EC PUBLIC KEY-----
+ * ...
+ * -----END EC PUBLIC KEY-----""";
+ * ECPublicKey publicKey = KeyLoader.loadPublicKey(pemPublicKey);
+ * }
+ */
+public class EcKeyLoader implements KeyLoader {
+
+ private final KeyFactory keyFactory;
+
+ /**
+ * Initialise a key loader for EC-based algorithms.
+ *
+ * @throws NoSuchAlgorithmException if no {@code Provider} supports a {@code KeyFactorySpi}
+ * implementation for the specified algorithm
+ */
+ public EcKeyLoader() throws NoSuchAlgorithmException {
+ this.keyFactory = KeyFactory.getInstance("EC");
+ }
+
+ /**
+ * Load ECDSA private key from pem-formatted key text.
+ *
+ * @param pemKeyText pem-formatted key text
+ * @return loaded private key
+ * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
+ * or EC Key Factory is not loaded, or key spec is invalid
+ */
+ @Override
+ public ECPrivateKey loadPrivateKey(String pemKeyText) {
+ try {
+ // remove all unnecessary parts of the pem key text
+ pemKeyText = pemKeyText
+ .replaceAll("-----BEGIN EC PRIVATE KEY-----", "")
+ .replaceAll("-----END EC PRIVATE KEY-----", "")
+ .replaceAll("\n", "");
+ var decodedKeyString = Base64.getDecoder().decode(pemKeyText);
+ var keySpec = new PKCS8EncodedKeySpec(decodedKeyString);
+
+ var _key = keyFactory.generatePrivate(keySpec);
+ if (_key instanceof ECPrivateKey privateKey) {
+ return privateKey;
+ } else {
+ throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
+ }
+ } catch (InvalidKeySpecException e) {
+ throw new KeyLoadingException("Key spec is invalid.", e);
+ }
+ }
+
+ /**
+ * Load public key from pem-formatted key text.
+ *
+ * @param pemKeyText pem-formatted key text
+ * @return loaded private key
+ * @throws KeyLoadingException if the generated key is not a {@link ECPrivateKey} instance,
+ * or EC Key Factory is not loaded, or key spec is invalid
+ */
+ @Override
+ public ECPublicKey loadPublicKey(String pemKeyText) {
+ try {
+ // remove all unnecessary parts of the pem key text
+ pemKeyText = pemKeyText
+ .replaceAll("-----BEGIN EC PUBLIC KEY-----", "")
+ .replaceAll("-----END EC PUBLIC KEY-----", "")
+ .replaceAll("\n", "");
+ var keyBytes = Base64.getDecoder().decode(pemKeyText);
+ var spec = new X509EncodedKeySpec(keyBytes);
+ var key = keyFactory.generatePublic(spec);
+ if (key instanceof ECPublicKey publicKey) {
+ return publicKey;
+ } else {
+ throw new KeyLoadingException("Unable to load private key from pem-formatted key text.");
+ }
+ } catch (InvalidKeySpecException e) {
+ throw new KeyLoadingException("Key spec is invalid.", 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 51e0267..8b18cd7 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
@@ -20,6 +20,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.simplejwt.TokenPayload;
import com.onixbyte.simplejwt.TokenResolver;
import com.onixbyte.simplejwt.annotations.ExcludeFromPayload;
@@ -178,8 +179,9 @@ public class AuthzeroTokenResolver implements TokenResolver