🔑

連載NIST PKITS証明書パス検証テストスイート(2)OpenSSLのpkits-test.plは何をしてるのか

2023/02/15に公開

前回は証明書チェーンの認証パス検証のテストスイート NIST PKITS のテストを実施するOpenSSLのスクリプト pkits-test.pl を動かすところまでやりました。
https://zenn.dev/kjur/articles/6bfacadbc7387a

今日は、pkits-test.pl のPerlスクリプトを眺めながらOpenSSLを使って何をどんな風にテストケースを実行しているのかを見ていきたいとおもいます。

テストケースの定義

テストケースは配列@testlistsで定義されていて要素2つ、3つ、7つのケースがあります。

my @testlists = (
    [ "4.1", "Signature Verification" ], # 要素2つ
    [ "4.1.1", "Valid Signatures Test1",                        0 ], #要素3つ
    [ "4.1.2", "Invalid CA Signature Test2",                    7 ],
    (中略)
    [ # 要素7つ
        "4.8.1.1",
        "All Certificates Same Policy Test1",
        "-policy anyPolicy -explicit_policy",
        "True", $nist1, $nist1, 0
    ],
  • 要素2つのケースはテストケースの大きな節番号の表示にだけ使っており、これ自体はテストは行いません。PKITSテストケース仕様書PDFの6ページから4章全体には個々のテストケースが記載されていて4.1節は証明書の署名値検証のテストになります。
  • 要素3つのケースは証明書ポリシ処理以外の一般的なテストケースの記述になっており、要素は節番号、テストケース名、コマンドの戻り値の期待値で構成されています。0の場合は正常終了、それ以外の場合はエラー終了です。
  • 要素7つのケースは証明書ポリシー処理のテストケースで、4.8節〜4.12節のテストケースは2つを除き全て要素7つのテストケースになっています。7つの要素はそれぞれ以下の通りです:
    • (1) テストケースの節以下の番号
    • (2) テストケース名(テスト対象データファイルを作るのに使われる。後述)
    • (3) openssl cms -verifyコマンドの引数(ポリシ処理初期値のオプション)
    • (4) openssl cms -verifyコマンドに-policy_printオプションを追加するとポリシー処理結果の詳細が表示されますが、ポリシー明示(explicit policy)だったか結果期待値のフラグ(True or False)
    • (5) -policy_printで表示されるAuthority Policiesの期待値となるポリシーOIDのリスト
    • (6) -policy_printで表示されるUser Policiesの期待値となるポリシーOIDのリスト
    • (7) コマンドの戻り値の期待値。0なら正常終了、それ以外ならエラー終了

@testlistsの要素数はテストケース数の247個に4.1節〜4.16節の節見出し16個を足した数になっています。

一般的なテストケースのOpenSSL実行コマンド

例えば4.8.1項の正常系の証明書のRSA署名値の検証テストでは、pkits-test.plスクリプトからは次のコマンドが呼び出されます。

../util/shlib_wrap.sh ../apps/openssl cms -verify -verify_retcode \
-CAfile pkitsta.pem -crl_check_all \
-x509_strict -policy_check -extended_crl -use_deltas \
-out /dev/null 2>&1 -in pkits/smime/SignedValidSignaturesTest1.eml \
-policy anyPolicy

opensslコマンドはいろんな用途で使うことができますが、openssl cmsはS/MIME署名暗号メールのメッセージのためのコマンドでopenssl cms -verifyでS/MIME署名メールのメッセージの「検証」を行うことができます。

openssl cms -verifyコマンドの基本形は以下になります。

openssl cms -verify -CAfile 信頼するルート証明書 -in SMIME署名メールメッセージファイル

OpenSSLのソースコードを展開したフォルダで4.8.1のテストを単体で動かすには以下のようにします。

$ cd openssl-3.0.8/test
$ openssl cms -verify -CAfile pkits/pkitsta.pem \
-in pkits/smime/SignedValidSignaturesTest1.eml 

すると以下のような出力となり検証成功していることがわかります。

Content-Type: text/plain

This is a sample signed message.
CMS Verification successful

コマンドの戻り値

シェルで実行コマンドの戻り値は、コマンド実行直後に$?に設定されますが、テストケース4.8.1の戻り値の期待値0と一致していることもわかります。

$ openssl cms -verify -CAfile pkits/pkitsta.pem \
-in pkits/smime/SignedValidSignaturesTest1.eml 
$ echo $?
0

-verify_retcodeオプションをつけると異常系のテストケースで期待値と一致するか確認できるようになります。CAのRSA署名値が間違っている4.8.2のテストケースでは戻り値は39になりました。

$ openssl cms -verify -CAfile pkits/pkitsta.pem -verify_retcode \
-in pkits/smime/SignedInvalidCASignatureTest2.eml
(エラーメッセージは省略)
CMS Verification failure
$ echo $?
39

pkits-test.plのスクリプトの中で異常終了時には32を足すことになっており、4.8.2のテストケースの期待値は7なので

期待値 7 + 32 == 結果リターンコード 39

と期待値と一致します。

テストデータのS/MIME署名メッセージファイル

pkits-test.plスクリプトでは、テストケース毎のS/MIME署名メールファイルの有効性の検証をすることにより、期待値と一致するのかを確認するわけですが、4.8.1のテストケースで、テストケース名から署名メールファイル名は、こんな感じに自動的に変換されます。

"Valid Signatures Test1"
↓空白を取る
"ValidSignaturesTest1"
↓前後に決まったのをつける
"smime/Signed" + "ValidSignaturesTest1" + ".eml"
↓完成
"smime/SignedValidSignaturesTest1.eml"

ファイルを見てみるとMIMEマルチパートでCMS SignedDataの署名データが添付されたメールのメッセージファイルになっています。中身を見てみると、、、

$ cat smime/SignedValidSignaturesTest1.eml 
To: recipient@testcertificates.gov ←メールヘッダ
From: sender@testcertificates.gov
Subject: Valid Signatures Test1
MIME-Version: 1.0 ← MIMEマルチパート
Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg="sha-256"; boundary="----AADD99E9055BC286DC1CC034FA3CF1CD" ← メール本文

This is an S/MIME signed message

------AADD99E9055BC286DC1CC034FA3CF1CD ← プレーンテキストのメール本文
Content-Type: text/plain

This is a sample signed message.

------AADD99E9055BC286DC1CC034FA3CF1CD ← CMS SignedData形式の署名の添付ファイル
Content-Type: application/pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"

MIINAwYJKoZIhvcNAQcCoIIM9DCCDPACAQExDzANBglghkgBZQMEAgEFADALBgkq
hkiG9w0BBwGgggb9MIIDfDCCAmSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBFMQsw
CQYDVQQGEwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMG
(後略)

メッセージの署名値のCMS SignedDataを見てみると、CRLも含まれていることがわかります。pkits-test.plの中で、CRLによる失効検証もしますが、*.emlに含まれるCRLを使って失効検証をするのだとわかります。

openssl cms -verifyコマンドの追加オプション

NIST PKITSの検証のために指定されている残りのオプション引数をまとめておきます。

分類 オプション 内容
検証 -verify_retcode パス検証に失敗した際、実行コマンドの戻り値としてゼロ以外の値を返す。
検証 -x509_strict RFC 5280の検証を厳密に行う。例えばCA証明書の基本制約はクリティカルでなければならないなど。
ポリシー -policy_check 証明書のポリシ処理を行う。
ポリシー -policy ポリシOID 初期値 user-initial-policy-set の値として引数の値(OID)を追加する。anyPolicyも指定可能。
CRL -crl_check_all 適切なCRLを使用して証明書チェーン中の全ての証明書の失効検証を行う。
CRL -extended_crl 拡張されたCRLの機能、例えばindirect CRLやCRL署名鍵と証明書発行鍵を分けるなどに対応する。
CRL -use_deltas delta CRLをサポートする。

ポリシー処理のテストケースのOpenSSL実行コマンド

ポリシー処理のテストケースはもう少し複雑になります。4.8.3.3のテストケースで実行されるコマンドを見てみましょう。

cmd= ../util/shlib_wrap.sh ../apps/openssl cms -verify -verify_retcode \
-CAfile pkits/pkitsta.pem -crl_check_all -x509_strict -policy_check \
-extended_crl -use_deltas -out /dev/null 2>&1 \ 
-in pkits/smime/SignedDifferentPoliciesTest3.eml 
-policy 2.16.840.1.101.3.2.1.48.1 \
-policy 2.16.840.1.101.3.2.1.48.2 \
-explicit_policy -policy_print

他のテストケースは一つのS/MIME署名データに対して検証成功か失敗かをテストするものでしたが、ポリシー処理関連のテストでは、一つの署名データに対して、幾つかのポリシー関連の初期パラメーターを変えた時に期待通りの検証結果になるかどうかをテストするものになります。

ポリシー処理関係の入力初期値と状態出力の関係

NISTのテストケースやRFC 5280 6章パス検証にあるポリシー関連の入力値と、結果として得られる状態の関係を表したのが下図です。

ポリシー処理関係の入力パラメーター

初期値変数 説明
user-initial-policy-set 検証者が受け入れ可能な証明書ポリシーOIDの集合。何でも受け入れる場合にはanyPolicyを指定することができる。
initial-explicit-policy user-initial-policy-setの中の少なくとも1つのポリシーが有効でなければならないことを示すフラグ
initial-policy-mapping-inhibit ポリシーマッピングが禁止されているかを示すフラグ
initial-any-policy-inhibit 証明書チェーン中の証明書の証明書ポリシーにanyPolicyがあることを禁止するかどうかのフラグ

ポリシー処理関係の入力パラメーターのOpenSSLでの指定方法

ポリシー処理関係の入力パラメーターをopenssl cms -verifyコマンドの追加オプションとしてどのように指定するのかをまとめました。

初期値変数 opensslでの指定方法
user-initial-policy-set -policyオプションで指定できる。複数のポリシーOIDを指定する場合には-policy 1.2.3.4 -policy 1.2.3.5のように行う。anyPolicy(oid=2.5.29.32.0)を指定する場合には文字列で-policy anyPolicyのように指定できる。
initial-explicit-policy フラグをTRUEにしたい場合には-explicit_policyオプションをつける。FALSEならつけない。
initial-policy-mapping-inhibit フラグをTRUEにしたい場合には-inhibit_mapオプションをつける。FALSEならつけない。
initial-any-policy-inhibit フラグをTRUEにしたい場合には-inhibit_anyオプションをつける。FALSEならつけない。

ポリシー処理関係のオプションをつけてopenssl cms -verifyする

4.8.1.2のテストケースで、ポリシー明示フラグ(initial-explicit-policy)をつけて、初期ポリシ(user-initial-policy-set)を2.16.840.1.101.3.2.1.48.1のみ設定して検証してみます。その際、ポリシー処理関連の結果が表示されるように-policy_printオプションをつけておきます。

$ openssl cms -verify -verify_retcode -CAfile pkits/pkitsta.pem \
-crl_check_all -x509_strict -policy_check -extended_crl -use_deltas \
-out /dev/null 2>&1 -in pkits/smime/SignedAllCertificatesSamePolicyTest1.eml \
-policy 2.16.840.1.101.3.2.1.48.1 -explicit_policy -policy_print
Require explicit Policy: True
Authority Policies:
  Policy: 2.16.840.1.101.3.2.1.48.1
    Non Critical
    No Qualifiers
User Policies:
  Policy: 2.16.840.1.101.3.2.1.48.1
    Non Critical
    No Qualifiers
CMS Verification successful

有効なポリシー2.16.840.1.101.3.2.1.48.1が見つかって検証成功しているとわかります。

4.8.1.3のテストケースでは、初期ポリシーを*.48.2に変更すると、通るポリシーがなくて検証失敗します。

$ openssl cms -verify -verify_retcode -CAfile pkits/pkitsta.pem \
-crl_check_all -x509_strict -policy_check -extended_crl -use_deltas \
-out /dev/null 2>&1 -in pkits/smime/SignedAllCertificatesSamePolicyTest1.eml \
-policy 2.16.840.1.101.3.2.1.48.2 -explicit_policy -policy_print
Require explicit Policy: True
Authority Policies:
  Policy: 2.16.840.1.101.3.2.1.48.1
    Non Critical
    No Qualifiers
User Policies: <empty>
CMS Verification failure

ポリシー処理関係の出力パラメーター

出力パラメーター 説明
authorities-constrained-policy-set user-inital-policy-setを使わずに証明書チェーンだけでポリシー処理をした結果の有効な証明書ポリシーOIDの結果の集合。RFC 5280 6章ではこの変数の記載がなく、NIST PKITSのテストケース仕様書でのみ記載されている。
user-constrained-policy-set authorities-constrained-policy-setとuser-initial-policy-setの積集合であり、初期値も反映した受け入れ可能な証明書ポリシーOIDの結果の集合。RFC 5280 6章ではこの変数の記載がなく、NIST PKITSのテストケース仕様書でのみ記載されている。
explicit-policy-indicator 結果として明示的な証明書ポリシーを必要とするかを示すフラグ。RFC 5280 6章ではこの変数の記載がなく、NIST PKITSのテストケース仕様書でのみ記載されている。

ポリシー処理関係の出力結果パラメーターとOpenSSLでの表示

openssl cms -verifyコマンドで-policy_printオプションをつけるとポリシー処理結果に関する情報が表示されます。出力パラメーターと-policy_printをつけた結果との関係を表にまとめます。

出力パラメーター 説明
authorities-constrained-policy-set Authority Policies:の下に有効なポリシーの一覧が表示されます。
user-constrained-policy-set User Policies:の下に有効なポリシーの一覧が表示されます。ここに<empty>でなくポリシーOIDが表示されていれば、ポリシー処理の結果は有効になります。
explicit-policy-indicator Require explicit Policy:の行にTrueFalseが表示されます。

おわりに

今日は、pkits-test.plでNIST PKITSのパス検証をする際に、実際にどんなopensslコマンドを実行しているのかを見てきました。ポイントはこんなとこ。

  • パス検証といいながらS/MIME署名メッセージを使ってパス検証のテストをしている。
  • openssl cms -verifyを使って検証している。
  • パス検証に必要な証明書とCRLは署名メールのCMS SignedDataに含まれている。
  • いろんな検証オプションがフルフルでついている。
  • 一般的なケースに対し、ポリシー処理検証のケースでは検証オプションが増える。

長々とすみませんでした。今日はこんなところで。

過去の連載

https://zenn.dev/kjur/articles/6bfacadbc7387a

Discussion