🔑

Python:JWK(s)の有効期限とx5cチェーンの検証

2024/10/22に公開

はじめに

  • x5cフィールドを利用して,jwk(s)の公開鍵データの有効期限および証明書チェーンを検証するプログラムをpython3で記述しました.
  • なお,公開鍵はサーバー証明書で登録された公開鍵を想定しています.

ライブラリ

  • pyca/cryptography(バージョン43.0.1を使用している)

プログラム

  • 検証のロジックを司っている関数は,verify_jwk()です.
import json
from cryptography.x509 import DNSName, load_pem_x509_certificates
from cryptography.x509.verification import PolicyBuilder, Store, VerificationError

def verify_jwk(jwk, dns):
    cert_chain = ""
    # Extract the certificate data from the x5c field in the JWK
    for cert in jwk["x5c"]:
        pem_data_serialized = '-----BEGIN CERTIFICATE-----' + cert + '-----END CERTIFICATE-----'
        cert_chain += pem_data_serialized
    cert_chain = cert_chain.encode()

    # Load the certificate chain to a list of Certificate objects
    pem_x509_cert_list = load_pem_x509_certificates(cert_chain)
    server_cert, intermediate_cert, root_cert = pem_x509_cert_list

    store = Store([root_cert])
    builder = PolicyBuilder().store(store)
    verifier = builder.build_server_verifier(DNSName(dns))
    try:
        chain_cert_list = verifier.verify(server_cert,[intermediate_cert])
        print("\nVerification successful:\n", chain_cert_list) 
        return True
    except VerificationError:
        return False
    
def verify_jwks(jwks, DNSs):
    for jwk, DNS in zip(jwks["keys"], DNSs):
        if "x5c" not in jwk:
            return False
        elif verify_jwk(jwk, DNS) == False:
            return False
    return True

if __name__ == "__main__":
    with open("jwks.json", "r") as jwks_file:
        jwks = json.load(jwks_file)
    """
    jwks内の各jwkに対応するドメインを順にリストに格納する.
    例:DNSs = ["example.com", "example.org"]
    """
    DNSs = []
    jwks_verification_result = verify_jwks(jwks, DNSs)
    print(f"JWKS Verification Result: {jwks_verification_result}")

verify_jwk関数の解説

x5cから証明書を取得する

cert_chain = ""

# Extract the certificate data from the x5c field in the JWK
for cert in jwk["x5c"]:
    """
    - x5cから各証明書データ(プログラムでは,3つを想定)を取得し,
    ヘッダーとフッターの追加を行います.
    """
    pem_data_serialized = '-----BEGIN CERTIFICATE-----' + cert + '-----END CERTIFICATE-----'
    """
    - ヘッダーとフッターを追加した証明書データをそれぞれ結合させます.
    """
    cert_chain += pem_data_serialized

証明書データをcryptographyライブラリで扱えるようにする.

  • cryptography.x509.load_pem_x509_certificatesメソッドを利用する.

https://cryptography.io/en/latest/x509/verification/

from cryptography.x509 import DNSName, load_pem_x509_certificates
# ....

"""証明書データをバイナリ化する"""
cert_chain = cert_chain.encode()

# Load the certificate chain to a list of Certificate objects
"""
複数のpem形式証明書が含まれたバイナリデータを引数として,
cryptography.x509.Certificatクラスの証明書データを取得する.
"""
pem_x509_cert_list = load_pem_x509_certificates(cert_chain)
server_cert, intermediate_cert, root_cert = pem_x509_cert_list

サーバー証明書の有効期限と証明書チェーンの検証

  • cryptography.x509.verification内のメソッドやクラスとcryptography.x509.DNSNameクラスを利用する.

  • (cryptography.x509.DNSName)

https://cryptography.io/en/latest/x509/reference/#cryptography.x509.DNSName

  • (cryptography.x509.verification)

https://cryptography.io/en/latest/x509/verification/#module-cryptography.x509.verification

from cryptography.x509 import DNSName, load_pem_x509_certificates
from cryptography.x509.verification import PolicyBuilder, Store, VerificationError
# ....
"""ルート証明書を引数に,証明書ストアを生成"""
store = Store([root_cert])

"""
- 証明書ストアを元に,証明書検証者
(
cryptography.x509.verification.ClientVerifierや
cryptography.x509.verification.ServerVerifier
)を構築するためのインターフェースであるPolicybuilderを生成.
"""

builder = PolicyBuilder().store(store)

"""
- 証明書検証時の時刻を設定したい場合,
time()メソッドを使う.
builder = builder.time(verification_time)
※verification_timeは,datetime.datetimeのインスタンスを使う.

- time()メソッドが明示的に呼ばれなかった場合,
検証時の時刻は,(build_server_verifier()
またはbuild_client_verifier()メソッドが呼ばれた時に)
datetime.datetime.now()と設定される.
"""

"""
- build_server_verifier()メソッドを用いて,サーバー証明書を検証する
cryptography.x509.verification.ServerVerifierを構築する.

- 引数には,cryptography.x509.DNSNameまたは
cryptography.x509.IPAddressが必要.
サーバー証明書の対象となるであろうドメインまたは,ipアドレスが必要となる.
"""
verifier = builder.build_server_verifier(DNSName(dns))
    try:
        """
        verifyメソッドを用いて,サーバー証明書を検証する.
        第一引数をサーバー証明書のデータ,
        第二引数を中間証明書のデータが含まれたリストにする.
        """
        chain_cert_list = verifier.verify(server_cert,[intermediate_cert])
        print("\nVerification successful:\n", chain_cert_list) 
        return True
    except VerificationError:
        return False

参考資料

  • Welcome to pyca/cryptography — Cryptography 43.0.1 documentation

https://cryptography.io/en/43.0.1/

Discussion