From 7dd3c185bb08a4d0358f84a7e19bb08813cf47f2 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 1 May 2023 07:09:26 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=87=AA=E5=8B=95=E3=81=A7=E9=8D=B5?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/model/hideout/entity/Jwt.kt | 5 +++ .../dev/usbharu/hideout/plugins/Security.kt | 39 ++++++++++-------- .../service/ServerInitialiseServiceImpl.kt | 4 +- .../usbharu/hideout/util/JsonWebKeyUtil.kt | 41 +++++++++++++++++++ .../dev/usbharu/hideout/util/RsaUtil.kt | 19 +++++++++ 5 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Jwt.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/util/JsonWebKeyUtil.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Jwt.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Jwt.kt new file mode 100644 index 00000000..07f3ad55 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/Jwt.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.domain.model.hideout.entity + +import java.util.* + +data class Jwt(val kid: UUID, val privateKey: String, val publicKey: String) diff --git a/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt b/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt index e5a3766f..bb487c6f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt +++ b/src/main/kotlin/dev/usbharu/hideout/plugins/Security.kt @@ -7,7 +7,10 @@ import com.auth0.jwt.JWT import com.auth0.jwt.algorithms.Algorithm import dev.usbharu.hideout.domain.model.hideout.form.UserLogin import dev.usbharu.hideout.property +import dev.usbharu.hideout.repository.IMetaRepository import dev.usbharu.hideout.service.IUserAuthService +import dev.usbharu.hideout.util.JsonWebKeyUtil +import dev.usbharu.hideout.util.RsaUtil import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.auth.* @@ -15,19 +18,27 @@ import io.ktor.server.auth.jwt.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* +import kotlinx.coroutines.runBlocking import java.security.KeyFactory import java.security.interfaces.RSAPrivateKey -import java.security.interfaces.RSAPublicKey import java.security.spec.PKCS8EncodedKeySpec import java.util.* import java.util.concurrent.TimeUnit const val TOKEN_AUTH = "jwt-auth" -fun Application.configureSecurity(userAuthService: IUserAuthService) { +fun Application.configureSecurity(userAuthService: IUserAuthService, metaRepository: IMetaRepository) { - val privateKeyString = property("jwt.privateKey") - val issuer = property("jwt.issuer") + val privateKeyString = runBlocking { + requireNotNull(metaRepository.get()).jwt.privateKey + } + val publicKey = runBlocking { + val publicKey = requireNotNull(metaRepository.get()).jwt.publicKey + println(publicKey) + RsaUtil.decodeRsaPublicKey(Base64.getDecoder().decode(publicKey)) + } + println(privateKeyString) + val issuer = property("hideout.url") // val audience = property("jwt.audience") val myRealm = property("jwt.realm") val jwkProvider = JwkProviderBuilder(issuer) @@ -57,8 +68,6 @@ fun Application.configureSecurity(userAuthService: IUserAuthService) { if (check.not()) { return@post call.respond(HttpStatusCode.Unauthorized) } - - val publicKey = jwkProvider.get("6f8856ed-9189-488f-9011-0ff4b6c08edc").publicKey val keySpecPKCS8 = PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString)) val privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpecPKCS8) val token = JWT.create() @@ -66,22 +75,16 @@ fun Application.configureSecurity(userAuthService: IUserAuthService) { // .withIssuer(issuer) .withClaim("username", user.username) .withExpiresAt(Date(System.currentTimeMillis() + 60000)) - .sign(Algorithm.RSA256(publicKey as RSAPublicKey, privateKey as RSAPrivateKey)) + .sign(Algorithm.RSA256(publicKey, privateKey as RSAPrivateKey)) return@post call.respond(hashSetOf("token" to token)) } - get("/.well-known/jwks.json"){ + get("/.well-known/jwks.json") { //language=JSON - call.respondText(contentType = ContentType.Application.Json,text = """{ - "keys": [ - { - "kty": "RSA", - "e": "AQAB", - "kid": "6f8856ed-9189-488f-9011-0ff4b6c08edc", - "n":"tfJaLrzXILUg1U3N1KV8yJr92GHn5OtYZR7qWk1Mc4cy4JGjklYup7weMjBD9f3bBVoIsiUVX6xNcYIr0Ie0AQ" - } - ] -}""") + call.respondText( + contentType = ContentType.Application.Json, + text = JsonWebKeyUtil.publicKeyToJwk(requireNotNull(metaRepository.get()).jwt.publicKey) + ) } } } diff --git a/src/main/kotlin/dev/usbharu/hideout/service/ServerInitialiseServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/ServerInitialiseServiceImpl.kt index 39ea048e..f18a090a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/ServerInitialiseServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/ServerInitialiseServiceImpl.kt @@ -43,8 +43,8 @@ class ServerInitialiseServiceImpl(private val metaRepository: IMetaRepository) : val generateKeyPair = keyPairGenerator.generateKeyPair() val jwt = Jwt( UUID.randomUUID(), - Base64.getEncoder().encodeToString(generateKeyPair.public.encoded), - Base64.getEncoder().encodeToString(generateKeyPair.private.encoded) + Base64.getEncoder().encodeToString(generateKeyPair.private.encoded), + Base64.getEncoder().encodeToString(generateKeyPair.public.encoded) ) val meta = Meta(implementationVersion, jwt) metaRepository.save(meta) diff --git a/src/main/kotlin/dev/usbharu/hideout/util/JsonWebKeyUtil.kt b/src/main/kotlin/dev/usbharu/hideout/util/JsonWebKeyUtil.kt new file mode 100644 index 00000000..73eb47a1 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/util/JsonWebKeyUtil.kt @@ -0,0 +1,41 @@ +package dev.usbharu.hideout.util + +import java.math.BigInteger +import java.security.KeyFactory +import java.security.interfaces.RSAPublicKey +import java.security.spec.X509EncodedKeySpec +import java.util.* + +object JsonWebKeyUtil { + + fun publicKeyToJwk(publicKey: String): String { + val x509EncodedKeySpec = X509EncodedKeySpec(Base64.getDecoder().decode(publicKey)) + val generatePublic = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) + return publicKeyToJwk(generatePublic as RSAPublicKey) + } + + fun publicKeyToJwk(publicKey: RSAPublicKey): String { + val e = encodeBase64UInt(publicKey.publicExponent) + val n = encodeBase64UInt(publicKey.modulus) + return """{"e":"$e","n":"$n","use":"sig","kty":"RSA"}""" + } + + private fun encodeBase64UInt(bigInteger: BigInteger, minLength: Int = -1): String { + if(bigInteger.signum() < 0){ + throw IllegalArgumentException("Cannot encode negative numbers") + } + + var bytes = bigInteger.toByteArray() + if (bigInteger.bitLength() % 8 == 0 && (bytes[0] == 0.toByte()) && bytes.size > 1){ + bytes = Arrays.copyOfRange(bytes, 1, bytes.size) + } + if (minLength != -1){ + if (bytes.size < minLength){ + val array = ByteArray(minLength) + System.arraycopy(bytes, 0, array, minLength - bytes.size, bytes.size) + bytes = array + } + } + return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt b/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt new file mode 100644 index 00000000..e7f9aef4 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/util/RsaUtil.kt @@ -0,0 +1,19 @@ +package dev.usbharu.hideout.util + +import java.security.KeyFactory +import java.security.interfaces.RSAPrivateKey +import java.security.interfaces.RSAPublicKey +import java.security.spec.PKCS8EncodedKeySpec +import java.security.spec.X509EncodedKeySpec + +object RsaUtil { + fun decodeRsaPublicKey(byteArray: ByteArray):RSAPublicKey{ + val x509EncodedKeySpec = X509EncodedKeySpec(byteArray) + return KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey + } + + fun decodeRsaPrivateKey(byteArray: ByteArray):RSAPrivateKey{ + val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(byteArray) + return KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey + } +}