自堕落な技術者の日記

基本は喰ってるか飲んでるかですが、よく趣味でカラオケ・PKI・署名・認証・プログラミング・情報セキュリティをやっています。旅好き。テレビ好きで芸能通

パス検証

pathfinderという証明書のパス検証フリーウェア試用奮闘記(泣) (第2話)

一昨日は悔しい思いをしたのでw、OpenSSLのDebian鍵脆弱性の時に作ったUbuntu環境を引っ張りだしてきて、pathfinderをインストールしてみることにした。

以下、備忘録的に、、、

% aptitude install openssh-server
■ D-Busのビルドに必要そうなパッケージのインストール
% apt-get install -s dbus (1.1.20だったので古すぎ)パッケージは使わなかった
% apt-get install autoconf
% apt-get install automake
% apt-get install libtool
% apt-get install expat
% apt-get install libexpat1-dev
■ D-Busのビルド
% ./configure
% make >& make.log &
% tail -f make.log
% make install
■ WvStreamsのビルドとインストール
% ./configure
Configure: error: C++ preprocessor "/lib/cpp" と出たので以下を実行
% apt-get install g++
% apt-get install libssl-dev
% make >& make.log &
% tail -f make.log
% make install
■ pathfinderのビルドとインストール


途中、パッケージのインストール中で再起動を求められたんですが、再起動後ネットワークアダプタが認識されなくなってしまいました。orz

どうやらD-Busが悪さをしているようです。結局、pathfinderのインストールまで辿りつけませんでした。

かなり心が折れている、、、

たかがパス検証なのに、どうしてこんなにも、どうでもよいパッケージに依存しているのかと、、、

pathfinderという証明書のパス検証フリーウェア試用奮闘記(泣) (第1話)

本当はやらなければならないことが山積しているのに、現実逃避でpathfinderというRFC 5280のパス検証に準拠したフリーソフトが公開されたようなので早速試してみた。特徴はこんなとこらしい

・証明書の署名値検証
・名前チェーン検証
・基本制約の検証
・証明書ポリシの検証(含ポリシーマッピング)
・鍵使用法の検証
・OCSPによる失効検証
・ブリッジCAを含むパス検証にも対応しているっぽい
・AIA拡張によるパス構築も対応してるっぽい
・C++で書かれているっぽい。

ソースコードはここ(code.google.com)からダウンロードできるようで、今日の時点でバージョンは1.1.2(2009.09.23版)だった。

インストール


ソースコードのみの配布なので、ビルドしないといけない。cygwin環境でやることにする。以下のものが多分必要になる。
・g++
・cmake

インストール先はデフォルトでよいので(cmakeを使ったことないのに)cmake一発してみることにする。

% cmake .


あえなくエラーで敗退。何やらサードパーティーのライブラリとして
・D-Bus
・WvStreams
・OpenSSL (まぁこれはあるけど)
が必要なんだそうだ。

バージョンの指定も無いので仕方なくD-Busのサイトからdbus-1.3.0.zipをダウンロードした。雰囲気的にDebianならバイナリパッケージがありそうだった orz

同様にWvStreamsの公式サイトから辿って、wvstreams-4.6.1.tar.gzを落としてきた。


再戦(1):WvStreamsのビルド



公式サイトのトップに親切にもビルド法が書いてあるので有難くその通りやる。

% tar -xzvf wvstreams-4.6.1.tar.gz
% cd wvstreams-4.6.1
% ./configure
% make (as root:)
% make install


で、"./configure"してみたんだけどエラーが、、、

configure: WARNING: DBUS is missing.
configure: WARNING: PAM is missing.
configure: WARNING: Qt is missing.
configure: WARNING: Valgrind is missing.
configure: WARNING: readline is missing.
configure: WARNING: both tr1/functional and boost/function.hpp are missing.
configure: error: Required dependencies missing: boost/function.hpp


tr1やらboostって何って調べてみるにC++の標準ライブラリの拡張案Technical Report 1の実装のようだ。最近、C++に疎いので全然しらなかった。つ〜〜か、なんでg++には入ってないんだろう、、、老人にはビルドは辛いので是非標準でTR1を実装して頂きたい。

段々、面倒臭くなってきたorz

Boostのインストール


Boostの公式サイト(www.boost.org)からバージョン1.40.0のアーカイブをダウンロードして(おいおいソースtgzで40MBもあるよ)ビルドしようとしたんですが、cygwinで良く見たらboostパッケージあったよ。有難くパッケージインストールする。cygwinを良く見ましたが、D-BusとWvStreamsはパッケージは無いことを確認した。

