asymmetric-key-jwt

비대칭 키 JWT

private, public key를 사용하는 JWT 방식

비대칭 키 생성

# 1. Private Key 생성 (2048 bit)
openssl genrsa -out private_key.pem 2048

# 2. Private Key로부터 Public Key 추출
openssl rsa -in private_key.pem -pubout -out public_key.pem

의존성

// build.gradle
dependencies {
    // JJWT 라이브러리
    implementation("io.jsonwebtoken:jjwt-api:0.12.5")
    runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.5")
    runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.5")
}

spring kotlin version

  • private key로 JWT encoding, public key로 decoding
@Component
class JwtProvider(
// application.yml로 path를 입력 (형식 file:/~~)
    @Value("\${jwt.public-key-path}") private val publicKeyResource: Resource,
    @Value("\${jwt.private-key-path}") private val privateKeyResource: Resource
) {

    private val publicKey: PublicKey
    private val privateKey: PrivateKey

    init {
        // key string을 Key 객체로 decoding
        publicKey = decodePublicKey(publicKeyResource.inputStream.readBytes())
        privateKey = decodePrivateKey(privateKeyResource.inputStream.readBytes())
    }

    fun createToken(~~): String {
        return Jwts.builder()
            .subject("chat-history")
            .claim(~~)
            .issuedAt(Date())
            .signWith(privateKey, Jwts.SIG.RS256)
            .compact()
    }


    fun verifyAndExtract(token: String): Map<String, Any> {
        return Jwts.parser()
            .verifyWith(publicKey)
            .build()
            .parseSignedClaims(token)
            .payload
    }

    // PEM 문자열(ByteArray)을 PublicKey 객체로 변환
    private fun decodePublicKey(keyBytes: ByteArray): PublicKey {
        val spec = X509EncodedKeySpec(parsePem(keyBytes))
        return KeyFactory.getInstance("RSA").generatePublic(spec)
    }

    // PEM 문자열(ByteArray)을 PrivateKey 객체로 변환
    private fun decodePrivateKey(keyBytes: ByteArray): PrivateKey {
        val spec = PKCS8EncodedKeySpec(parsePem(keyBytes))
        return KeyFactory.getInstance("RSA").generatePrivate(spec)
    }

    // PEM 문자열만 남기고 자름
    private fun parsePem(pemBytes: ByteArray): ByteArray {
        val pem = String(pemBytes)
            .replace("-----BEGIN (.*)-----".toRegex(), "")
            .replace("-----END (.*)-----".toRegex(), "")
            .replace("\\s".toRegex(), "")
        return Base64.decode(pem)
    }
}