비대칭 키 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)
}
}