NIST PKITS(PKI Test Suite) Path Validation Testing Programとは米国標準技術局(NIST:National Institute of Standards and Technology)で2004年に開発された、RFC 3280で規定された証明書のパス検証を正しく実装されているか確認できるテストケースのセットです。

ドキュメント、テスト用の証明書・CRL・リポジトリ(LDAPサーバー)・CMS署名データが公開されているので、誰でもダウンロードして試すことができます。量的には、






テストケース数だいたい250
テスト可能な証明書チェーン数224
証明書の数405
CRLの数175


というなかなか、ものすごいもので、こんなの手で一つ一つやっていたら日が暮れちゃうぐらいやり甲斐がのあるテストです。

Java系の実装でApache Ant+JUnitベースでテストしたい



Apache Antは言わずと知れたmakeコマンドの代わりとなるコンパイルやビルドなどしてくれる有難いツールです。JUnitはJavaの単体テストのフレームワークですね。

Challenge PKI Test Suiteでは、テストをゴッソリ動かすようなPerlスクリプトを作って動かしていましたが、既存のNIST PKITSで使おうとすると証明書やCRLなどテストデータをいちいちデータベースに投入する必要があってかなり面倒でした。

パス検証クライアントがJavaベースの時にAnt+JUnitの組み合わせでもう少しシンプルに、PKITSのディレクトリ構成を生かしながらテストできないかな、、、と考えていました。前のChallenge PKIが古いNISTのテストケースを参照にしていたので、新しい(といっても2004年ですが)テストケース、ファイル構成に対応させたいと思っていました。

SunのCertPathValidatorでちょっと気になっていたこともあったりしたので、昨日ちょっと時間があったので作ってみました。

実現方法



基本は、「PKITSをAnt+JUnitでパパッとやってレポート作成もそちらにお任せしたい」ってことです。テストの実施は、以下のような感じのテストケースの設定ファイルをJavaのpropertiesファイルで作ってテストさせます。設定ファイルには検証対象の証明書のチェーンと、CRLのファイル名が設定されています。

# t040101_ValidSignaturesTest1.cfg
path.crt.1=TrustAnchorRootCertificate.crt
path.crt.2=GoodCACert.crt
path.crt.3=ValidCertificatePathTest1EE.crt
path.crl.1=TrustAnchorRootCRL.crl
path.crl.2=GoodCACRL.crl


検証パラメータも同じくデフォルトをプロパティファイルで与えます。テストケースファイルにも例外を書けるようにしておきます。

input.initial-policy-set=2.5.29.32.0
input.initial-explicit-policy=0
input.initial-policy-mapping-inhibit=0
input.initial-inhibit-any-policy=0


で、このテストケース設定ファイルを250近く手で作るのはえらく面倒なのでテストケース設計書のPDFから自動で作れないかと考えました。テストケース設定ファイルの自動抽出では以下のようなことをします。

・pdf2text を用い PKITS.pdf のテストケース部分(4章)をテキストにする
・改行の乱れなどスクリプトで自動修正する
・サブサブセクション名からテストケース名を取得
・その中の記述より検証対象の証明書・CRL名を取得
・テストケース設定ファイルを生成

テストケース設定ファイル群よりパス検証をJUnitテストにより行うJavaのコードを自動生成するようなスクリプトを作りました。

JUnit単体テストのクラス

package pkits.auto;

import java.util.*;
import junit.framework.TestCase;
import pkits.util.*;

↓4.1章 "Signature Verification Test"の節のテスト
public class T0401_SignatureVerificationTest extends PKITSTestCase {
 public T0401_SignatureVerificationTest(String name) { super(name); }

 protected void setUp() {}
 protected void tearDown() {}

 ↓4.1.1節 "Valid Signatures Test1" のテストを設定ファイルを指定し実行
 public void test_t040101_ValidSignaturesTest1() throws Exception {
  doPKITSTestCase("t040101_ValidSignaturesTest1");
 }
 ↑コレをテストケース数だけ
 :以下略
}


クラスの頭の部分を"T0401_"にしたり、テストメソッドを"test_t040101_"のように節番号を含むようにしておくと、テストケース結果のレポートが節の順にきれいに表示されるので良いと思います。

テストの期待値ですが、基本的にはテストケース設定ファイルの名前"*_Valid*"、"*_Invalid*" で有効、無効を判断します。但し、4.8節の証明書ポリシのテストなどでは、テストメソッドが"test_t04080101_AllCertificatesSamePolicyTest1"のようにValid/Invalidは書かれていなくて、一つのテストの中で1〜4のポリシ処理の条件を変えてテストするので書かれていないわけです。これは、面倒ですが手で設定ファイルを分けてテストケース中に期待値を書くようにしました。

# t040801_AllCertificatesSamePolicyTest1.cfg
expectValue=INVALID
input.initial-policy-set=2.16.840.1.101.3.2.1.48.2
input.initial-explicit-policy=1


でCertPathValidatorを実際に動かす全てのテストの抽象親クラスを作って作業は大体おしまいです。

実行してみると、、、、

ここまでお膳立てできれば後は "ant test" 一発で動かすだけ。

% ant test
Buildfile: build.xml
init:
prepare:
[echo] ----------- NIST PKITS Test Runner 0.9.1 [2009] --------
prepare-src:
prepare-resource:
compile:
test:
[junit] Running pkits.auto.T0401_SignatureVerificationTest
[junit] Tests run: 6, Failures: 0, Errors: 0, Time elapsed: 2.139 sec
:中略
[junit] Running pkits.auto.T0404_BasicCertificateRevocationTestsTest
[junit] Tests run: 21, Failures: 1, Errors: 0, Time elapsed: 1.921 sec
[junit] Test pkits.auto.T0404_BasicCertificateRevocationTestsTest FAILED
:中略
[junitreport] Processing reports\TESTS-TestSuites.xml to null507600500
[junitreport] Transform time: 2874ms
BUILD SUCCESSFUL
Total time: 45 seconds


pkits01pub



250のテストケースで11個失敗、つまり期待値と不一致となっています。

pkits02pub



失敗したのはどれもCRL関係のやつです。Sunの実装はIndirect CRLとかDelta CRLとかサポートしていないので、当然といえば当然、、、、

pkits03pub



サポートしていないDelta CRLのテスト結果のさらなる詳細はこんなの。
Invalidのテストケースで無効になった場合には大抵CertPathValidatorExceptionなんですが、期待値通り例外が発生した場合JUnitの単体テストとしては成功で、するとどういう理由で例外発生したからテスト成功だったのかを結果表で表示させることができないんですよね。これは、ちょっと困ったところ。

仕方なく、標準出力を見たりします。

以上、こんな感じでApache AntとJUnitでNIST PKITS Path Validation Testを動かすことができ、集計結果の表示についてもほぼ満足できるものが完成しました。パチパチ(^^v

いや〜〜、最初からXMLか何かでテストケース書いてくれれば苦労も少ないんすけどね、、、、

今回はJUnitはJUnit 3.8という古いのを使ったんですが、今はJUnitは4.5になっており、それよりもTestNGの方が注目されているそうです、、、、知らなかった、、、そろそろTestNGに切り替える時が来たのかも、、、、、

JUnit 4.xはテスト結果のErrorが無くなってSuccess/Failureだけになってしまったので、これはちょっと困ったもんです。TestNGもこれは同じっぽい???あとJUnit 4.xもTestNGもJava 1.5以降のアノテーション機能を使っているので、古いJava 1.4を使い続けなければならない場合にちょっと困っています。