コンテンツにスキップ

セキュリティ / 署名:CMS、RFC 3161 タイムスタンプ、LTV、トラスト

このページでは、NextPDF Core に同梱される署名サーフェスについて説明します。対象は、CMS 署名の生成、RFC 3161 タイムスタンプの適用、RFC 5280 に照らした証明書チェーンの検証、OCSP と CRL による失効確認です。これは動作レベルの説明です。Core の実装クラスは内部向けです。本番コードが利用すべきなのは SignerInterface コントラクトであり、具象型である NextPDF\Security\Signature ではありません。生成された署名が検証に合格するかどうかは、検証側と、その検証側に設定されたトラストアンカーによって決まります。その結果は生成側では制御できないため、このページでは重要な箇所すべてでその点を明示します。

Terminal window
composer require nextpdf/core:^3

Core はバイト範囲から CMS SignedData 構造を構築し、それを DER エンコードして署名ディクショナリの Contents エントリーに格納します — ISO 32000-2 §12.8.1。この構造は SignerInfo の署名付き属性を含み、そこには content-type と message-digest が含まれます — RFC 5652 §5.3。検証側はコンテンツのダイジェストを再計算し、それを message-digest 属性と比較します。署名が有効であるためには、この比較が一致しなければなりません — RFC 5652 §5.4。SignerInfo は、ダイジェストアルゴリズム識別子と署名付き属性ブロックも保持します — RFC 5652 §5。Core に同梱されたソフトウェアパスでは、phpseclib3 を介して RSA、RSASSA-PSS、ECDSA、Ed25519 の署名を行います。

RFC 3161 タイムスタンプは、タイムスタンプ局(Time-Stamping Authority)との要求・応答のやり取りであり、TSTInfo 構造を返します — RFC 3161 §2.4.1。各トークンは、発行元の TSA において一意な serialNumber — RFC 3161 §2.4.2 — と、UTC で表され、トークンが作成された瞬間を示す genTime — RFC 3161 §2.4.2 — を保持します。

トラスト検証は 2 つのチェックで構成されます。パス検証は、署名者証明書からトラストアンカーまでをたどり、基本制約とパス構築の入力を確認します — RFC 5280 §6.1。失効確認は、OCSP レスポンダーに問い合わせるか、CRL を読み取ります。OCSP レスポンスは goodrevokedunknown のいずれかを報告し — RFC 6960 §2.2 — レスポンスの thisUpdatenextUpdate がそのステータスの鮮度を画定します — RFC 6960 §4.2。トラストアンカーと失効の鮮度ポリシーは、呼び出し側から供給されます。エンジンは与えられた入力に対して検証を行い、組み込みのトラストリストは同梱しません。

