Enterprise エディション
ASiC トラストバインディング
ASiC コンテナは、署名対象のファイルと、それを保護する署名とを束ねます。難しい問いは「署名が計算上正しいか」ではなく、「誰がその署名者を裏付けているか」です。NextPDF\Enterprise\Security\Asic\AsicTrustBinder は、まさにその問いに答えます。コンテナ署名から取り出した署名証明書、トラステッドリスト、検証時刻を渡すと、AsicTrustBindingResult が返ります。すなわち、信頼済み/非信頼の検証結果、判定に用いたアンカーバンドルのバージョン、そして機械可読な理由です。すべての拒否がその原因を明示するため、監査エビデンスはおのずと書き上がります。
意図的に設けた境界がひとつあり、あらかじめ述べておく価値があります。この API は ASiC コンテナをパースしません。コンテナを開いて署名証明書を抽出するのはお使いのツールであり、NextPDF はトラスト判定を担います。
提供とライセンス
「提供とライセンス」という見出しのセクションこの機能は NextPDF Enterprise(nextpdf/enterprise)に同梱され、Enterprise ティアのライセンスエンベロープで有効化されます。そのエンタイトルメントを持たないデプロイでは、この機能のクラスはロードされません。エディションを比較してライセンスを取得。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/enterprise有効化には Enterprise ライセンスエンベロープが必要です。インストールと認証を参照してください。本ページのクラスは NextPDF\Enterprise\Security\Asic および NextPDF\Enterprise\Security\Tsl 配下にあります。
概念の概観
「概念の概観」という見出しのセクションASiC(Associated Signature Containers、ETSI EN 319 162-1)は、データファイルと署名を 1 つのアーカイブにまとめます。ベースライン ASiC コンテナは、CAdES または XAdES のベースライン署名のみを埋め込みます。CAdES ベースライン署名は、その署名証明書を SignedData.certificates の内部に保持するため、署名が整形式でコンテナツールがサポートしている場合、検証者はそれをコンテナの署名から抽出することが期待されます。その抽出された証明書が、この API の入力です。
トラストの供給源は ETSI TS 119 612 のトラステッドリスト(TSL)です。これは、トラストサービスプロバイダーとそのサービス証明書を列挙する署名済み XML ドキュメントです。NextPDF\Enterprise\Security\Tsl\TslTrustAnchorProvider は、パース済みの TslDocument をアンカーバンドルへ変換します。granted ステータスかつ CA/QC サービスタイプの両方を満たすサービスのみが、アンカーセットの元になります。バンドルは、TSL のシーケンス番号と地域から導出したバージョン文字列に加え、SHA-256 の完全性ダイジェストを保持します。
いずれのアンカー比較よりも前に、2 つのフェイルクローズドなゲートが動作します。
- TSL の鮮度。
NextUpdateの時点が経過したトラステッドリストは、期限切れとして破棄しなければなりません。AsicTrustBinder::verify()は、単一のアンカーを導出する前に、指定された検証時刻で鮮度をアサートします。古いリスト、あるいは明示的な UTC 指示子を持たないNextUpdate値は、TslParseExceptionをスローします。 - 署名者の有効期間。 RFC 5280 のパス検証は、証明書の有効期間が検証時刻を含むことを要求します。暗号的には無傷でも、その時点で証明書が期限切れ、またはまだ有効でなかった署名は、厳密な理由コードとともに拒否されます。
そのうえで初めて、バインダーは各アンカーに対して署名証明書をテストします。一致すれば trusted: true と理由 anchor_signature_match を返します。一致しなければ trusted: false と理由 no_anchor_chain を返します。
なぜこの設計なのか
「なぜこの設計なのか」という見出しのセクション要となる設計上の決定は、コンテナのメカニクスとトラスト判定とを厳格に分離し、そのトラスト判定に時刻を明示させることです。コンテナ形式は多様ですが(ASiC-S、ASiC-E、CAdES または XAdES のペイロード)、トラストの問いは 1 つの不変の核心です。すなわち、この証明書は、ある明示された時点で、鮮度の保たれたトラステッドリスト由来のアンカーへ連鎖するか、ということです。その核心を ZIP や XML のパースから切り離しておくことで、網羅的にテストでき、あらゆるゲートでフェイルクローズドにできるほど小さく保てます。同じ理由から、暗黙の now デフォルトは禁じられます。検証時刻は検証結果を左右するため、呼び出し側がそれを担わなければなりません。鮮度はオプションのコラボレーターではなく、アンカー導出パスそのものの内部でアサートされるため、いかなる生成パスもそれを回避できません。
設計の背景: デジタル署名が署名者を証明する仕組み。
API サーフェス
「API サーフェス」という見出しのセクションAsicTrustBinder
「AsicTrustBinder」という見出しのセクションコンストラクタは、トラステッドリストをアンカーバンドルへ変換するアンカープロバイダーを受け取ります。
public function __construct( private readonly TslTrustAnchorProvider $anchorProvider,) {}主要なエントリーポイントは、署名者証明書をトラステッドリストに対して検証します。
public function verify( string $signerCertPem, TslDocument $tsl, DateTimeInterface $validationTime,): AsicTrustBindingResult$signerCertPem— 空でない PEM 文字列。ASiC 署名から取り出した署名証明書。$tsl— パース済みで認証済みのトラステッドリスト。$validationTime— 署名者証明書の有効期間が含んでいなければならない時点。デフォルトはありません。
スローまたは失敗する条件: TSL が古い(NextUpdate が経過)場合、NextUpdate が正規の UTC 値でない場合、またはリストに有効な CA/QC サービスが含まれない場合、NextPDF\Enterprise\Security\Tsl\TslParseException をスローします。非信頼の署名者はスローせず、trusted: false と理由コードを伴う結果を返します。
バッチ処理では、事前構築したバンドルに対して検証します。
public function verifyAgainstBundle( string $signerCertPem, EnterpriseCaTrustAnchorBundle $bundle, DateTimeInterface $validationTime,): AsicTrustBindingResultスローまたは失敗する条件: それ自体は例外をスローしません。すべての結果は AsicTrustBindingResult です。バンドルは TslTrustAnchorProvider::buildBundle() から取得してください。手作業で構築しないでください。
TslTrustAnchorProvider
「TslTrustAnchorProvider」という見出しのセクションpublic function buildBundle(TslDocument $tsl, DateTimeImmutable $now): EnterpriseCaTrustAnchorBundleスローまたは失敗する条件: TSL が古い場合、その NextUpdate が正規の UTC 値でない場合、または有効な CA/QC サービスがない場合、TslParseException をスローします。
AsicTrustBindingResult
「AsicTrustBindingResult」という見出しのセクションpublic function __construct( public bool $trusted, public string $anchorBundleVersion, public array $reasons,) {}$reasons は、機械可読なコードの list<non-empty-string> です。$anchorBundleVersion は使用したアンカーセットを、tsl-<territory>-seq<N> の形式で記録します(例: tsl-eu-seq42)。
| 理由コード | 意味 |
|---|---|
anchor_signature_match | 署名者証明書が TSL 由来のアンカーに対して検証成功。信頼済み。 |
no_anchor_chain | バンドル内のどのアンカーも署名者証明書を検証できない。非信頼。 |
signer_cert_expired | 検証時刻が証明書の notAfter より後。非信頼。 |
signer_cert_not_yet_valid | 検証時刻が証明書の notBefore より前。非信頼。 |
cannot_parse_signer_cert | 渡された PEM が X.509 証明書としてパースできない。非信頼。 |
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクションお使いのコンテナツールが署名証明書をすでに抽出済みとします。それを、取得・認証済みの加盟国トラステッドリストに紐付けます(Trusted listsを参照)。
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Asic\AsicTrustBinder;use NextPDF\Enterprise\Security\Tsl\TslParseException;use NextPDF\Enterprise\Security\Tsl\TslTrustAnchorProvider;use NextPDF\Enterprise\Security\Tsl\TslXmlParser;
// Extracted by YOUR tooling from META-INF/signature.p7s or signatures.xml.$signerCertPem = (string) file_get_contents(__DIR__ . '/asic-signer.pem');
// A trusted list you have already fetched and authenticated.$tslXml = (string) file_get_contents(__DIR__ . '/member-state-tsl.xml');
$binder = new AsicTrustBinder(new TslTrustAnchorProvider());
try { $tsl = (new TslXmlParser())->parse($tslXml);
$result = $binder->verify( signerCertPem: $signerCertPem, tsl: $tsl, validationTime: new DateTimeImmutable('2026-07-03T12:00:00Z'), );} catch (TslParseException $e) { // Fail closed: stale TSL, malformed NextUpdate, or no active CA/QC services. fwrite(STDERR, 'Trusted list rejected: ' . $e->getMessage() . PHP_EOL); exit(1);}
echo $result->trusted ? "TRUSTED\n" : "NOT TRUSTED\n";echo 'Anchors: ' . $result->anchorBundleVersion . "\n";echo 'Reasons: ' . implode(', ', $result->reasons) . "\n";一覧に載る CA/QC サービスが発行した署名者に対する想定出力:
TRUSTEDAnchors: tsl-eu-seq42Reasons: anchor_signature_matchコードサンプル — 本番
「コードサンプル — 本番」という見出しのセクショントラステッドリストごとにアンカーバンドルを一度だけ導出し、その後に多数のコンテナ署名者をそれに対して検証します。古い、または使用不能な TSL が 1 つあればバッチ全体がフェイルクローズドになり、個々の署名者の問題はコンテナ単位で表面化します。
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Asic\AsicTrustBinder;use NextPDF\Enterprise\Security\Asic\AsicTrustBindingResult;use NextPDF\Enterprise\Security\Tsl\TslDocument;use NextPDF\Enterprise\Security\Tsl\TslParseException;use NextPDF\Enterprise\Security\Tsl\TslTrustAnchorProvider;use NextPDF\Enterprise\Security\Tsl\TslXmlParser;
/** * @param array<string, non-empty-string> $signerPemsByContainer PEM per container path. * @return array<string, AsicTrustBindingResult> * @throws TslParseException When no anchor set can be derived from the TSL. */function bindBatch( TslDocument $tsl, array $signerPemsByContainer, DateTimeImmutable $validationTime,): array { $provider = new TslTrustAnchorProvider();
// Derive the anchor set ONCE; a throw here means the trusted list itself // is unusable at this validation time. $bundle = $provider->buildBundle($tsl, $validationTime);
$binder = new AsicTrustBinder($provider);
$results = []; foreach ($signerPemsByContainer as $container => $signerPem) { $results[$container] = $binder->verifyAgainstBundle( signerCertPem: $signerPem, bundle: $bundle, validationTime: $validationTime, ); }
return $results;}
$tsl = (new TslXmlParser())->parse( (string) file_get_contents(__DIR__ . '/member-state-tsl.xml'),);
$signerPems = [ 'invoice-2026-06.asice' => (string) file_get_contents(__DIR__ . '/signer-a.pem'), 'tender-2019.asice' => (string) file_get_contents(__DIR__ . '/signer-b.pem'),];
try { $results = bindBatch( tsl: $tsl, signerPemsByContainer: $signerPems, validationTime: new DateTimeImmutable('now', new DateTimeZone('UTC')), );} catch (TslParseException $e) { // Fail closed for the WHOLE batch: no trustworthy anchor set exists. fwrite(STDERR, 'Anchor derivation failed: ' . $e->getMessage() . PHP_EOL); exit(1);}
foreach ($results as $container => $result) { printf( "%s => %s (%s; anchors %s)\n", $container, $result->trusted ? 'trusted' : 'rejected', implode(',', $result->reasons), $result->anchorBundleVersion, );}署名者証明書の 1 つが期限切れの場合の想定出力:
invoice-2026-06.asice => trusted (anchor_signature_match; anchors tsl-eu-seq42)tender-2019.asice => rejected (signer_cert_expired; anchors tsl-eu-seq42)エッジケースと落とし穴
「エッジケースと落とし穴」という見出しのセクション- 検証時刻は必須かつ決定的。 暗黙の
nowデフォルトはありません。2019 年に検証成功した署名も、notAfterを過ぎた 2026 年の時点で検証するとsigner_cert_expiredを報告します。過去の資料については、実時計ではなく、エビデンスが裏付ける時刻(例: 存在証明の時刻)を渡してください。 - 古い TSL はスローし、「非信頼」の検証結果ではありません。
verify()またはbuildBundle()からのTslParseExceptionは、トラストの供給源が使用不能であることを意味します。これは運用上の障害として扱い、リストを更新してください。署名者の拒否として記録しないでください。 - アンカーは直接の発行者としてテストされます。 各アンカーは、署名者証明書に署名した証明書として試行されます。EU 加盟国の TSL は発行元の CA/QC サービス証明書を列挙するため、エンドエンティティの適格証明書は通常、直接一致します。一覧に載る有効な CA/QC サービスそのものではない中間 CA が発行した署名者は、
no_anchor_chainになります。 - アンカー導出は厳格にフィルタリングします。 撤回されたサービス、または CA/QC 以外のタイプのサービスは、決してアンカーになりません。有効な CA/QC セットが空のリストは、空のバンドルを生成するのではなくスローします。
NextUpdateは正規の UTC でなければなりません。 明示的なZまたは数値オフセット指示子を持たない値は、フェイルクローズドで拒否され、サーバーのローカルタイムゾーンで再解釈されることはありません。- 不正な入力は厳密に劣化します。 パースできない PEM は
cannot_parse_signer_certを返し、まだ有効でない証明書は期限切れの証明書と区別されます。 anchorBundleVersionを記録してください。 これは各検証結果の背後にある正確なアンカーセット(tsl-<territory>-seq<N>)を示し、まさに監査人が求めるものです。
セキュリティ上の注意
「セキュリティ上の注意」という見出しのセクション- 設計上フェイルクローズド。 鮮度はいずれのアンカー導出よりも前にアサートされます。署名者の有効性ゲートはいずれのアンカー比較よりも前に動作します。使用不能なトラスト材料はスローされ、疑わしい署名者は理由とともに拒否されます。いかなるパスも暗黙のパスへ劣化しません。
- トラストバインディングは 1 つのレイヤーであり、検証の全体ではありません。 この API は、コンテナ内容に対する CAdES 署名値を検証せず、失効確認(CRL や OCSP のルックアップ)を行わず、TSL ドキュメント自体の認証も行いません。まずトラステッドリストのパイプラインでリストを認証し(Trusted listsを参照)、署名ツールで暗号的に署名を検証し、ポリシーに応じて失効確認を追加してください。
- 検証時刻は慎重に選んでください。 検証結果は、渡す時刻の関数です。攻撃者が影響を与えられる時計ではなく、信頼できるエビデンス(適格タイムスタンプ、アーカイブ記録)から導出してください。
- エビデンス出力は決定論的。
trusted、anchorBundleVersion、reasonsは安定した機械可読な値であり、署名付き監査ログに適します。
AsicTrustBinder は、ETSI EN 319 162-1(ASiC ベースラインコンテナ)、ETSI EN 319 122-1(CAdES ベースライン署名)、および ETSI TS 119 612(トラステッドリスト)に沿ったワークフローをサポートし、指定された検証時刻で RFC 5280 の有効期間ゲートを適用します。
サポートは適合ではなく、適合は認証ではありません。NextPDF は本ページが記述するチェックを実装していますが、いかなる機関によってもこれらの標準に対して認証されておらず、この API の使用そのものによって出力が eIDAS またはその他の規制のもとで「適格」または法的に有効になるわけではありません。NextPDF はいかなる認証も保持せず、いかなる認証も付与しません。完全な検証プロセスが特定の法的要件や調達要件を満たすかどうかは、あなたの評価者による判断事項です。
FIPS モードでの挙動
「FIPS モードでの挙動」という見出しのセクショントラストバインディングは X.509 の証明書署名チェックをインプロセスで実行します。これは Enterprise の FIPS モードランタイムガードを経由せず、FIPS モードを有効にしても挙動は変わりません。これは FIPS 検証済みの暗号サービスではなく、FIPS 140 認証も主張されません。FIPS 義務のあるデプロイは、この API のスコープをそれに応じて定め、FIPS 140-2/3 暗号ポリシーを参照してください。
verify()は、指定された検証時刻で鮮度の保たれた TSL からのみアンカーを導出します。古い、または不正なリストは、いかなるアンカーも存在する前にTslParseExceptionをスローします。- アンカーは、granted ステータスかつ CA/QC サービスタイプの TSL サービスからのみ導出されます。有効セットが空の場合はスローします。
- 署名者証明書の有効期間は検証時刻を含まなければなりません。違反は
signer_cert_expiredまたはsigner_cert_not_yet_validを返します。 - すべての結果は、
trusted、anchorBundleVersion、そして少なくとも 1 つの理由コードを伴うAsicTrustBindingResultです。理由のない検証結果はありません。 - 非信頼の署名者は返され、スローされることはありません。使用不能なトラスト材料はスローされ、検証結果として返されることはありません。
- コンテナのパースはこの API 内では決して発生しません。入力は抽出済みの PEM、トラステッドリスト、検証時刻です。
Core でのフォールバック
「Core でのフォールバック」という見出しのセクションNextPDF Core は、CaTrustAnchorBundle 契約を通じて明示的にピン留めしたトラストアンカーに対して、PDF(CMS/PAdES)署名を検証します — Core securityを参照してください。Core にはトラステッドリスト(TSL)の取り込みも、ASiC 固有のトラストバインディングもありません。Core 単体では、PDF 署名検証のために独自のアンカーセットを維持できますが、ETSI TS 119 612 のトラステッドリストからアンカーを導出し、ASiC コンテナの署名者をそれに紐付けるには NextPDF Enterprise が必要です。
公開範囲の境界
「公開範囲の境界」という見出しのセクション本ページは、外部から観測可能な挙動と、サポートされる公開 API サーフェスのみを記述します。内部の名前空間パス、ヘルパークラス、メカニズムの表、ランブックのファイル名、チケットのプレフィックスは対象外です。
- Trusted lists — アンカープロバイダーに供給する TSL の取得・認証・パース。
- Signature verification — PDF 署名向けの Enterprise 検証サーフェス。
- FIPS 140-2/3 暗号ポリシー — Enterprise の FIPS モードの姿勢。
- デジタル署名が署名者を証明する仕組み — 第一原理からの背景。
- 長期検証 — 検証時刻と保全されたエビデンスが重要な理由。