D-Busのインストール


WvStreamsはD-Busを使うようなので、D-Busから先にインストールすることにした。

% ./configure --prefix=/usr
% make
% su make install


でいけるそうだ。gettextとexpat or libxml-2を必要とするらしい。cygwinのlibxml-2-develパッケージをインストールした。

で、ええいっ、、、っと"./configure"してみるも"./configure"が無いじゃ〜〜〜ん。autoconfなのかな。わからないのでcmakeディレクトリに下りて

% cd cmake
% cmake .


してみた。cmakeはうまくいったようだ。で、

% make


う〜〜む、ビルドエラーだ。

cmakeはだめみたいだ。"autoconf"にするか、、、、仕方なく、cygwinのautoconf、automake、libtool(libtoolizeが入っている)のパッケージを追加する。でコンフィグレーション用のスクリプトがあるようなので実行してみる。

% ./autogen.sh


MD4に関して警告が出たが無視してしまった。

Now type 'make' to compile dbus.


と出たので、どうやら"./configure"ファイルの生成と実行はうまくいったようだ。

% make


で、エラー。どうやらshared library版をつくるためのオプションが間違っているようだ。libtoolまわりの記述がいけないのかもしれない。面倒なのでstaticだけにしてしまう。

% ./configure --enable-static --disable-shared


で、

% make >& make.log &
% tail -f make.log


とりあえず、D-Busのビルドは順調に進んでいるように見える、、、と思ったらコケた。多分aclocal.m4、acinclude.m4のマクロが処理できていないっぽい。autoconfから戻るか。

さきほどの"autogen.sh"の実行時の警告では、

libtoolize: Consider adding `AC_CONFIG_MACRO_DIR([m4])' to configure.in and
libtoolize: rerunning libtoolize, to keep the correct libtool macros in-tree.
libtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am.


と出ていたので、"configure.in"をいじって

修正前
AC_INIT(dbus, [dbus_version])
修正後
AC_INIT(dbus, [dbus_version])
AC_CONFIG_MACRO_DIR([m4])


とすることにして、再度 "autogen.sh" を実行した。警告は無くなり無事 "configure" が実行されたようだ。(毎回こんなことを書いている。)また、staticライブラリのみにするようconfigureを再実行してビルドする。

% ./configure --enable-static --disable-shared
% make >& make.log &
% tail -f make.log


う〜むだめだエラーだ〜〜〜。dbus-sysdeps-unix.cがダメっぽい。

D-Busをソースからcygwinにインストールするのは諦めることにする。

たまたま、D-Busをちょっと古いけどcygwinパッケージにして配っているところを発見したのでこれを使ってみることにする。

http://ftp.daum.net/cygwin/release-2/dbus/


URLを指定して普通にはセットアップできなかったので、パッケージファイル(.bz2)を手でダウンロードして、

- dbus-1.2.16-1.tar.bz2
- libdbus1_3-1.2.16-1.tar.bz2
- libdbus1-devel-1.2.16-1.tar.bz2
を /tmp などで
% bunzip2 -dc dbus-1.2.16-1.tar.bz2 | tar xvf -
して、
% cp -r usr /
% cp -r etc/dbus-1 /etc
etc/{postinstall,preremove}を参考に
% mkdir -p /var/lib/dbus
% /usr/bin/dbus-uuidgen --ensure


すれば、D-Bus 1.2.16のパッケージはインストールできたのではないかと思う。

長かった、、、orz

再戦:WvStreamsのインストール



WvStreamsのインストールに戻ることにする。

% ./configure


D-Busは1.2.14以上が必要なようだ。D-Busが使えるようになっていることを一応確認した。

configure: WARNING: DBUS is missing.
configure: WARNING: PAM is missing.
configure: WARNING: Qt is missing.
configure: WARNING: Valgrind is missing.
configure: WARNING: readline is missing.
configure: WARNING: both tr1/functional and boost/function.hpp are missing.
configure: error: Required dependencies missing: boost/function.hpp


相変わらず、D-Busが無く、Boostも無いと怒られたorz

% ./configure --with-dbus --with-openssl


pkg-configがいるみたいだぞ。cygwinのパッケージを追加した。段々、頭きた。相変わらずTR1かBoostでは怒られる。

cygwinのBoostのパッケージのインクルードファイルの場所を見てみたら

/usr/include/boost がないので
% cd /usr/include
% ln -s boost-1_33_1/boost boost


これで、ようやく configure はうまく通った(やれやれ)。ビルドしてみる。

% make >& make.log &
% tail -f make.log


あらら、、、再度 configure しちゃってる(泣)。気長に待つことにする。あらら、やっぱりダメだった。

./include/wvtask.h:27:22: ucontext.h: No such file or directory
./include/wvtask.h:57: error: `ucontext_t' does not name a type


