Key Management Service

키 사양

사용방법

Spring Kotling Example

dependencies {
	implementation("software.amazon.awssdk:kms:2.23.7")
	implementation("software.amazon.awssdk:sdk-core:2.23.7")
}
package com.fastfive.booking.application

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.kms.KmsClient
import software.amazon.awssdk.services.kms.model.DecryptRequest
import software.amazon.awssdk.services.kms.model.EncryptRequest
import software.amazon.awssdk.services.kms.model.EncryptionAlgorithmSpec
import java.nio.charset.StandardCharsets
import java.util.*

@Component
class KmsService(val kmsClient: KmsClient, @Value("\\${aws.kms.symmetric}") val keyId: String) {

    fun encrypt(plainText: String): String {
        val request = EncryptRequest.builder()
            .keyId(keyId)
            .plaintext(SdkBytes.fromString(plainText, StandardCharsets.UTF_8))
            .encryptionAlgorithm(EncryptionAlgorithmSpec.SYMMETRIC_DEFAULT)
            .build()
        val encryptResponse = kmsClient.encrypt(request)
        val encode = Base64.getEncoder().encode(encryptResponse.ciphertextBlob().asByteArray())

        return String(encode, StandardCharsets.UTF_8)
    }

    fun decrypt(encryptedText: String): String {
        val decode = Base64.getDecoder().decode(encryptedText)

        val decryptRequest = DecryptRequest.builder()
            .keyId(keyId)
            .ciphertextBlob(SdkBytes.fromByteArray(decode))
            .encryptionAlgorithm(EncryptionAlgorithmSpec.SYMMETRIC_DEFAULT)
            .build()

        val decryptResponse = kmsClient.decrypt(decryptRequest)
        val plaintext = decryptResponse.plaintext().asByteArray()

        return String(plaintext, StandardCharsets.UTF_8)
    }
}
@Bean
fun customJwtDecoder(kmsClient: KmsClient): JwtDecoder {
    return JwtDecoder { token: String? ->
        val signedJwt = SignedJWT.parse(token)
        val header = "${signedJwt.header}"
        val payload = "${signedJwt.payload}"
        val signature = signedJwt.signature.decode()
        val fromByteArray = Base64Utils.encodeToString(header, payload)

        val kmsVerifyRequest = VerifyRequest.builder()
            .keyId(asymmetric)
            .message(fromByteArray)
            .signature(SdkBytes.fromByteArray(signature))
            .signingAlgorithm(SigningAlgorithmSpec.ECDSA_SHA_256)
            .build()
        val verifyResponse = kmsClient.verify(kmsVerifyRequest)

        val headers = LinkedHashMap(signedJwt.header.toJSONObject())
        val claims = MappedJwtClaimSetConverter.withDefaults(emptyMap())
            .convert(signedJwt.jwtClaimsSet.claims) as Map<String, Any>

        Jwt.withTokenValue(token)
            .headers { h -> h.putAll(headers) }
            .claims { c -> c.putAll(claims) }
            .build()
    }
}

@Bean
fun customJwtEncoder(kmsClient: KmsClient, objectMapper: ObjectMapper): JwtEncoder {
    return JwtEncoder { parameters: JwtEncoderParameters? ->
        if (parameters == null) throw IllegalArgumentException("parameters cannot be null")
        val jwsHeader = parameters.jwsHeader ?: JwsHeader.with(SignatureAlgorithm.ES256).build()
        val claims = parameters.claims

        val header = objectMapper.writeValueAsString(jwsHeader.headers)
        val payload = objectMapper.writeValueAsString(claims.claims)
        val fromByteArray = Base64Utils.encodeToString(header, payload)

        val signRequest = SignRequest.builder()
            .keyId(asymmetric)
            .messageType(MessageType.RAW)
            .message(fromByteArray)
            .signingAlgorithm(SigningAlgorithmSpec.ECDSA_SHA_256)
            .build()
        val signResponse: SignResponse = kmsClient.sign(signRequest)

        val headers = Base64Utils.encodeToBase64String(header.toByteArray())
        val payloads = Base64Utils.encodeToBase64String(payload.toByteArray())
        val signature = Base64Utils.encodeToBase64String(signResponse.signature().asByteArray())
        val tokenValue = "$headers.$payloads.$signature"

        Jwt(tokenValue, claims.issuedAt, claims.expiresAt, jwsHeader.headers, claims.claims)
    }
}