diff --git a/key-pair-loader/build.gradle.kts b/key-pair-loader/build.gradle.kts new file mode 100644 index 0000000..17c4fd3 --- /dev/null +++ b/key-pair-loader/build.gradle.kts @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024-2024 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 + +val buildGroupId: String by project +val buildVersion: String by project +val projectUrl: String by project +val projectGithubUrl: String by project +val licenseName: String by project +val licenseUrl: String by project + +group = buildGroupId +version = buildVersion + +dependencies { + implementation(project(":devkit-core")) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + withSourcesJar() + withJavadocJar() +} + +tasks.test { + useJUnitPlatform() +} + +publishing { + publications { + create("keyPairLoader") { + groupId = buildGroupId + artifactId = "key-pair-loader" + version = buildVersion + + pom { + name = "Key Pair Loader" + description = + "This module can easily load key pairs from a PEM content." + 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" + } + } + } + + from(components["java"]) + + signing { + sign(publishing.publications["keyPairLoader"]) + } + } + + 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/key-pair-loader/src/main/java/com/onixbyte/keypairloader/KeyLoader.java b/key-pair-loader/src/main/java/com/onixbyte/keypairloader/KeyLoader.java new file mode 100644 index 0000000..a2bdcf3 --- /dev/null +++ b/key-pair-loader/src/main/java/com/onixbyte/keypairloader/KeyLoader.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024-2024 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.keypairloader; + +import com.onixbyte.keypairloader.exception.KeyLoadingException; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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.util.Base64; +import java.util.Optional; + +/** + * KeyLoader can load key pairs from PEM formated content. + * + * @author zihluwang + * @version 1.6.0 + * @since 1.6.0 + */ +@Slf4j +public class KeyLoader { + + /** + * Private constructor prevents from being initialised. + */ + private KeyLoader() { + } + + public ECPrivateKey loadEcdsaPrivateKey(String pemKeyText) { + return Optional.ofNullable(pemKeyText) + .map(Base64.getDecoder()::decode) + .map(PKCS8EncodedKeySpec::new) + .map((keySpec) -> { + try { + var kf = KeyFactory.getInstance("EC"); + return kf.generatePrivate(keySpec); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + throw new KeyLoadingException("Unable to load key from text.", e); + } + }).map((publicKey) -> { + if (publicKey instanceof ECPrivateKey privateKey) { + return privateKey; + } + return null; + }) + .orElse(null); + } + + // public ECPublicKey loadEcdsaPublicKey(String pemKeyText) { + // + // } + +} diff --git a/key-pair-loader/src/main/java/com/onixbyte/keypairloader/exception/KeyLoadingException.java b/key-pair-loader/src/main/java/com/onixbyte/keypairloader/exception/KeyLoadingException.java new file mode 100644 index 0000000..3a72811 --- /dev/null +++ b/key-pair-loader/src/main/java/com/onixbyte/keypairloader/exception/KeyLoadingException.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024-2024 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.keypairloader.exception; + +public class KeyLoadingException extends RuntimeException { + + public KeyLoadingException() { + } + + public KeyLoadingException(String message) { + super(message); + } + + public KeyLoadingException(String message, Throwable cause) { + super(message, cause); + } + + public KeyLoadingException(Throwable cause) { + super(cause); + } + + public KeyLoadingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/key-pair-loader/src/main/resources/logback.xml b/key-pair-loader/src/main/resources/logback.xml new file mode 100644 index 0000000..1cf5a50 --- /dev/null +++ b/key-pair-loader/src/main/resources/logback.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + ${COLOURFUL_OUTPUT} + + + + + + \ No newline at end of file diff --git a/key-pair-loader/src/test/java/com/onixbyte/keypairloader/KeyPairLoaderTest.java b/key-pair-loader/src/test/java/com/onixbyte/keypairloader/KeyPairLoaderTest.java new file mode 100644 index 0000000..5a5abdd --- /dev/null +++ b/key-pair-loader/src/test/java/com/onixbyte/keypairloader/KeyPairLoaderTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024-2024 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.keypairloader; + +import org.junit.jupiter.api.Test; + +public class KeyPairLoaderTest { + + @Test + public void test() { + + } + +} diff --git a/map-util-unsafe/build.gradle.kts b/map-util-unsafe/build.gradle.kts new file mode 100644 index 0000000..3376b22 --- /dev/null +++ b/map-util-unsafe/build.gradle.kts @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024-2024 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 + +val buildGroupId: String by project +val buildVersion: String by project +val projectUrl: String by project +val projectGithubUrl: String by project +val licenseName: String by project +val licenseUrl: String by project + +group = buildGroupId +version = buildVersion + +dependencies { + implementation(project(":devkit-core")) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + withSourcesJar() + withJavadocJar() +} + +tasks.test { + useJUnitPlatform() +} + +publishing { + publications { + create("mapUtilUnsafe") { + groupId = buildGroupId + artifactId = "map-util-unsafe" + version = buildVersion + + pom { + name = "Unsafe Map Util" + description = + "This module is a converter that can convert an object to a map with unsafe methods." + 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" + } + } + } + + from(components["java"]) + + signing { + sign(publishing.publications["mapUtilUnsafe"]) + } + } + + 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/settings.gradle.kts b/settings.gradle.kts index 1e3dd3e..3496393 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,3 +29,4 @@ include( "property-guard-spring-boot-starter" ) include("map-util-unsafe") +include("key-pair-loader") diff --git a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java index b153c23..768b8ce 100644 --- a/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java +++ b/simple-jwt-authzero/src/main/java/com/onixbyte/simplejwt/authzero/config/AuthzeroTokenResolverConfig.java @@ -24,51 +24,48 @@ import com.onixbyte.simplejwt.constants.TokenAlgorithm; import com.onixbyte.simplejwt.exceptions.UnsupportedAlgorithmException; import com.auth0.jwt.algorithms.Algorithm; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.*; import java.util.function.Function; /** * The {@code AuthzeroTokenResolverConfig} class provides the configuration for * the {@link AuthzeroTokenResolver}. *

- * This configuration is used to establish the mapping between the standard - * {@link TokenAlgorithm} defined in the - * {@code cn.org.codecrafters:simple-jwt-facade} and the specific algorithms - * used by the {@code com.auth0:java-jwt} library, which is the underlying - * library used by {@link AuthzeroTokenResolver} to handle JSON Web Tokens - * (JWTs). + * This configuration is used to establish the mapping between the standard {@link TokenAlgorithm} + * defined in the {@code cn.org.codecrafters:simple-jwt-facade} and the specific algorithms used + * by the {@code com.auth0:java-jwt} library, which is the underlying library used by + * {@link AuthzeroTokenResolver} to handle JSON Web Tokens (JWTs). *

* Algorithm Mapping: - * The {@code AuthzeroTokenResolverConfig} allows specifying the relationships - * between the standard {@link TokenAlgorithm} instances supported by - * {@link AuthzeroTokenResolver} and the corresponding algorithms used by the - * {@code com.auth0:java-jwt} library. The mapping is achieved using a Map, - * where the keys are the standard {@link TokenAlgorithm} instances, and the - * values represent the algorithm functions used by {@code com.auth0:java-jwt} - * library for each corresponding key. + * The {@code AuthzeroTokenResolverConfig} allows specifying the relationships between the standard + * {@link TokenAlgorithm} instances supported by {@link AuthzeroTokenResolver} and the corresponding + * algorithms used by the {@code com.auth0:java-jwt} library. The mapping is achieved using a Map, + * where the keys are the standard {@link TokenAlgorithm} instances, and the values represent the + * algorithm functions used by {@code com.auth0:java-jwt} library for each corresponding key. *

* Note: - * The provided algorithm mapping should be consistent with the actual - * algorithms supported and used by the {@code com.auth0:java-jwt} library. It - * is crucial to ensure that the mapping is accurate to enable proper token - * validation and processing within the {@link AuthzeroTokenResolver}. + * The provided algorithm mapping should be consistent with the actual algorithms supported and used + * by the {@code com.auth0:java-jwt} library. It is crucial to ensure that the mapping is accurate + * to enable proper token validation and processing within the {@link AuthzeroTokenResolver}. * * @author Zihlu Wang * @version 1.1.1 * @since 1.0.0 */ -public final class AuthzeroTokenResolverConfig implements TokenResolverConfig> { +public final class AuthzeroTokenResolverConfig + implements TokenResolverConfig> { /** * Gets the instance of {@code AuthzeroTokenResolverConfig}. *

- * This method returns the singleton instance of - * {@code AuthzeroTokenResolverConfig}. If the instance is not yet created, - * it will create a new instance and return it. Otherwise, it returns the - * existing instance. + * This method returns the singleton instance of {@code AuthzeroTokenResolverConfig}. If the + * instance is not yet created, it will create a new instance and return it. Otherwise, it + * returns the existing instance. * * @return the instance of {@code AuthzeroTokenResolverConfig} */ @@ -81,23 +78,18 @@ public final class AuthzeroTokenResolverConfig implements TokenResolverConfig - * This method returns the algorithm function associated with the given - * {@link TokenAlgorithm}. The provided {@link TokenAlgorithm} represents - * the specific algorithm for which the corresponding algorithm function - * is required. The returned Algorithm Function represents the function - * implementation that can be used by the {@link TokenResolver} to handle - * the specific algorithm. + * This method returns the algorithm function associated with the given {@link TokenAlgorithm}. + * The provided {@link TokenAlgorithm} represents the specific algorithm for which the + * corresponding algorithm function is required. The returned Algorithm Function represents the + * function implementation that can be used by the {@link TokenResolver} to handle the + * specific algorithm. * - * @param algorithm the {@link TokenAlgorithm} for which the algorithm - * function isrequired - * @return the algorithm function associated with the given {@link - * TokenAlgorithm} - * @throws UnsupportedAlgorithmException if the given {@code algorithm} is - * not supported by this - * implementation + * @param algorithm the {@link TokenAlgorithm} for which the algorithm function is required + * @return the algorithm function associated with the given {@link TokenAlgorithm} + * @throws UnsupportedAlgorithmException if the given {@code algorithm} is not supported by + * this implementation */ @Override public Function getAlgorithm(TokenAlgorithm algorithm) { @@ -139,5 +131,22 @@ public final class AuthzeroTokenResolverConfig implements TokenResolverConfig { + try { + var keyBytes = Base64.getDecoder().decode(privateKey); + var spec = new PKCS8EncodedKeySpec(keyBytes); + var kf = KeyFactory.getInstance("EC"); + var key = kf.generatePrivate(spec); + if (key instanceof ECPrivateKey pk) { + return Algorithm.ECDSA256(pk); + } else { + throw new RuntimeException("Type error!"); + } + } catch (NoSuchAlgorithmException ignored) { + } catch (InvalidKeySpecException e) { + throw new RuntimeException(e); + } + return null; + }); }}; }