cygwinはUser Thread Contextが実装されていないので"ucontext.h"も無いため、そりゃビルドできないわけだ。

うむむ、残念、今日はこれくらいで、、、

方法が他に見つからないようならcygwinはあきらめて、Debian(Ubuntu)でビルドしてみることにします。

米国GSAでSCVP

DigitalIDNews | GSA rolling out PKI validation service
The General Services Administration will unveil Central Certificate Validator program that will of perform certificate path discovery and validation (PD-VAL) in compliance with RFC 5280 in support of PKI-based authentication mechanisms described in FIPS 201.


だそうだ、、、

米国政府調達庁(GSA)では証明書の認証パス検証のための検証サーバーであるSCVPが使えるようになるそうです。CoreStreetなのかな、、、ちょっとおもしろそう。

今更ながらNIST PKITS

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を使い続けなければならない場合にちょっと困っています。


Sun Java CertPathBuilderの例外のイケズ

あるデジタル証明書が信頼するルート証明書から辿って有効であるかどうか判定するために認証パス検証(Certification Path Validation)という処理を行います。

これは、HTTPSで保護されたサイトに接続する時やS/MIME署名メールを開く際に実は裏で行われているとっても重要な処理です。

Java ではCertPathBuilderCertPathValidatorというクラスがあり、仕組み的にはX.509公開鍵証明書に限らず汎用的に証明書のパスが検証できたり、いろいろな会社の実装したアルゴリズム、実装方法が選択できるようになっています。

Sun Java にはRFC 3280準拠のパス検証実装が含まれており、これに基づくX.509証明書のパス検証ができるようになっています。


CertPathBuilderのSunプロバイダのPKIXアルゴリズム
検証対象証明書、中間証明書、CRL、証明書やCRLを取得するLDAPディレクトリ、OCSPレスポンダの設定、トラストアンカとなるルート証明書群を指定することにより、自動的にパス構築と(狭義の)パス検証を同時に行うことにより検証対象証明書のパスが有効であるかを判定します。内部でCertPathValidatorの実装が使われているわけではありません。
CertPathValidatorのSunプロバイダのPKIXアルゴリズム
検証対象証明書からトラストアンカとなるルート証明書の一つ手前までの証明書のチェーン(CertPath)を与えることにより、(狭義の)パス検証を行います。


Sun PKIXのCertPathBuilderのパス構築をザックリ図にしたのが以下、、、

pathbuild02



SunプロバイダのCertPathBuilderのPKIXアルゴリズム実装なんですが、これが、またちょっとイケズな実装になっていてエラーメッセージというか例外処理がとても不親切なんです。

パスを構築するための証明書が不足していたり、CRLが取得できなかったり、失効していたり、期限切れだったり、鍵用途が間違っていたり、中間CA証明書にあるべき基本制約のcAフラグがTRUEでなかったりすると例外が発生しますが、どんな理由であっても全く同じ

sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target


