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

Enterprise エディション

ハードウェアセキュリティモジュール署名 (PKCS#11)

NextPDF Enterprise は、ハードウェアセキュリティモジュール (HSM) の内部に保持された鍵を用いて PDF に署名します。署名者を PKCS#11 トークン — スマートカード、Universal Serial Bus (USB) トークン、またはネットワーク接続 HSM — に向けると、署名操作はデバイス上で実行されます。秘密鍵がトークン境界から出ることは決してありません。このページは挙動レベルの内容です。署名者が何を行い、お客様が何を提供し、そして鍵の保管がどこで NextPDF の責任でなくなるかを記載します。

HSM 署名者は Core の署名者コントラクトを通じて解決されるため、アプリケーションは具体的な Enterprise 型ではなくコントラクトに依存します。これは Core が使用するのと同じ Cryptographic Message Syntax (CMS) 署名パスを拡張したものですが、暗号操作がトークンに委譲される点が異なります。

前提条件はフロントマターに記載され、作業の途中で驚かされることがないよう 前提条件 の下にも再掲されています。

この機能は NextPDF Enterprise (nextpdf/enterprise) で提供され、Enterprise ティアのライセンスエンベロープで有効化されます。その利用資格を持たないデプロイでは、この機能のクラスは読み込まれません。エディションを比較してライセンスを取得する

NextPDF Core は、鍵をプロセス内に保持するか、Core の署名ストラテジーコントラクトを通じて鍵を受け取るソフトウェア CMS 署名者を提供します。NextPDF Pro はリモートおよびクラウド key-management-service (KMS) 署名ストラテジーを追加します。PKCS#11 によるハードウェア鍵の保管は Enterprise の機能であり、Core や Pro では提供されません。

PKCS#11 トークンは、ベンダー提供の共有ライブラリの背後に暗号オブジェクト — 証明書と秘密鍵 — を公開します。Enterprise 署名者はそのライブラリをアダプトします。

  1. トークンの共有ライブラリをプロセスごとに一度だけ開き、モジュールハンドルをキャッシュします。PKCS#11 はモジュールをプロセスごとに正確に一度だけ初期化することを要求するためです。
  2. 設定されたスロット上でセッションを開き、指定された PIN でログインします。ログインは、いかなる秘密鍵操作の前にもユーザーを認証します (PKCS#11 v3.1 §5.6.8 に準拠)。
  3. トークン上の署名証明書をラベルで特定し、証明書を Distinguished Encoding Rules (DER) 形式で読み取り、公開鍵アルゴリズムを検出します。
  4. 署名時には秘密鍵をラベルで特定し — 一部のトークンでは証明書ラベルと異なる場合があります — トークンに署名の計算を依頼します。署名対象のデータが渡され、鍵はデバイス上に留まります。

署名者は、PKCS#1 v1.5 パディングを用いた RSA (SHA-256、SHA-384、SHA-512)、ソルト長がダイジェスト長に等しい Probabilistic Signature Scheme (PSS) パディングを用いた RSA、および SHA-256、SHA-384、SHA-512 を用いた Elliptic Curve Digital Signature Algorithm (ECDSA) をサポートします。ECDSA の曲線とダイジェストは慣例的にペアリングされます — P-256 と SHA-256、P-384 と SHA-384、P-521 と SHA-512 — これは RFC 5480 の推奨ペアリングに従っています。トークンは ECDSA 署名を 2 つの整数の生の連結として返しますが、署名者はそれを PDF と OpenSSL が期待する DER エンコード形式に変換します。

署名生成には、少なくとも 2048 ビットの RSA 鍵と少なくとも 224 ビットの ECDSA 曲線位数が、NIST SP 800-131A Rev.2 §3 に準拠した許容可能な最小値です。トークンの鍵はこれらのサイズ以上でプロビジョニングしてください。

エンジンバックのトークン向けに、代替の OpenSSL エンジンパスが存在します。OpenSSL 3.x では PHP OpenSSL 拡張機能がエンジン application programming interface (API) を公開しないため、エンジンクラスは非推奨です。サポートされるエンジンバックの経路は OpenSSL コマンドラインバイナリを実行します。トークンに PKCS#11 ライブラリがある場合は、直接の PKCS#11 パスを優先してください。

決定的な設計判断は、秘密鍵が決してトークンから出ないことです。そのため署名者は暗号操作をデバイスに委譲し、署名対象のデータのみを PKCS#11 の継ぎ目を越えて移動させます。PHP メモリ内で鍵素材を読み取ったり再構築したりすることは決してありません。具体的な Enterprise 型ではなく Core の HsmSignerInterface コントラクトを通じて解決するため、鍵がソフトウェア、クラウド KMS、ハードウェアトークンのいずれに存在しても署名コードは同一です。PKCS#11 が各モジュールをプロセスごとに正確に一度だけ初期化するため、モジュールハンドルをプロセスごとに一度キャッシュし、その後トークンの生の ECDSA 出力を DER に変換して、バリデータが期待するエンコードを認識できるようにします。この形を決めるのは利便性ではなく保管です。信頼境界はデバイスの端に留まります。

設計背景: HSM ベースの署名

HSM で署名する前に、各項目を確認してください。

  1. NextPDF Core と Enterprise パッケージをインストールします: composer require nextpdf/core:^3 および composer require nextpdf/enterprise
  2. 有効な NextPDF Enterprise ライセンスを保持し、Private Packagist 上でライセンス資格情報に対してパッケージを解決します。
  3. トークンベンダーの PKCS#11 共有ライブラリをホストにインストールし (例: Linux では .so、Windows では .dll)、その絶対パス、スロット番号、およびオブジェクトラベルを控えます。
  4. ext-pkcs11 PHP 拡張機能を読み込みます。これは標準の PHP にはバンドルされておらず、別途インストールする必要があります。拡張機能が存在しない場合、署名者のコンストラクタは型付きの操作エラーを発生させます。

署名者に次の入力を渡します。

  • ライブラリパス — ベンダー PKCS#11 共有ライブラリへの絶対パス。
  • スロット識別子 — トークンのスロット番号。通常は 0
  • PIN — トークンの PIN。これは秘密として扱います。シークレットマネージャーから供給し、ソースやログからは決して供給しないでください。署名者は PIN パラメータを機微とマークするため、スタックトレースやシリアライズから除外されます。
  • 証明書ラベル — トークン上の証明書オブジェクトのラベル。
  • 鍵ラベル — 証明書ラベルと異なる場合の秘密鍵オブジェクトのラベル。
  • チェーン — トークンが保持していない場合の、DER 形式のオプションの中間証明書。

署名者を構築する前にトークンの利用可能性を確認してください。構築時にトークンから証明書を読み取るため、誤設定されたスロットやラベルは署名時ではなく型付きエラーで早期に失敗します。

  1. 拡張機能の利用可能性を確認して、ランタイムが PKCS#11 をサポートしていることを確認します。拡張機能が存在しない場合は署名者を構築しないでください。
  2. シークレットマネージャーから PIN を、決してログに出力されない変数に読み込みます。
  3. ライブラリパス、スロット、PIN、およびラベルを用いて HSM 署名者を構築します。構築時にログインして証明書を読み取ります。
  4. HsmSignerInterface を通じて署名者を Core の署名オーケストレータに渡します。オーケストレータはバイト範囲を計算し、CMS 署名属性を構築し、データをトークンに渡し、署名済み PDF を組み立てます。
  5. 最も具体的な失敗を捕捉し、PIN を含まない構造的なメッセージをログに記録して、再スローします。
examples/contracts/hsm-signer-availability.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HsmSignerInterface;
/**
* Build a hardware-token signer only when the runtime supports it.
*
* The concrete PKCS#11 signer is resolved through the Core contract so the
* caller depends on the interface, not the Enterprise implementation type.
* The PIN arrives from a secret resolver; it is never written to source.
*
* @param callable(): bool $pkcs11Available Reports ext-pkcs11 availability.
* @param callable(): HsmSignerInterface $signerFactory Builds the configured token signer.
*
* @throws \RuntimeException When the PKCS#11 extension is not loaded.
*
* @return HsmSignerInterface The token signer, ready for the Core orchestrator.
*/
function resolveHsmSigner(callable $pkcs11Available, callable $signerFactory): HsmSignerInterface
{
if ($pkcs11Available() !== true) {
throw new \RuntimeException(
'PKCS#11 signing requires the ext-pkcs11 extension; install it before signing.',
);
}
return $signerFactory();
}

本番配線 — 正確なコンストラクタ引数リストと型付き例外の型 — は HSM 詳細リファレンス に記載されています。

examples/contracts/hsm-sign-guarded.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HsmSignerInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class HsmSigningService
{
public function __construct(
private HsmSignerInterface $signer,
private LoggerInterface $logger,
) {}
/**
* Sign data on the token through the Core HSM contract.
*
* The byte range is computed by the engine, never accepted from the
* caller. The token performs the signing operation; the private key
* does not leave the device.
*
* @param string $data The bytes the orchestrator hands to the token.
* @param string $algorithm The OpenSSL-style signing algorithm identifier.
*
* @throws NextPdfException When the token operation fails.
*
* @return string The raw signature bytes returned by the token.
*/
public function sign(string $data, string $algorithm): string
{
try {
return $this->signer->sign($data, $algorithm);
} catch (NextPdfException $e) {
// Structural message only — never the PIN or key material.
$this->logger->error('HSM signing failed', ['reason' => $e->getMessage()]);
throw $e;
}
}
}

バリデータが行うのと同じ方法で結果を確認します。

  1. 署名者から署名者証明書とチェーンを DER 形式で読み戻し、トークンにプロビジョニングされた証明書と一致することを確認します。
  2. 信頼アンカーで設定されたバリデータで署名済み PDF を開き、署名が暗号的に無傷であると報告されることを確認します。生成された署名は検証済みの署名ではありません。信頼の判断は、生成者ではなく、バリデータとその信頼アンカーに属します。
  3. ECDSA 署名の場合、埋め込まれた署名が DER エンコードされていることを確認します — 署名者がトークンの生の出力を変換してくれるため、生の連結形式を拒否するバリデータでも埋め込まれた署名は受け入れるはずです。
  4. PIN、トークンラベル、または鍵素材がアプリケーションログに現れないことを確認します。
  • 鍵はトークン上に留まります。 署名対象のデータはトークンに渡され、署名操作はトークン境界の内部で実行されます。秘密鍵が PHP メモリに読み込まれることは決してありません。
  • PIN は秘密です。 これは機微なコンストラクタパラメータであり、ログやシリアライズから除外されます。シークレットマネージャーから供給してください。再認証の失敗が繰り返されるとトークン側で PIN がロックされることがあります。このポリシーを強制するのは NextPDF ではなくトークンです。
  • フェイルクローズ。 トークンまたは HSM のエラーは型付き例外を発生させます。署名者は未署名または部分的に署名された結果を生成せず、より弱いアルゴリズムを代替することも決してありません。
  • アルゴリズム強度。 少なくとも 2048 ビットの RSA 鍵と少なくとも 224 ビット位数の ECDSA 曲線をプロビジョニングしてください。これは NIST SP 800-131A Rev.2 §3 に準拠した署名生成の許容可能な最小値です。
  • ポスト量子署名は実験的であり、デフォルトでは無効です。 明示的なオプトインフラグの背後にポスト量子パスが存在します。標準的な PDF Advanced Electronic Signatures (PAdES) の長期アーカイブプロファイルはまだポスト量子スイートを認識しておらず、ほとんどのビューアは検証時にそれらを拒否します。本番の PAdES 署名では有効化しないでください。

このページは暗号署名とハードウェアセキュリティモジュール統合に関するものです。すべての規範的な情報源は言い換えられており、規範的なテキストは再現されていません。 ### 鍵保管の境界

NextPDF Enterprise は PKCS#11 トークンまたは HSM と統合します。署名鍵を保存、生成、またはそのセキュリティを保証することはありません。鍵のセキュリティは、トークンまたは HSM、デプロイ、およびオペレータに依存し、NextPDF Enterprise 単独には依存しません。トークンのプロビジョニング、PIN の取り扱い、スロット設定、およびネットワーク接続 HSM のネットワーク保護については、お客様の責任です。

  • 拡張機能が存在しない。 ext-pkcs11 が読み込まれていない場合、PKCS#11 署名者の構築は型付き操作例外を発生させます。まず利用可能性を確認してください。
  • 証明書または鍵がラベルで見つからない。 構築または署名は、欠落しているオブジェクトを名指しする型付き例外を発生させます。ラベルとスロットを確認してください。
  • すでにログイン済み。 複数の署名者インスタンスが同じスロットに対してキャッシュされたモジュールを共有する場合、署名者はログアウトして再度ログインし、新たな PIN 検証を提供します — 「毎回 PIN」ポリシーを持つ personal-identity-verification トークンで必要です。
  • サポートされないアルゴリズム。 署名者がマップしないアルゴリズムを要求すると、代替で署名するのではなく引数エラーを発生させます。
  • ネットワーク HSM に到達できない。 ネットワークまたはデバイスのエラーは型付き例外を発生させます。署名者が未署名の文書を黙って生成することは決してありません。

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