コンテンツにスキップ
getnextpdf.com

Enterprise エディション

ASiC トラストバインディング

ASiC コンテナは、署名対象のファイルと、それを保護する署名とを束ねます。難しい問いは「署名が計算上正しいか」ではなく、「誰がその署名者を裏付けているか」です。NextPDF\Enterprise\Security\Asic\AsicTrustBinder は、まさにその問いに答えます。コンテナ署名から取り出した署名証明書、トラステッドリスト、検証時刻を渡すと、AsicTrustBindingResult が返ります。すなわち、信頼済み/非信頼の検証結果、判定に用いたアンカーバンドルのバージョン、そして機械可読な理由です。すべての拒否がその原因を明示するため、監査エビデンスはおのずと書き上がります。

意図的に設けた境界がひとつあり、あらかじめ述べておく価値があります。この API は ASiC コンテナをパースしません。コンテナを開いて署名証明書を抽出するのはお使いのツールであり、NextPDF はトラスト判定を担います。

この機能は NextPDF Enterprisenextpdf/enterprise)に同梱され、Enterprise ティアのライセンスエンベロープで有効化されます。そのエンタイトルメントを持たないデプロイでは、この機能のクラスはロードされません。エディションを比較してライセンスを取得

Terminal window
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 つのフェイルクローズドなゲートが動作します。

  1. TSL の鮮度。 NextUpdate の時点が経過したトラステッドリストは、期限切れとして破棄しなければなりません。AsicTrustBinder::verify() は、単一のアンカーを導出する前に、指定された検証時刻で鮮度をアサートします。古いリスト、あるいは明示的な UTC 指示子を持たない NextUpdate 値は、TslParseException をスローします。
  2. 署名者の有効期間。 RFC 5280 のパス検証は、証明書の有効期間が検証時刻を含むことを要求します。暗号的には無傷でも、その時点で証明書が期限切れ、またはまだ有効でなかった署名は、厳密な理由コードとともに拒否されます。

そのうえで初めて、バインダーは各アンカーに対して署名証明書をテストします。一致すれば trusted: true と理由 anchor_signature_match を返します。一致しなければ trusted: false と理由 no_anchor_chain を返します。

要となる設計上の決定は、コンテナのメカニクスとトラスト判定とを厳格に分離し、そのトラスト判定に時刻を明示させることです。コンテナ形式は多様ですが(ASiC-S、ASiC-E、CAdES または XAdES のペイロード)、トラストの問いは 1 つの不変の核心です。すなわち、この証明書は、ある明示された時点で、鮮度の保たれたトラステッドリスト由来のアンカーへ連鎖するか、ということです。その核心を ZIP や XML のパースから切り離しておくことで、網羅的にテストでき、あらゆるゲートでフェイルクローズドにできるほど小さく保てます。同じ理由から、暗黙の now デフォルトは禁じられます。検証時刻は検証結果を左右するため、呼び出し側がそれを担わなければなりません。鮮度はオプションのコラボレーターではなく、アンカー導出パスそのものの内部でアサートされるため、いかなる生成パスもそれを回避できません。

設計の背景: デジタル署名が署名者を証明する仕組み

コンストラクタは、トラステッドリストをアンカーバンドルへ変換するアンカープロバイダーを受け取ります。

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() から取得してください。手作業で構築しないでください。

public function buildBundle(TslDocument $tsl, DateTimeImmutable $now): EnterpriseCaTrustAnchorBundle

スローまたは失敗する条件: TSL が古い場合、その NextUpdate が正規の UTC 値でない場合、または有効な CA/QC サービスがない場合、TslParseException をスローします。

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を参照)。

asic-trust-binding-quickstart.php
<?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 サービスが発行した署名者に対する想定出力:

TRUSTED
Anchors: tsl-eu-seq42
Reasons: anchor_signature_match

トラステッドリストごとにアンカーバンドルを一度だけ導出し、その後に多数のコンテナ署名者をそれに対して検証します。古い、または使用不能な TSL が 1 つあればバッチ全体がフェイルクローズドになり、個々の署名者の問題はコンテナ単位で表面化します。

asic-trust-binding-batch.php
<?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を参照)、署名ツールで暗号的に署名を検証し、ポリシーに応じて失効確認を追加してください。
  • 検証時刻は慎重に選んでください。 検証結果は、渡す時刻の関数です。攻撃者が影響を与えられる時計ではなく、信頼できるエビデンス(適格タイムスタンプ、アーカイブ記録)から導出してください。
  • エビデンス出力は決定論的。 trustedanchorBundleVersionreasons は安定した機械可読な値であり、署名付き監査ログに適します。

AsicTrustBinder は、ETSI EN 319 162-1(ASiC ベースラインコンテナ)、ETSI EN 319 122-1(CAdES ベースライン署名)、および ETSI TS 119 612(トラステッドリスト)に沿ったワークフローをサポートし、指定された検証時刻で RFC 5280 の有効期間ゲートを適用します。

サポートは適合ではなく、適合は認証ではありません。NextPDF は本ページが記述するチェックを実装していますが、いかなる機関によってもこれらの標準に対して認証されておらず、この API の使用そのものによって出力が eIDAS またはその他の規制のもとで「適格」または法的に有効になるわけではありません。NextPDF はいかなる認証も保持せず、いかなる認証も付与しません。完全な検証プロセスが特定の法的要件や調達要件を満たすかどうかは、あなたの評価者による判断事項です。

トラストバインディングは 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 を返します。
  • すべての結果は、trustedanchorBundleVersion、そして少なくとも 1 つの理由コードを伴う AsicTrustBindingResult です。理由のない検証結果はありません。
  • 非信頼の署名者は返され、スローされることはありません。使用不能なトラスト材料はスローされ、検証結果として返されることはありません。
  • コンテナのパースはこの API 内では決して発生しません。入力は抽出済みの PEM、トラステッドリスト、検証時刻です。

NextPDF Core は、CaTrustAnchorBundle 契約を通じて明示的にピン留めしたトラストアンカーに対して、PDF(CMS/PAdES)署名を検証します — Core securityを参照してください。Core にはトラステッドリスト(TSL)の取り込みも、ASiC 固有のトラストバインディングもありません。Core 単体では、PDF 署名検証のために独自のアンカーセットを維持できますが、ETSI TS 119 612 のトラステッドリストからアンカーを導出し、ASiC コンテナの署名者をそれに紐付けるには NextPDF Enterprise が必要です。

本ページは、外部から観測可能な挙動と、サポートされる公開 API サーフェスのみを記述します。内部の名前空間パス、ヘルパークラス、メカニズムの表、ランブックのファイル名、チケットのプレフィックスは対象外です。