という例外メッセージが表示されます。これじゃ何がエラー理由だったのかさっぱりわからない(^^;メッセージ "unable to find valid ..." でぐぐってみると皆さんエラー理由がわからず困っておられる様子、、、、

CertPathBuilderExceptionクラスではgetCause()メソッドにより、その例外が発生した元の原因となる例外を取得できるんですが、Sunの実装では深さ優先探索のせいか?!不親切なのか?!これが設定されていないため、パス構築・検証に失敗した原因がわからないんです。

パスの深さ優先探索とは


SunのCertPathBuilderのPKIXアルゴリズムの実装ではパスを見つける際、幅優先探索と深さ優先探索のうち深さ優先探索が使われています。(人工知能系の方、Prolog系の方はよくご存知、、、(^^;)

以下のような少し複雑なPKIモデルで考えて見ましょう。

cpb01



ここでは信頼するルート証明書からエンドエンティティ証明書まで3通りのパスがあるんですが、そのうち2つは無効なパスになっています。

・ROOT→AAAのCA証明書は証明書有効期限が切れているので無効
・ROOT→BBBのCA証明書は基本制約にcA=TRUEが無いため無効

SunのCertPathBuilderでパス構築した場合の、パス構築で辿ってみた順序を図説したのが以下です。

cpb02



Sun実装ではエンドエンティティ証明書から始めてトラストアンカであるルート証明書まで深さ優先探索でパス構築を試みます。深さ優先とは、ぶっちゃけちゃうと「行ける所まで行く」、
踏み出せばその一足が道となり、その一足が道となる。迷わず行けよ。行けばわかるさ。ありがとう!
的なアントニオ猪木の名言の様なアルゴリズムです。

(1) エンドエンティティ証明書HHH←GGGからパス構築開始
(2) CA証明書 GGG←EEE、EEE←BBB を辿る。
   その際に都度、個々の追加される証明書に対して署名値、
   識別名前の一致、鍵用途、各種制約の処理失効検証などを行う。
(3) BBB←ROOT証明書の基本制約拡張にcA=TRUEが無いのでGGGに戻る
(4) CA証明書 GGG←DDD、DDD←AAA を辿る。
(5) BBB←ROOT証明書は期限切れなのでGGGに戻る。
(6) CA証明書 GGG←FFF、FFF←CCC、CCC←ROOT を辿る。
(7) トラストアンカまで辿れたので(6)が有効なパスとなり
   パス構築・検証は完了。

幅優先の場合、途中選択肢を全て抱えたまま探索するので多くメモリを消費すると言われています。そうした意味では深さ優先はまぁ、妥当かなと、、、

Sun PKIX CertPathBuilder実装の例外のイケズ



ただ、次のようなケースの場合、パス構築を試みて結局は有効なパスが見つからないため「unable to find valid certification path to requested target(要求された対象(証明書)に対する有効な認証パスが見つかりません)」ということになります。

cpb03



この際のCertPathBuilderException例外のgetCause()には何も入っていないので、何故パス構築・検証に失敗したのかは(普通は)謎のままです。せめてパス構築失敗の元を最後に起こした例外がgetCause()に設定されていれば結構理由がわかるもんなんですけど、残念ながらそうなってません。深さ優先探索なので「前の例外なんかいちいち保存しておくかぁ!!」という実装なんでしょう。

最後に発生した例外をgetCause()に設定するだけだと下のようなケースでは本質的な問題がわからないケースもあるんですが、まぁ、それは現状の何も無いよりはましみたいな感じで残しておいて欲しいなぁ、、、、

cpb04



パス構築で辿った順序が何故わかるか?



では、何故上の方の図でSun PKIX CertPathBuilderが辿ったパス構築順序がわかったかというとログを見たからなんです。CertPathBuilderやCertPathValidatorでは、以下のように "-Djava.security.debug=certpath"のオプションを付ければデバッグログが標準出力に表示されます。
% java -Djava.security.debug=certpath [CertPathを使ったJavaプログラム]


ただ、ここから出てくるログは今まで見てきたログの中では「中の下」ぐらいのダメさ加減で、多分観てもうんざりするだけだと思います。

で、パス構築で辿った順を観るには以下ようなdepthFirstSearchForwardの部分を見ればよいです。


certpath: SunCertPathBuilder.depthFirstSearchForward(CN=HHH, C=JP, State [
issuerDN of last cert: null
traversedCACerts: 0
init: true
keyParamsNeeded: false
subjectNamesTraversed:
[]]
)
・・・中略・・・
certpath: SunCertPathBuilder.depthFirstSearchForward(CN=ROOT, C=JP, State [
issuerDN of last cert: CN=ROOT, C=JP
traversedCACerts: 3
init: false
keyParamsNeeded: false
subjectNamesTraversed:
[CN=GGG, C=JP, CN=HHH, C=JP, CN=CCC, C=JP, CN=FFF, C=JP]]
)


これを観ていくとHHH→GGG→EEE→BBB×後戻り→DDD→AAA×後戻り→FFF→CCC→ROOT○と二度の失敗にもめげずに辿ったんだぁなぁ、、、頑張ったなぁ、、、、よしよし、、、という風に感慨深いものがあります。

ログを観てもAAA←ROOTやBBB←ROOTの証明書で基本制限に違反しているからとか、期限切れだからとか、そうした理由はログには全く書かれていません。

3本パスがあるうち何故その順序で選んだか?



HHH→GGGと辿って、次にEEE、DDD、FFFのどれを選んでも良いような気がしますが、必ずEEE、DDD、FFFの順序になりました。

証明書を探す際の入れ物としてArrayListをベースにしたCollectionCertStoreを使っていたんですが、そこに中間証明書を加える順序に依存しているのかもしれません。

結局 CertPathBuilder が出す例外は全く役に立たない



というわけで、Sun PKIX CertPathBuilderの吐く例外やログはというのは、どのような理由でパス構築・検証の失敗したのかわからないため使い物にならず、、ただ「unable to find valid certification path to requested target」の例外メッセージが空しくも得られるだけということがおわかり頂けたかと思います。

ではパス構築・検証がダメ理由を知りたきゃどうするか?



SSLサーバー認証では多くの場合SSLサーバー証明書・必要な中間CA証明書・ルート証明書がチェーンの形でごっそり送られてきますし、S/MIME署名メールやCAdES/XAdES長期署名の場合なんかも検証すべき証明書チェーンが概ねわかっている時があります。

自分で証明書チェーンを
・主体者・発行者の名前の一致で
・署名値の一致で
作ることもできることがあります。

そんなときは、自分で簡易パス構築で証明書チェーン作ってCertPathオブジェクトを生成し、CertPathValidatorで(狭義の)パス検証だけを行うのがいいように思います。

例えば、今回のケースで言えば

自前でパス構築だけして、パス検証はCertPathValidatorを使えば、

HHH→GGG→DDD→AAA→ROOTのパスの期限切れエラーなら
×失敗: timestamp check failed
×失敗理由詳細: NotAfter: Sun Jan 02 09:00:00 JST 2000


HHH→GGG→EEE→BBB→ROOTのパスの基本制約のcA=TRUE不足エラーなら
×失敗: basic constraints check failed: this is not a CA certificate


と、例外CertPathValidatorExceptionのgetMessage()で理由がわかってスッキリします。

今回はこれまたマニアックなネタでごめんなさいね。

自分でもパス構築してみたくなったら今回の証明書セットはこちらからダウンロードできます。

<参考リンク>
Sun J2SE 6 - Java PKI APIプログラマーズガイド
Sun J2SE 6 - Java暗号化アーキテクチャー(JCA) リファレンスガイド
Sun J2SE 6 - java.security.certパッケージ
IPA: 電子政府情報セキュリティ相互運用支援技術の開発
 ・GPKIアプリケーション実装ガイド報告書 (PDF 831KB)
   5章:Javaによる証明書パス構築・パス検証の実装の説明

Challenge PKI Test Suiteの移行1

テスト用の証明書を使う際に、未だに

Challenge PKI Test Suite

というツールを騙し騙し使っています。

証明書生成の部分については最初にリリースされた時から、

CもPerlも全部書き直しており、オリジナルのコードとは

かなり違うものになっています。

  • オリジナルのASN.1エンコーダーを使っており、AiCryptoのASN.1データ生成は使うのをやめてしまった。

  • そのため自由に証明書の識別名を設定できる。例えば、個々のRDNのDirectoryStringTypeが別にできたり、改行を含むアスキーアート証明書も可能

  • 認証パス毎一括生成したり、様々なユーティリティーを追加

で、オリジナルから脱却できないままでいるのが、

署名値の生成の部分でAiCryptoを使ったままになっています。

そろそろ困っているのが、SHA2やECDSAの対応で、

AiCrypto自体がもうSHA2を含むような改変が今後

加わらないでしょうから、OpenSSLかBouncyCastleか

他の暗号ライブラリに移行しなければならないのかな、、、

と考えています。

今、思うに最初からBouncyCastleでやっておけばよかったな、、、と、、、

でも、そのころはJythonなんていう便利なものは無かったのか、、、

OpenSSLについては、DSAは別としてRSAの部分は

かなりわかってきたので、tbsCertificateの生成はそのままに

署名の部分だけ移行してやればいけそうな雰囲気。

BouncyCastleを使った場合には、

設定ファイルから生成する部分を

一から書き直しになるんですが、

将来的にはこちらの方がメンテしやすいかな、、、と、、、

ううむ、悩むところです。

最新記事
Categories
Archives
Twitter
記事Google検索

本ブログ内をGoogle検索
Yahoo!アクセス解析
Travel Advisor
記事検索
QRコード
QRコード
  • ライブドアブログ