From f6dc766910b3b38777c3e2e4e20349b5e01e0877 Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Wed, 1 Jan 2025 01:09:14 +0100 Subject: [PATCH] add PublicKeyProvider --- .../ltd/hlaeja/jwt/util/PublicKeyProvider.kt | 35 +++++++++++++ .../hlaeja/jwt/util/PublicKeyProviderTest.kt | 50 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/main/kotlin/ltd/hlaeja/jwt/util/PublicKeyProvider.kt create mode 100644 src/test/kotlin/ltd/hlaeja/jwt/util/PublicKeyProviderTest.kt diff --git a/src/main/kotlin/ltd/hlaeja/jwt/util/PublicKeyProvider.kt b/src/main/kotlin/ltd/hlaeja/jwt/util/PublicKeyProvider.kt new file mode 100644 index 0000000..52fec03 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/jwt/util/PublicKeyProvider.kt @@ -0,0 +1,35 @@ +package ltd.hlaeja.jwt.util + +import java.security.KeyException +import java.security.KeyFactory +import java.security.interfaces.RSAPublicKey +import java.security.spec.X509EncodedKeySpec +import java.util.Base64.getDecoder + +object PublicKeyProvider { + + fun load( + pemFile: String, + ): RSAPublicKey = readPublicPemFile(pemFile) + .let(PublicKeyProvider::makePublicKey) + + private fun makePublicKey( + publicKeyBytes: ByteArray, + ): RSAPublicKey = KeyFactory.getInstance("RSA") + .generatePublic(X509EncodedKeySpec(publicKeyBytes)) as RSAPublicKey + + private fun readPublicPemFile( + publicKey: String, + ): ByteArray = javaClass.classLoader + ?.getResource(publicKey) + ?.readText() + ?.let(PublicKeyProvider::getPublicKeyByteArray) + ?: throw KeyException("Could not load public key") + + private fun getPublicKeyByteArray( + keyText: String, + ): ByteArray = keyText.replace(Regex("[\r\n]+"), "") + .removePrefix("-----BEGIN PUBLIC KEY-----") + .removeSuffix("-----END PUBLIC KEY-----") + .let { getDecoder().decode(it) } +} diff --git a/src/test/kotlin/ltd/hlaeja/jwt/util/PublicKeyProviderTest.kt b/src/test/kotlin/ltd/hlaeja/jwt/util/PublicKeyProviderTest.kt new file mode 100644 index 0000000..4fa0b30 --- /dev/null +++ b/src/test/kotlin/ltd/hlaeja/jwt/util/PublicKeyProviderTest.kt @@ -0,0 +1,50 @@ +package ltd.hlaeja.jwt.util + +import java.security.KeyException +import java.security.interfaces.RSAPublicKey +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class PublicKeyProviderTest { + + @Test + fun `load public key - success`() { + // given + val pemFilePath = "cert/valid-public-key.pem" + + // when + val publicKey: RSAPublicKey = PublicKeyProvider.load(pemFilePath) + + // then + assertThat(publicKey).isNotNull + assertThat(publicKey.algorithm).isEqualTo("RSA") + } + + @Test + fun `load public key - file does not exist`() { + // given + val nonExistentPemFilePath = "cert/non-existent.pem" + + // when exception + val exception = org.junit.jupiter.api.assertThrows { + PublicKeyProvider.load(nonExistentPemFilePath) + } + + // then + assertThat(exception.message).isEqualTo("Could not load public key") + } + + @Test + fun `load public key - file is invalid`() { + // given + val invalidPemFilePath = "cert/invalid-public-key.pem" + + // when exception + val exception = org.junit.jupiter.api.assertThrows { + PrivateKeyProvider.load(invalidPemFilePath) + } + + // then + assertThat(exception.message).contains("Illegal base64 character 2d") + } +}