契約 / 署名
署名ドメインには 6 つの契約が含まれます。これらは、CMS 署名の生成方法、RFC 3161 タイムスタンプの適用方法、ハードウェア鍵による署名方法、長期検証の有効化方法を定義します。Core は契約を公開し、Pro エディションと Enterprise エディションが本番コードを提供します。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3概念の概要
「概念の概要」という見出しのセクションPDF のデジタル署名は、署名辞書に格納される CMS SignedData 構造です。Contents エントリーには、DER エンコードされた構造が格納されます。ByteRange エントリーは、ダイジェストの対象となるバイト範囲を指定します。ダイジェストはファイル全体を対象とし、署名値そのものは除外します(ISO 32000-2 §12.8.1 を参照)。CMS 構造は RFC 5652 §5.1 に従い、バージョン、ダイジェストアルゴリズム、カプセル化されたコンテンツ、署名者情報のシーケンスで構成されます。
SignerInterface は中核となる契約です。バイト範囲に対する CMS SignedData を生成し、署名値にタイムスタンプを適用し、長期検証をサポートするかどうかを報告します。これは、ETSI EN 319 142 で定義されている PAdES ベースラインレベル B-B から B-LTA までに対応します。レベルが上がるごとに要素が追加されます。B-B は基本署名を保持します。B-T は署名タイムスタンプを追加します。B-LT は失効データを追加します。B-LTA はアーカイブタイムスタンプを追加します。
HsmSignerInterface は、Hardware Security Module 内に保持された鍵で署名します。秘密鍵がハードウェア境界の外に出ることはありません。この契約は、CMS レイヤーが構造を構築できるように、署名者証明書と証明書チェーンを DER 形式で返します。DeferredSignerInterface は、非同期署名のために SignerInterface を拡張します。呼び出し元はデータを送信してジョブ識別子を受け取り、完了をポーリングしたうえで結果を取得します。リモートの HSM やクラウド鍵サービスが結果をすぐに返さない場合に使用します。
TimestampProviderInterface は、RFC 3161 タイムスタンプトークンを要求します。このプロトコルは、Time-Stamping Authority とのリクエストとレスポンスのやり取りです(RFC 3161 §2.4 を参照)。トークン内の messageImprint は、署名値のハッシュです(RFC 3161 §2.4.2)。LtvManagerInterface は長期検証を有効にします。証明書チェーンを収集し、OCSP および CRL レスポンスを取得し、Document Security Store を構築し、B-LTA 用のドキュメントタイムスタンプを追加します。CryptoPolicyInterface は、暗号処理が実行される前に、どのハッシュ、署名、暗号化アルゴリズム、鍵強度を許可するかを制御します。
API サーフェス
「API サーフェス」という見出しのセクション| 型 | 種別 | 主要メンバー | 安定性 | 導入バージョン |
|---|---|---|---|---|
SignerInterface | interface | sign(string): SignatureResult, timestamp(string): string, supportsLtv(): bool | 安定版 | 1.0.0 |
HsmSignerInterface | interface | sign(string, string): string, getCertificateDer(), getCertificateChainDer(), getPublicKeyAlgorithm() | 安定版 | 1.0.0 |
DeferredSignerInterface | interface | submitForSigning(string): string, retrieveSignature(string), isComplete(string)(SignerInterface を拡張) | 実験的 | 3.0.0 |
TimestampProviderInterface | interface | getTimestamp(string): string | 実験的 | 3.0.0 |
LtvManagerInterface | interface | enableLtv(...), addDocumentTimestamp(...) | 安定版 | 1.10.0 |
CryptoPolicyInterface | interface | isHashAlgorithmAllowed(), isSignatureAlgorithmAllowed(), isEncryptionAlgorithmAllowed(), isKeyStrengthAllowed(), getPreferredHashAlgorithm(), getName() | 安定版 | 1.9.0 |
SignerInterface::sign() は NextPDF\Security\Signature\SignatureResult を返します。public な $cmsSignedData プロパティには、DER エンコードされた CMS が格納されます。public な $digestHex プロパティには、SHA-256 ダイジェストが格納されます。public な $timestampToken プロパティには、オプションの TSA トークンが格納されます。アクセサーは toHex() / toHexPadded(int) / getSize() / hasTimestamp() です。HsmSignerInterface::sign() は、RSA の場合は DER エンコードされたバイト列を返し、ECDSA の場合は生の r‖s バイト列を返します。アルゴリズム引数には OpenSSL 識別子を使用します。
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクション<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;
/** * Sign a byte range with any SignerInterface implementation. * * @param SignerInterface $signer A core or Premium signer. * @param string $byteRange The PDF byte range to sign. * * @return string Hex-encoded CMS SignedData for the PDF /Contents field. */function signByteRange(SignerInterface $signer, string $byteRange): string{ $result = $signer->sign($byteRange);
return $result->toHex();}SignerInterface::sign() は SignatureResult を返します。toHex() は、ライターが /Contents フィールドに配置する 16 進文字列を生成します。生の DER バイト列は public な $cmsSignedData プロパティにあります。この関数は、具体的なクラスではなく契約に依存します。Core のテスト署名者でも Premium の HSM 署名者でも、この契約を満たしていれば同じように扱えます。
コードサンプル — 本番
「コードサンプル — 本番」という見出しのセクション<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;use NextPDF\Contracts\SignerInterface;use NextPDF\Contracts\TimestampProviderInterface;use NextPDF\Exception\NextPdfException;use Psr\Log\LoggerInterface;
final readonly class TimestampedSigningService{ public function __construct( private SignerInterface $signer, private TimestampProviderInterface $timestamps, private CryptoPolicyInterface $policy, private LoggerInterface $logger, ) {}
/** * Sign a byte range, then timestamp the CMS structure. * * @param string $byteRange The PDF byte range to sign. * * @return array{cms: string, digest: string, tst: string} */ public function sign(string $byteRange): array { if (!$this->policy->isHashAlgorithmAllowed($this->policy->getPreferredHashAlgorithm())) { throw new \LogicException('Preferred hash rejected by crypto policy.'); }
try { $signature = $this->signer->sign($byteRange); $token = $this->timestamps->getTimestamp($signature->cmsSignedData);
return [ 'cms' => $signature->toHex(), 'digest' => $signature->digestHex, 'tst' => $token, ]; } catch (NextPdfException $e) { $this->logger->error('Signing failed', [ 'policy' => $this->policy->getName(), 'error' => $e->getMessage(), ]);
throw $e; } }}このサービスは 3 つの契約を注入します。暗号ポリシーは、署名操作の前に参照されます。SignatureResult は、public な $cmsSignedData プロパティで CMS バイト列を、$digestHex で SHA-256 ダイジェストを、toHex() で /Contents 用の 16 進文字列を公開します。タイムスタンププロバイダーは CMS バイト列を受け取り、DER エンコードされたトークンを返します。catch ブロックはポリシー名をログに記録し、再スローします。失敗を握りつぶすことはありません。
エッジケースと注意点
「エッジケースと注意点」という見出しのセクション- バイト範囲のダイジェストは、署名値を除外しなければなりません。
Contentsエントリーを含めたダイジェストは、決して検証に成功しない署名を生成します(ISO 32000-2 §12.8.1)。 SignerInterface::supportsLtv()は、状態ではなく能力を報告します。 署名者は長期検証をサポートしていても、タイムスタンプサービスが設定されていない場合は B-B 署名を生成することがあります。DeferredSignerInterface::retrieveSignature()は、ジョブが完了するまでnullを返します。チェックのたびにペイロードを転送しないように、まずisComplete()をポーリングしてください。結果が存在すれば、取得処理はべき等です。LtvManagerInterface::addDocumentTimestamp()は、Document Security Store の書き込み後に実行しなければなりません。先に呼び出すと、無効な B-LTA 構造が生成されます。CryptoPolicyInterfaceは、ポリシーが設定されていない場合、すべてのアルゴリズムに対してtrueを返します。規制された環境では、明示的なポリシーを設定してください。オープンなデフォルトに依存しないでください。HsmSignerInterface::getCertificateChainDer()は、署名者証明書を除外します。署名者のリーフ証明書にはgetCertificateDer()を、中間証明書にはチェーンメソッドを使用してください。
パフォーマンス
「パフォーマンス」という見出しのセクション署名のコストは、契約ではなく、暗号処理とネットワークのラウンドトリップによって決まります。ローカルのソフトウェア署名は 1 桁台のミリ秒です。HSM 署名では、デバイスとのラウンドトリップが加わります。タイムスタンプでは、Time-Stamping Authority へのネットワークのラウンドトリップが加わります。長期検証では、チェーン内の証明書ごとに 1 回の OCSP または CRL の取得が加わります。1500 ms の実時間という performance_budget は、ウォーム接続のリモート TSA を使用した、タイムスタンプ付き署名 1 件をカバーします。遅い失効エンドポイントに対する長期検証はこれを超過するため、リクエストパスの外で実行する必要があります。再現性プロファイルは structural であり、bitwise ではありません。タイムスタンプは署名時刻を埋め込むため、2 回の実行ではドキュメント構造は同一のまま、タイムスタンプのバイト列が異なります。
セキュリティに関する注意
「セキュリティに関する注意」という見出しのセクション署名契約はエンジンの主要な暗号境界であるため、脅威モデルは明示的です。1 つ目の懸念は鍵の管理です。HsmSignerInterface は秘密鍵をハードウェア境界内に保持し、この契約が鍵素材を公開することはありません。2 つ目はアルゴリズムのダウングレードです。CryptoPolicyInterface は処理の前に弱いハッシュや短い鍵をブロックするため、エンジンをフォークすることなく FIPS 140-3 や eIDAS のプロファイルをデプロイで適用できます。3 つ目はタイムスタンプの信頼です。RFC 3161 トークンの信頼性は Time-Stamping Authority の信頼性と同等に限られるため、プロバイダー契約は注入可能であり、デプロイ側が独自の認証局を固定します。4 つ目は長期検証です。失効素材は署名時に取得され、Document Security Store に格納されるため、証明書の有効期限が切れても検証は維持されます。すべての署名者入力を信頼できないものとして扱ってください。バイト範囲はエンジンによって計算され、呼び出し元から受け取ることはありません。この契約は暗号署名を管理するため、このページには export_control_class: legal-review-required が設定されています。本文は引用衛生に従い、すべての規範的ソースを言い換えており、いずれも引用していません。
| 主張 | 標準 | 条項 | 証跡 |
|---|---|---|---|
署名値は、署名辞書の Contents エントリーに、CMS SignedData または TimeStampToken として DER エンコード形式で格納 | ISO 32000-2 | §12.8.1 | |
ダイジェストは、ByteRange 配列で定義されたバイト範囲に対して計算され、署名値を除外 | ISO 32000-2 | §12.8.1 | , |
| 長期検証素材は、VRI、OCSP、CRL、証明書のエントリーとともに Document Security Store に保持 | ISO 32000-2 | §12.8.4.3 | , |
| PAdES の長期検証では、検証データを DSS および VRI 辞書に配置 | ETSI EN 319 142-2 | §6.3 | , |
RFC 3161 タイムスタンプトークンは、TSA のリクエストとレスポンスを介してやり取りされ、messageImprint を通じて署名値のハッシュを関連付け | RFC 3161 | §2.4.2, §2.4 | , |
| CMS SignedData シーケンスは、バージョン、ダイジェストアルゴリズム、カプセル化されたコンテンツ、署名者情報を保持 | RFC 5652 | §5.1 |
すべての条項は言い換えられています。NextPDF は規範的なテキストを複製しません。正式な文言については、公開されている標準を参照してください。
商用コンテキスト
「商用コンテキスト」という見出しのセクションCore は署名契約を公開し、凍結します。HsmSignerInterface、LtvManagerInterface、および遅延署名者を支える本番実装は、PKCS#11 ハードウェア統合や PAdES B-LT および B-LTA を含めて、Pro エディションと Enterprise エディションで提供されます。Core は class_exists() を使用して実行時にこれらを resolve(解決)し、契約にキャストします。そのため、オープンソースエンジンは商用依存関係を持たず、アップグレード時に API が変更されることはありません。
- 契約: 41 個の公開インターフェイス (SPI) — SPI の概要と安定性ティア。
- 契約 / セキュリティポリシー —
CryptoPolicyInterfaceによるアルゴリズムと鍵の制御。 - 契約 / 抽出 — 署名済みアーカイブと組み合わせる PDF/A の適用。
- セキュリティ — 暗号化と署名の実装サーフェス。
- 監査 — ポリシー名と署名イベントの監査ログ。
- 例外 — 署名者がスローする
NextPdfExceptionの階層。