種別役割安定性導入バージョン
SignerInterfaceインターフェイス(NextPDF\Contracts呼び出し側が依存する署名コントラクト安定1.0.0
SignatureLevelenumPAdES レベルのセレクターおよび利用可否プローブ安定1.0.0
Rfc5280PathValidatorインターフェイス証明書パス検証のエントリーポイント(validate(...)安定(3.1.0 で凍結)3.1.0
RevocationStatusenumOCSP / CRL の結果:good、revoked、unknown安定3.1.0
CaTrustAnchorBundle呼び出し側が供給するトラストアンカーの集合安定3.1.0
TstInfo解析済みの RFC 3161 タイムスタンプフィールド安定3.2.0

SignerInterface::sign()SignatureResult を返します。返された結果の toHex()/Contents の 16 進文字列を生成し、cmsSignedData プロパティは生の DER バイト列を保持します。この動作を実装する具象 NextPDF\Security\Signature クラスは内部向けです(モジュールマニフェストで stability: internal)。このクラスは公開 API の一部ではなく、メジャーバージョンを上げずに変更される場合があります。上記のコントラクトと enum に依存してください。

examples/contracts/signing-quickstart.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;
/**
* Produce the CMS SignedData hex for a PDF /Contents field.
*
* @param SignerInterface $signer A Core or Premium signer.
* @param string $byteRange The PDF byte range to sign.
*
* @return string Hex-encoded CMS SignedData.
*/
function sign(SignerInterface $signer, string $byteRange): string
{
return $signer->sign($byteRange)->toHex();
}

呼び出し側はコントラクトに依存します。Core のソフトウェア署名者と Premium の HSM 署名者はいずれも SignerInterface を満たすため、このコードはエディションが変わっても変わりません。

examples/contracts/signing-trust.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;
use NextPDF\Contracts\TimestampProviderInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class TimestampedSigner
{
public function __construct(
private SignerInterface $signer,
private TimestampProviderInterface $timestamps,
private LoggerInterface $logger,
) {}
/**
* Sign a byte range, then timestamp the CMS structure.
*
* The timestamp's trust still depends on the verifier accepting the
* Time-Stamping Authority; this method only produces the structure.
*
* @param string $byteRange The PDF byte range to sign.
*
* @return array{cms: string, tst: string}
*/
public function sign(string $byteRange): array
{
try {
$signature = $this->signer->sign($byteRange);
$token = $this->timestamps->getTimestamp($signature->cmsSignedData);
return ['cms' => $signature->toHex(), 'tst' => $token];
} catch (NextPdfException $e) {
$this->logger->error('Signing failed', ['error' => $e->getMessage()]);
throw $e;
}
}
}

タイムスタンププロバイダーは注入されるため、デプロイメントは独自のタイムスタンプ局を固定できます。catch ブロックはログに記録して再スローします。失敗を決して握りつぶさず、署名パスをフェイルクローズのまま保ちます。

  • 生成された署名は、検証済み署名ではありません。パス検証と失効確認は、検証側のトラストアンカーを用いて検証側で実行されます。生成側はその結果を主張できません。
  • バイト範囲のダイジェストは、署名値を除外します。Contents のオクテットを含むダイジェストは検証に合格できません — ISO 32000-2 §12.8.1。
  • 失効ステータスには鮮度ウィンドウがあります。OCSP レスポンスは、その thisUpdate / nextUpdate の間隔が示す時点までしか最新ではありません — RFC 6960 §4.2。古いレスポンスは、検証時点で行う新たな確認の代わりにはなりません。
  • エンジンは組み込みのトラストリストを同梱しません。CaTrustAnchorBundle は呼び出し側が供給します。空のバンドルでは、設計上どのチェーンも検証に合格しません。
  • OCSP の unknowngood ではありません。unknown は、暗黙の合格ではなく、判定不能として扱ってください — RFC 6960 §2.2。
  • HSM の鍵管理、遅延署名、クラウド署名、PAdES B-LT / B-LTA のプロデューサーは、Core には含まれていません。Core ディストリビューションでこれらのパスを選択すると、不足している Enterprise コンポーネント名を示すメッセージとともにフェイルクローズします。

ソフトウェア署名は 1 桁ミリ秒台で完了します。タイムスタンプは TSA への 1 回のネットワーク往復を追加します。証明書がメモリ上にあれば、パス検証はローカルで行われます。失効確認は、チェーン内の証明書ごとに OCSP または CRL の取得を 1 回追加します。1500 ms のウォール予算は、ウォームな接続上のリモート TSA を用いた、タイムスタンプ付き署名 1 件をカバーします。遅いエンドポイントに対する失効確認はこれを超過するため、リクエストパスの外に置くべきです。再現性プロファイルは structural です。タイムスタンプは署名の瞬間を埋め込むため、2 回の実行はタイムスタンプのバイト列で異なる一方、ドキュメント構造は同一です。

これはエンジンの主要な暗号境界であるため、脅威モデルを明示します。バイト範囲はエンジンが計算し、呼び出し側から受け取ることは決してありません。署名パスはフェイルクローズです。プリミティブの失敗や機能の欠如は型付き例外を発生させ、より弱いアルゴリズムへ暗黙的にダウングレードすることは決してありません。タイムスタンプが必須となるレベル(B-T、B-LT、B-LTA)では、空のトークンを返すタイムスタンプ局は終端的な障害となります。署名は拒否され、暗黙的にタイムスタンプが付与されないまま格下げされた状態で発行されることはありません。ただし、文書化された機能低下を承認するフォールトハンドラーが配線されている場合を除きます。トラストは設計上、呼び出し側が制御します。アンカーと失効ポリシーはエンジンのデフォルトではなく入力です。これは、自身のトラストを主張する生成側が、検証側のみが確立できる事実を主張することになるためです。タイムスタンプのトラストは、タイムスタンプ局へのトラストに帰着します。タイムスタンプ局は注入可能であり、デプロイメントは独自のものを固定できます。このページは暗号署名に関わるため、export_control_class: legal-review-required と記されています。引用衛生に従い、すべての規範的ソースは言い換えられており、いずれも複製していません。

主張標準条項根拠
CMS 署名の、署名ディクショナリの Contents エントリーへの DER エンコード形式での格納ISO 32000-2§12.8.1
SignerInfo による content-type と message-digest の署名付き属性の保持RFC 5652§5.3
検証側によるコンテンツダイジェストの再計算と、message-digest 属性との比較RFC 5652§5.4
RFC 3161 の TSA によるタイムスタンプトークン生成と、一意の serialNumber および UTC の genTime の保持RFC 3161§2.4.1、§2.4.2,,
基本制約と、署名者からトラストアンカーまでのパス入力を確認する証明書パス検証RFC 5280§6.1,
OCSP による certStatus(good、revoked、unknown)の報告と、thisUpdate / nextUpdate による画定RFC 6960§2.2、§4.2,

すべての条項は言い換えられています。NextPDF は規範的テキストを転載しません。正式な文言については、公開されている標準を参照してください。

Core は、ソフトウェア CMS 署名者(RSA、RSASSA-PSS、ECDSA、Ed25519)、RFC 3161 タイムスタンプの利用、RFC 5280 のパス検証、OCSP / CRL の失効確認を同梱します。HSM および PKCS#11 の鍵管理、遅延署名およびクラウド署名、PAdES B-LT および B-LTA のプロデューサー、FIPS 140-3 の暗号ポリシープロファイルは、Pro および Enterprise エディションに同梱されます。Core はこれらをコントラクトに照らして実行時に解決するため、オープンソースエンジンは商用依存を持たず、API はアップグレード時に変更されません。