一覧SC-08-3 · 証明書とPKI

証明書の失効確認とCRL・OCSP

証明書の期限と、失効情報の状態・鮮度が別の検査であることを説明します。

説明

証明書は有効期限の途中でも失効することがあります。CRLは発行者が示す失効一覧、OCSPは個々の証明書に対する状態応答です。応答の署名、対象、有効な時刻範囲を確認してから状態を判断します。

同じ試料証明書について、通常の経路検証とCRLを含む検証を比較します。次に署名付きOCSP応答のgood・revoked・unknownと古い応答を調べ、状態不明を有効と決めつけない処理を確認します。

証明書の期限と、失効情報の状態・鮮度が別の検査であることを説明します。

1失効情報を使わず経路を検証する

固定時刻2026-11-13は葉証明書の有効期間内です。まず失効情報を与えない場合を確認します。

from sc08 import * print(check()署名経路と利用条件を検証する)

OKですが、このコマンドには失効確認を指定していません。経路検証が通ったことから、失効していないと結論付けることはできません。

2CRLの発行者と失効番号を確認する

中間CAが署名したCRLを読みます。証明書番号30は11月10日に失効しています。

crl = x509.load_pem_x509_crlCRLを読み込む((HERE/"revoked.crl.pem").read_bytes()) print(crl.issuer.rfc4514_string()) print([(r.serial_number失効した証明書の番号を読む,r.revocation_date_utc.isoformat()) for r in crl]) print(crl.last_update_utc.isoformat(),crl.next_update_utc.isoformat())

このCRLの発行者は葉証明書の発行者に対応します。一覧の番号は、その発行者の証明書番号と照合します。

3CRLを指定して検証する

同じ検証へcrl_checkを加えます。CRLの署名と鮮度もOpenSSLが検査します。

print(check(crl=TrueCRLによる失効確認を追加する))

certificate revokedとなりました。証明書の有効期限が先でも、失効情報が利用拒否の根拠になります。

4OCSP応答の対象を読む

ここからは別々に作った状態応答を比較します。同一時点に三つの状態が同時に正しいという意味ではありません。

good = response("good")署名付きgood応答を読み込む print(good.serial_number,good.hash_algorithm.name) print(good.issuer_name_hash.hex()) print(good.issuer_key_hash発行者公開鍵のハッシュを確認する.hex()) print(good.this_update_utc.isoformat(),good.next_update_utc.isoformat())

対象は番号と発行者名・公開鍵のハッシュで特定します。番号30だけで、異なる発行者の証明書と混同しないようにします。

5署名と対象と時刻を検査する

statusは信頼した発行者の公開鍵で署名を確認し、対象証明書との一致と時刻範囲を検査してから状態を返します。

print(status(good)good応答の署名・対象・鮮度を確認する) print(status(response("revoked"))失効応答を確認する) print(status(response("unknown"))状態不明の応答を確認する)

GOOD、REVOKED、UNKNOWNです。goodはこの応答の失効状態に関する情報で、名前・用途・経路など他の検証を省略する理由にはなりません。

6古いgood応答を提示する

検証時刻をnextUpdateより後へ進めます。署名が正しいことと情報が新しいことは別です。

print(status(good,when="2026-12-02"応答の更新期限後へ時刻を進める))

staleです。古いgoodをいつまでも使うと、後から発生した失効を反映できません。

7状態不明と取得失敗を有効へ変換しない

この例の業務方針は、GOODが確認できた場合だけ先へ進むことです。取得失敗をNoneとして同じ表で評価します。

states = [status(good),status(response("revoked")),status(response("unknown")),status(good,"2026-12-02"),None] print([(s,s == "GOOD"例示した業務方針で受付を判定する) for s in states])

この方針ではGOOD以外を受け入れません。利用継続を優先する別の方針を採る場合も、失効なしを確認した状態と、確認できなかった状態を記録上分ける必要があります。

まとめ

証明書の期限内でも、失効情報によって利用を拒否します。

状態応答は署名・対象・鮮度を検証して利用します。

unknownや取得失敗はgoodの同義語ではありません。

操作履歴と解説を振り返れます。

Python 3.15 / cryptography 50.0.2 / 公開試料と固定時刻

1
from sc08 import * print(check())
(0, ['leaf.pem: OK'])
2
crl = x509.load_pem_x509_crl((HERE/"revoked.crl.pem").read_bytes()) print(crl.issuer.rfc4514_string()) print([(r.serial_number,r.revocation_date_utc.isoformat()) for r in crl]) print(crl.last_update_utc.isoformat(),crl.next_update_utc.isoformat())
CN=Lbox intermediate
[(30, '2026-11-10T00:00:00+00:00')]
2026-11-11T00:00:00+00:00 2026-12-01T00:00:00+00:00
3
print(check(crl=True))
(2, ['error 23 at 0 depth lookup: certificate revoked'])
4
good = response("good") print(good.serial_number,good.hash_algorithm.name) print(good.issuer_name_hash.hex()) print(good.issuer_key_hash.hex()) print(good.this_update_utc.isoformat(),good.next_update_utc.isoformat())
30 sha256
f742cc0df675f568b1e40f6e2b09bfa5cfc253cb7261a802c161a05289db0a96
6a3803d5f059902a1c6dafbc9ba4729212f7caac08634cc3ae76b27529f03827
2026-11-11T00:00:00+00:00 2026-12-01T00:00:00+00:00
5
print(status(good)) print(status(response("revoked"))) print(status(response("unknown")))
GOOD
REVOKED
UNKNOWN
6
print(status(good,when="2026-12-02"))
stale
7
states = [status(good),status(response("revoked")),status(response("unknown")),status(good,"2026-12-02"),None] print([(s,s == "GOOD") for s in states])
[('GOOD', True), ('REVOKED', False), ('UNKNOWN', False), ('stale', False), (None, False)]

演習完了