chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

JWTの署名の検証を自力でやってみる(苦戦中)

諸事情により*1、OAuth2.0とかOpenID Connect等の仕様を勉強している。
認証が通ると、IDトークンがJWTで送られてくる。IDトークンは検証してから使うこと、、、ということで、公開鍵による検証をする。
PythonのJWTライブラリを使うとライブラリにお任せでどんどん進むが、理解のためになるべく自力でやりたい。自力といってもRSAの演算を手ではできないから、RSAライブラリを使ってるのだが。。
以下のようなコードで署名が正しいかを検証するのだが、、どうしてもPASSにならない。引数とか型とか間違ってるのだろうけど。

pub_key = RSA.importKey(open(PUBLIC_KEY).read())
verifier = PKCS1_v1_5.new(pub_key)
ans = verifier.verify(HEADER + "." + PAYLOAD , SIG)

0が返る。。
pyjwtはGitで公開されてるので、ソースをよく読めばどうやって検証しているのか分かると思うが。。
Python sign message with private key and verify with public key · GitHub
pyjwt/api_jws.py at master · jpadilla/pyjwt · GitHub
https://assets.ctfassets.net/2ntc334xpx65/5HColfm15cUhMmDQnupNzd/30d5913d94e79462043f6d8e3f557351/jwt-handbook-jp.pdf

それに、、いろんな脆弱性を回避するために、乱数を埋め込んで、あとから一致するかを試すのだが、どのフェーズでどんな攻撃を受けるのか、なぜその乱数を入れるとどのように回避できるのか?が分からない。分からないと言ってないで、ちゃんと解説本を読めば分かるのだろう。。か。。

■追記
そもそもの署名を自分でやってみて検証もやってみる必要があると思った。以下の記事に解説があるのでそれをまずやってみる。
OpenSSLコマンドによる公開鍵暗号、電子署名の方法 - Qiita

■追記
上記試作コードでは、本文とSignatureを公開鍵で検証(Verify)しようとしているが、正しくは、本文のSHA256ハッシュ値と、Signatureを公開鍵で検証しないといけないのではなかろうか。

■追記
opensslで秘密鍵と公開鍵を作って、署名して検証する。これは正常に動作

$ openssl genrsa 1024 > private-key.pem
$ openssl rsa -in private-key.pem -pubout -out public.pem
$ openssl dgst -sha256 -sign private-key.pem message.txt > signature.dat
$ openssl dgst -sha256 -verify public-key.pem -signature signature.dat message.txt

$ cat public-key.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo+yAy3ZPnoRGsWJO/FLo1Ofxx
wykC8dbE60rvyJXF80QW9acgijf3FDrhw5ytHJ0g3QiNzh7+G+fAAW2oiT2cJ76S
XMmvJOpCRF91XnE97FQ/gV6WR4FUgTCKDZLd4yi8fw3eIjbnE7vERHqLPRrgIws4
RBY6VKEiEyqfOUwGjQIDAQAB
-----END PUBLIC KEY-----

$ cat private-key.pem
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCo+yAy3ZPnoRGsWJO/FLo1OfxxwykC8dbE60rvyJXF80QW9acg
ijf3FDrhw5ytHJ0g3QiNzh7+G+fAAW2oiT2cJ76SXMmvJOpCRF91XnE97FQ/gV6W
            *略*
iaUaJ0U4G7NTbJMQw7UCQHhYFr4NmSRaHP+gafbG9F1qUg77sCiVKRH+sUkT85fT
XDkLYwNT+2d3nWVrLCX+Ox5uhJwHhh7C+FI7yBEdQ8M=
-----END RSA PRIVATE KEY-----

$ cat message.txt
this is test message.
bye


$ openssl dgst -sha256 -sign private-key.pem message.txt > signature.dat
$ cat signature.dat
vN@&??wI>
*FQN.
 \\4$?m<^YY\b'v&c?!F77hFj1S?!#w>s
(バイナリなので文字化け)

$ openssl dgst -sha256 -verify public-key.pem -signature signature.dat message.txt
Verified OK

参考にした記事 (感謝でございます)
OpenSSLで電子署名の生成と署名検証 - 闘うITエンジニアの覚え書き

上記OpenSSLで検証したデータを使ってPythonで検証してみる

from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256


PUBLIC_KEY = "keys/public-key.pem"
MESSAGE = "keys/message.txt"
SIG = "keys/signature.dat"

with open(PUBLIC_KEY, "r") as f:
    pub_key = RSA.importKey(f.read())

verifier = PKCS1_v1_5.new(pub_key)

digest = SHA256.new()
with open(MESSAGE, "r") as f:
    digest.update(f.read().encode('utf-8'))

with open(SIG, "rb") as f:
    sig = f.read()

ans = verifier.verify(digest, sig)
print(ans)

実行結果は以下

$ python3 veri2.py
True

上記作業で、自分の秘密鍵を使った署名と、公開鍵による検証が確認できたので、次にやるべきはJWTの仕様書を読んで、署名をどうやって計算しているか?を理解して、自分の鍵で署名と検証をやってみる。それがうまくいったら、本物のJWTと公開鍵で検証をする・・・と

*1:仕事で必要になってきているというのと、認証を勉強しておくと後々強みになるだろうと。。