多分2017年くらいにやるかと思って放置していた JWT 周りのサポートをようやく重い腰をあげて完了した。一応、現時点て RFC になってるものは全部入れたはず(EdDSA とか ES256K とか)。ドラフトの ECDH-1PU はそのうちファイナルになったらサポートするかもしれない。
こんな感じで使える。
(import (rnrs)
(rfc jwt)
(rfc jwk)
(rfc jwe)
(rfc jws)
(rfc uuid)
(crypto)
(srfi :19)
(math ec) ;; for ec-parameters
(sagittarius combinators))
(define keypair (generate-key-pair Ed25519))
(define alg 'EdDSA)
;; If you want to use other algorighm and keys
;; (define keypair (generate-key-pair ECDSA :ec-parameter NIST-P-256))
;; (define alg 'ES256)
;; (define keypair (generate-key-pair RSA :size 2048))
;; (define alg 'PS512)
(define claims
(jwt-claims-builder
(iss "Sagittarius Scheme")
(aud "All saints")
(sub "Use Sagittarius")
(iat (current-time))
(nbf (add-duration (current-time) (make-time time-duration 0 -1)))
(exp (add-duration (current-time) (make-time time-duration 0 600)))
(jti (uuid->string (make-v4-uuid)))))
(define jws-header
(jws-header-builder
(alg alg)))
(define payload (string->utf8 (jwt-claims->json-string claims)))
(define jws-object (make-jws-object jws-header payload))
(define signer (private-key->jws-signer (keypair-private keypair)))
(define jwk
(public-key->jwk (keypair-public keypair)
(jwk-config-builder (kid "my key"))))
(define jwks (make-jwk-set (list jwk)))
(let ((jwt-object (jws:sign jws-object signer)))
;; Share the JWT to 3rd party
;; (jws:serialize jwt-object)
;; (jwk-set->json-string jwks)
;; Verify the JWT token with the public key
(let* ((kid-matcher (jwk-matcher:kid "my key"))
(verifier (public-key->jws-verifier
(jwk-set:find-key jwks kid-matcher)))
(jwt-consumer (jwt-consumer-builder
(verifier verifier)
(claims-validator
(compose jwt:iss-required-validator
jwt:sub-required-validator
jwt:aud-required-validator
jwt:exp-required-validator
jwt:nbf-required-validator
jwt:iat-required-validator
jwt:jti-required-validator
(jwt:iss-value-validator "Sagittarius Scheme"
"Sagittarius")
(jwt:sub-value-validator "Use Sagittarius")
(jwt:aud-value-validator "All saints")
(jwt:nbf-validator)
(jwt:exp-validator)))))
(claims (jwt:consume jwt-consumer jwt-object)))
;; use the user claim
(jwt-claims-aud claims))) ;; retrieve 'aud' field
上記はおそらく 90% 以上くらいの JWT ユーザーが使っているであろう JWS を用いたもの。(個人的に JWE を JWT として使ってるのアプリを見たことがない)っで、以下が JWE を作る方法
(import (rnrs)
(rfc jwe)
(rfc jwk))
(define jwk-bob
(json-string->jwk
"{\"kty\":\"EC\",
\"crv\":\"P-256\",
\"x\":\"weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ\",
\"y\":\"e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck\",
\"d\":\"VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw\"}"))
(define jwe-header
(jwe-header-builder
(alg 'ECDH-ES+A128KW)
(enc 'A128GCM)
(apu "QWxpY2U")
(apv "Qm9i")))
;; Alice wants to encrypt with Bob's public key
(define alice-encryptor (make-ecdh-jwe-encryptor (jwk->public-key jwk-bob)))
;; Bob needs to decrypt Alice's message with his private key
(define bob-decryptor (make-ecdh-jwe-decryptor jwk-bob))
(define secret-key (string->utf8 "down the rabbit hole"))
(let ((jwe-object (jwe:encrypt alice-encryptor jwe-header secret-key)))
(jwe:serialize jwe-object)
(let ((secret-key (jwe:decrypt bob-decryptor jwe-object)))
(utf8->string secret-key)))
基本的な使用感は Java の Nimbus JOSE + JWT を参考にしているが、JWT Consumer は多分違う。ライブラリがいくつにも分かれているのは単なる趣味。よく使われる JWS を整合性チェックに用いるのは (rfc jwk)
と (rfc jws)
だけで済むとかまぁ、そういう感じにしたかったからという。