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") + } +}