Kotlinで公開鍵暗号方式の鍵を扱うときの備忘録とかJWTの生成とか

目的

KotlinでJWTを生成したい。

実際はKtorでサーバー書いてJWT返す処理を書いたけど、そこは割愛。

準備

opensslで秘密鍵の生成。

$ openssl genrsa -out private.key 2048

JavaPKCS8EncodedKeySpec秘密鍵インスタンスを作るのに秘密鍵PKCS 8 形式にしておく。

$ openssl pkcs8 -in private.key -topk8 -nocrypt -out private.key.pk8

公開鍵は特別なことはせず作成。

$ openssl rsa -in private.key -pubout -out public.key

鍵、どうやって扱おう

鍵ファイルをそのまま扱うのは正直面倒なので、環境変数に格納して扱いたい。

ただ、ファイルの内容をそのまま環境変数に入れようとしても改行が入っているのでそのまま扱うのは避けたい。

扱えないわけではないけど、改行コードの扱いだったりDotenvのアレコレでハマるのも嫌なので改行なしの文字列として扱えるようにしてしまう。

以下のような感じでソイヤっとBase64エンコードしちゃう。で、それを環境変数だったり.envに設定する。

そもそも鍵の中身自体Base64エンコードされていてマトリョーシカ感あるけどソレはソレ。

$ base64 -i private.key.pk8
$ base64 -i public.key

作った鍵を使ってJWTを生成する

JWTの生成は以下を利用。

鍵の文字列からそれぞれのKeySpecをつくるところは、そのまま以下のブログを参考にした。

PemReader.readFirstSectionAndClose を使いたいがために以下を入れてしまっているので、気になる人は自分で書いても良いと思う。

    private val dotenv = dotenv { ignoreIfMissing = true }
    private val privateKeyString = Base64.getDecoder().decode(dotenv.get("RSA_PRIVATE_KEY")).decodeToString()
    private val publicKeyString = Base64.getDecoder().decode(dotenv.get("RSA_PUBLIC_KEY")).decodeToString()

    private val keyFactory: KeyFactory = KeyFactory.getInstance("RSA")
    private val privateKey = keyFactory.generatePrivate(privateKeyString.fromPKCS8toKeySpec()) as RSAPrivateKey
    private val publicKey = keyFactory.generatePublic(publicKeyString.fromX509toKeySpec()) as RSAPublicKey
     private val algorithm: Algorithm = Algorithm.RSA256(publicKey, privateKey)

    fun create(contract: PostContract): String {
        return JWT.create()
            .withIssuer("hogehoge")
            .withExpiresAt(Date.from(ZonedDateTime.now(ZoneOffset.UTC).plusDays(1L).toInstant()))
            .withIssuedAt(Date.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant()))
            .sign(algorithm)
    }

    private fun String.fromPKCS8toKeySpec(): PKCS8EncodedKeySpec {
        return PKCS8EncodedKeySpec(PemReader.readFirstSectionAndClose(StringReader(this)).base64DecodedBytes)
    }

    private fun String.fromX509toKeySpec(): X509EncodedKeySpec {
        return X509EncodedKeySpec(PemReader.readFirstSectionAndClose(StringReader(this)).base64DecodedBytes)
    }