[Python] 使用 PyJWT 自己寫製作與解碼 JWT token 的程式

[Python] 使用 PyJWT 自己寫製作與解碼 JWT token 的程式

專案裡面常常會碰到 JWT 這個東西,

偶爾就會出現 JWT token 驗證失敗的問題,

這時就得拿 JWT token 與對應的 public key 來驗證。

 

找了一下網路上,是有不少解碼 (decode) JWT token 的程式,

不過不知是我試驗的方法有問題還是怎樣,

有些程式自己製作出來的 JWT token,用原本的 public key 居然解不開?!

而有些程式只提供了解碼的功能,沒有製作 JWT token 的功能…

 

決定用 PyJWT 自己寫個簡單的製作 JWT token 與解碼的程式~

下面這個是 jwt_encode.py,它會從 stdin 讀入一個 json 內容,

再用 CLI 參數指定的 private key 檔案簽章,產生一個 JWT token 印在 stdout:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
import sys

import jwt

# Read data from stdin
data = json.loads(input())

# Read private key from CLI argument
private_key = open(sys.argv[1], "rb").read()

# Encode jwt
encoded = jwt.encode(data, private_key, algorithm="RS256")
print(encoded)

 

下面這個是 jwt_encode.py,它會從 stdin 讀入一個 JWT token 內容,

如果有給 CLI 參數的話,就會解碼並驗證 JWT token 符合 public key;

否則就只是將 JWT token 的內容解碼印出來:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
import sys

import jwt

# Read jwt from stdin
encoded = input()

# Read public key from CLI argument
try:
    public_key = open(sys.argv[1], "rb").read()

except Exception as e:
    public_key = None

# Get algorithm from jwt header
alg = jwt.get_unverified_header(encoded)["alg"]

# Decode jwt
if public_key:
    print(f"* Decoding and verifying with public key, algorithm={alg}...")
    decoded = jwt.decode(encoded, public_key, algorithms=[alg])
else:
    print(f"* Decoding without public key, algorithm={alg}...")
    decoded = jwt.decode(encoded, options={"verify_signature": False}, algorithms=[alg])

print(json.dumps(decoded, indent=2))

 

來實驗看看吧~

首先用 openssl 指令產生一組公私鑰的組合,

分別存在 public_key.pem 和 private_key.pem:

openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -out public_key.pem -pubout -outform PEM

 

public_key.pem 內容會像這樣:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqh......
......
EwIDAQAB
-----END PUBLIC KEY-----

 

private_key.pem 內容會像這樣:

-----BEGIN PRIVATE KEY-----
MIIEvQIBA......
......
ZRvkn1u4s0OwtGiFGuW6lzY=
-----END PRIVATE KEY-----

 

接著,我們用 jwt_encode.py 來編碼一個 json 內容:

$ echo '{"a": 1, "b": ["c", "d"], "data": {"name": "John", "age": 9}}' | jwt_encode.py private_key.pem

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjpbImMiLCJkIl0sImRhdGEiOnsibmFtZSI6IkpvaG4iLCJhZ2UiOjl9fQ.PgliGwjO6Jl4Ehg4xf61nGt4SeYsgvm-wYCL-UYcLYPljLqdVc2TgdzO4R7jSUzSl3tmkV02TaYc33M1jjfVlaU4VDrlUGpHJ78gvSHTIActrSKaIhkjmR0j767FM7T1nssQsd1cG_p6KqBtw0vNE83NUGJ2CGn96EkxQtRzKv3WGo8kzoUPtw9jbOP-_bb_7FXBJHKhb3kJd4Vy5zDMXdUWsyOAPKjrTDU0WxQV0FPlX4a3G3ysFtI9N8Agj1s0byFzj3kuzqvHqAJh3MndLlHyerwn8HJNVjESJleQETV5dMipU2kPY-uJ28KUslDomHkGmTAE1jxpjy2IBBoEtA

 

輸出的 eyJh… 就是 JWT token 的內容。

假設我們拿到這個 JWT token,想要看看裡面存了什麼,

就可以用 jwt_decode.py 來解碼 (先不給 public key)

$ echo "${JWT_TOKEN}" | jwt_decode.py

* Decoding without public key, algorithm=RS256...
{
  "a": 1,
  "b": [
    "c",
    "d"
  ],
  "data": {
    "name": "John",
    "age": 9
  }
}

 

如果手上有 public key 的話,放在參數裡,就會用來驗證這個 JWT token,

如果沒有問題的話,一樣會印出解碼後的內容:

$ echo "${JWT_TOKEN}" | jwt_decode.py public_key.pem

* Decoding and verifying with public key, algorithm=RS256...
{
  "a": 1,
  "b": [
    "c",
    "d"
  ],
  "data": {
    "name": "John",
    "age": 9
  }
}

 

我們可以做出另外一組公私鑰的組合,然後用另一組公鑰來解碼看看…

這時因為 JWT token 並不是用這組私鑰簽章的,所以公鑰驗證會失敗:

$ echo "${JWT_TOKEN}" | jwt_decode.py public_key2.pem

* Decoding and verifying with public key, algorithm=RS256...
Traceback (most recent call last):
  File "/jwt_decode.py", line 25, in <module>
    decoded = jwt.decode(encoded, public_key, algorithms=[alg])
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/jwt/api_jwt.py", line 210, in decode
    decoded = self.decode_complete(
              ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/jwt/api_jwt.py", line 151, in decode_complete
    decoded = api_jws.decode_complete(
              ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/jwt/api_jws.py", line 209, in decode_complete
    self._verify_signature(signing_input, header, signature, key, algorithms)
  File "/usr/local/lib/python3.11/site-packages/jwt/api_jws.py", line 310, in _verify_signature
    raise InvalidSignatureError("Signature verification failed")
jwt.exceptions.InvalidSignatureError: Signature verification failed

 

這樣子就可以確認這兩個 JWT token 的製作與解碼程式是正確無誤的囉~

 

參考資料:Usage Examples — PyJWT 2.8.0 documentation

(本頁面已被瀏覽過 35 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料