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

証明書チェーンと認証局

提示された証明書と検証側の信頼設定が、それぞれ何を担うかを説明します。

説明

証明書は公開鍵と名前などの情報を発行者の署名で結び付けます。検証側は対象の証明書から発行者をたどり、あらかじめ信頼しているCAへつながる経路を検証します。自己署名されているだけで、自動的に信頼されるわけではありません。

サーバー、中間CA、ルートCAの3枚を調べます。OpenSSLで経路を検証した後、中間証明書や信頼するルートを変え、失敗する深さと原因を確認します。

提示された証明書と検証側の信頼設定が、それぞれ何を担うかを説明します。

1サーバー証明書の名前と発行者

固定の演習用証明書を読みます。subjectは証明書の主体、issuerは発行者の名前です。

from sc08 import * leaf = certificate("leaf")サーバー証明書を読み込む print(leaf.subject.rfc4514_string()) print(leaf.issuer発行者名を取得する.rfc4514_string()) print(leaf.serial_number)

主体はLbox leaf、発行者はLbox intermediate、シリアル番号は30です。番号は発行者の範囲で証明書を特定する値です。

2中間CAからルートへたどる

中間証明書の発行者と、ルートの主体名を比較します。名前の一致だけで署名検証が済むわけではありません。

middle = certificate("intermediate")中間CA証明書を取得する root = certificate("root") print(middle.subject.rfc4514_string(),middle.issuer.rfc4514_string()) print(root.subjectルートの主体を確認する.rfc4514_string(),root.issuer.rfc4514_string())

中間の発行者はLbox rootです。ルートは主体名と発行者名が同じ自己発行の証明書で、この試料は自身の鍵で署名しています。

3CA制約と公開鍵を確認する

Basic ConstraintsのcaがTrueならCAとしての証明書です。path_lengthは下に置ける中間CAの数を制約します。

for c in (leaf,middle,root): print(c.subject.rfc4514_string(),c.extensions.get_extension_for_class(x509.BasicConstraintsCA属性と経路長制約を取得する).value) print(leaf.public_key().public_bytes_raw()葉の公開鍵を表示する.hex())

葉はCAではなく、中間はpath_length=0、ルートは1です。中間の下へ別の中間CAを増やす構成は、この制約に合いません。

4信頼するルートまで検証する

checkはOpenSSL verifyを呼び、終了コードと検証行を返します。時刻は2026-11-13、名前はapi.example.test、用途はTLSサーバーに固定します。

print(check()ルート・中間・葉を使って検証する)

終了コード0でleaf.pem: OKです。署名の経路だけでなく、ここでは指定した名前・時刻・用途も合っています。

5中間証明書を渡さない

信頼するルートは同じまま、中間を経路構築へ渡す設定だけ外します。

print(check(intermediate=False中間証明書を経路候補から外す))

深さ0、つまり葉の発行者が見つかりません。ルートを持っていても、中間の公開鍵を取得できなければ経路を構築できません。

6別のルートだけを信頼する

中間は提示しますが、信頼するCAを無関係なwrongへ変更します。

print(check(ca="wrong"信頼するルートを変更する))

深さ1で中間の発行者へつながりません。サーバーが送ってきた証明書をすべて信頼先へ追加する対応は、検証の目的を損ないます。

7自己署名の検証と信頼設定を分ける

試料ルートの署名を、その公開鍵で検証します。これは証明書の署名の整合性を調べる操作です。

root.public_key().verifyルートの自己署名を検証する(root.signature,root.tbs_certificate_bytes) print(root.subject == root.issuer) print(check(ca="root")確認済みのルートへ戻して検証する)

自己署名の整合性と、検証側がこのルートを信頼すると決めることは別です。最後は確認済みのrootへ設定を戻し、経路が通ることを確かめました。

まとめ

経路は葉から中間を経て、検証側が信頼するCAへつなげます。

CA制約・署名・利用条件をそれぞれ検証します。

自己署名が正しいだけで、そのCAを信頼してよいとは決まりません。

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

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

1
from sc08 import * leaf = certificate("leaf") print(leaf.subject.rfc4514_string()) print(leaf.issuer.rfc4514_string()) print(leaf.serial_number)
CN=Lbox leaf
CN=Lbox intermediate
30
2
middle = certificate("intermediate") root = certificate("root") print(middle.subject.rfc4514_string(),middle.issuer.rfc4514_string()) print(root.subject.rfc4514_string(),root.issuer.rfc4514_string())
CN=Lbox intermediate CN=Lbox root
CN=Lbox root CN=Lbox root
3
for c in (leaf,middle,root): print(c.subject.rfc4514_string(),c.extensions.get_extension_for_class(x509.BasicConstraints).value) print(leaf.public_key().public_bytes_raw().hex())
CN=Lbox leaf <BasicConstraints(ca=False, path_length=None)>
CN=Lbox intermediate <BasicConstraints(ca=True, path_length=0)>
CN=Lbox root <BasicConstraints(ca=True, path_length=1)>
ed4928c628d1c2c6eae90338905995612959273a5c63f93636c14614ac8737d1
4
print(check())
(0, ['leaf.pem: OK'])
5
print(check(intermediate=False))
(2, ['error 20 at 0 depth lookup: unable to get local issuer certificate'])
6
print(check(ca="wrong"))
(2, ['error 20 at 1 depth lookup: unable to get local issuer certificate'])
7
root.public_key().verify(root.signature,root.tbs_certificate_bytes) print(root.subject == root.issuer) print(check(ca="root"))
True
(0, ['leaf.pem: OK'])

演習完了