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 { * @return the builder instance */ public Builder keyPair(String publicKey, String privateKey) { - this.publicKey = KeyLoader.loadEcdsaPublicKey(publicKey); - this.privateKey = KeyLoader.loadEcdsaPrivateKey(privateKey); + var keyLoader = new EcKeyLoader(); + this.publicKey = keyLoader.loadPublicKey(publicKey); + this.privateKey = keyLoader.loadPrivateKey(privateKey); return this; }