コンテンツにスキップ

KMS プロバイダー契約

HsmSignerInterface は、サードパーティーが NextPDF に鍵保管を提供する際に実装する公開契約です。秘密鍵はお客様のコードが保持します。エンジンは CMS 構造を構築します。

Terminal window
composer require nextpdf/core:^3

NextPDF は、署名の組み立てと鍵保管を分離します。エンジンはバイト範囲を準備し、CMS SignedData 構造を組み立てます。秘密鍵は保持しません。鍵保管は公開契約を通じて署名バックエンドに委譲されます。

3 つの公開契約のいずれかを実装します。

  • SignerInterface 基本契約です。指定されたデータに対して SignatureResult を返します。タイムスタンプを付与します。長期検証のサポート状況を報告します。署名ロジックがプロセス内で実行される場合は、この契約を使用します。
  • HsmSignerInterface 鍵保管契約です。実装は署名操作をハードウェア境界の内側で実行する必要があります。秘密鍵はその境界を離れてはなりません。鍵管理システムのプロバイダーはこの契約を実装します。
  • DeferredSignerInterface 非同期バックエンド向けの SignerInterface の拡張です。呼び出し元はデータを送信し、ジョブ識別子を受け取ります。結果は後で取得します。

このページでは公開契約を規定します。内部の NextPDF Pro または NextPDF Enterprise の実装については説明しません。有償エディションでは、この契約のサポート対象の実装を提供できます。依存すべき対象は、実装ではなく契約です。

この契約は、秘密鍵がセキュア境界を決して離れないことを要求します。標準的な慣行もこの要件を裏付けています。NIST SP 800-57 Part 1 Revision 5 は、鍵管理を鍵のライフサイクル全体にわたる取り扱いとして定義しています。そのライフサイクルとは、セキュアな生成、保管、配布、使用、および破棄です。PKCS#11 v3.1 は、その境界を強制できるようにします。秘密鍵オブジェクトで CKA_SENSITIVE が true、または CKA_EXTRACTABLE が false に設定されている場合、トークンは鍵の値を平文でトークンの外に開示してはなりません。

この要件を守る責任はお客様の実装にあります。エンジンが代わりにこれを強制することはできません。お客様の sign() メソッドが生の鍵バイトをプロセスメモリに読み込めてしまう場合、この契約のセキュリティ特性は失われます。

NextPDF\Contracts\HsmSignerInterface(stable、バージョン 1.0.0 以降で利用可能):

メソッド戻り値目的
sign(string $data, string $algorithm)stringハードウェア境界内でのデータ署名。生の署名バイトの返却。失敗時は RuntimeException のスロー。
getCertificateDer()string署名者の X.509 証明書の DER 形式での返却。
getCertificateChainDer()array<string>署名者証明書を除いた、中間証明書の発行者からルートの順での返却。
getPublicKeyAlgorithm()string公開鍵アルゴリズム識別子の返却。

NextPDF\Contracts\SignerInterface(stable、バージョン 1.0.0 以降で利用可能):

メソッド戻り値目的
sign(string $data)SignatureResultDER エンコードされた CMS SignedData の返却。
timestamp(string $signatureValue)stringDER エンコードされた RFC 3161 タイムスタンプトークンの返却。
supportsLtv()bool長期検証データを埋め込めるかどうかの報告。

NextPDF\Contracts\DeferredSignerInterface(experimental、3.0.0 以降)は、非同期バックエンド向けに SignerInterfacesubmitForSigning()retrieveSignature()、および isComplete() で拡張します。

エンジンが生成する署名レベルは PAdES プロファイルに従います。ETSI EN 319 142-2 は、EN 319 142-1 のベースライン構成ブロックの上に構築される拡張 PAdES プロファイルを定義しています。エンジンは、レベル B-B、B-T、B-LT、B-LTA をそれらの構成ブロックにマッピングします。お客様の署名者は、エンジンが必要とする暗号化操作と証明書素材を提供します。

これは最小限の PKCS#11 スタイルのプロバイダーです。署名の呼び出しはトークンに委譲されます。鍵の値が PHP に読み込まれることはありません。

<?php
declare(strict_types=1);
use NextPDF\Contracts\HsmSignerInterface;
final class TokenSigner implements HsmSignerInterface
{
public function __construct(private readonly TokenSession $session) {}
public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): string
{
// The token computes the signature. The key stays inside the token.
return $this->session->c_sign($data, $algorithm);
}
public function getCertificateDer(): string
{
return $this->session->readCertificate();
}
/** @return array<string> */
public function getCertificateChainDer(): array
{
return $this->session->readChain();
}
public function getPublicKeyAlgorithm(): string
{
return 'rsaEncryption';
}
}

本番環境のプロバイダーは入力を検証します。トークンエラーが発生した場合はフェイルクローズします。鍵や署名の素材をログに記録することはありません。

<?php
declare(strict_types=1);
use NextPDF\Contracts\HsmSignerInterface;
use Psr\Log\LoggerInterface;
use RuntimeException;
final class KmsSigner implements HsmSignerInterface
{
public function __construct(
private readonly RemoteKmsClient $kms,
private readonly string $keyId,
private readonly LoggerInterface $logger,
) {}
public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): string
{
if ($data === '') {
throw new RuntimeException('Refusing to sign empty data');
}
try {
// The KMS performs the operation. The key never reaches this process.
return $this->kms->sign($this->keyId, $data, $algorithm);
} catch (\Throwable $error) {
// Fail closed. Do not log key material or signature bytes.
$this->logger->error('kms.sign.failed', ['key' => $this->keyId]);
throw new RuntimeException('KMS signing failed', previous: $error);
}
}
public function getCertificateDer(): string
{
return $this->kms->certificate($this->keyId);
}
/** @return array<string> */
public function getCertificateChainDer(): array
{
return $this->kms->chain($this->keyId);
}
public function getPublicKeyAlgorithm(): string
{
return $this->kms->algorithm($this->keyId);
}
}
  • 署名バイト形式。 生の署名バイトを返します。RSA の場合、バイト列は DER 形式です。ECDSA の場合、バイト列は生の r 値に s 値が続く形式です。
  • チェーンの順序。 getCertificateChainDer() は署名者証明書を除外します。中間証明書は発行者からルートの順に並べます。
  • アルゴリズム識別子。 sign() は OpenSSL スタイルの識別子を使用します。getPublicKeyAlgorithm() は X.509 アルゴリズム名を返します。
  • 失敗はフェイルクローズ。 トークンまたは KMS のエラーが発生した場合は RuntimeException をスローします。部分的な署名や空の署名を返してはなりません。
  • 鍵の破棄。 鍵が暗号期間の終わりに達したら、お客様の鍵管理手順に従って破棄します。NIST SP 800-57 Part 1 Revision 5 §8.2.1.2 は、鍵が不要になり次第破棄すべきであると定めています。破棄そのものは §8.3.4 に従います。

この契約は、署名対象のデータと証明書素材のみを転送します。ドキュメントの内容や個人データを署名バックエンドに転送することはありません。バイト範囲は最小限に保ちます。署名の理由フィールドや場所フィールドに個人データを入れてはなりません。これらのフィールドは署名済みファイル上で確認できます。

メソッド sign() に渡されたデータ、返された署名バイト、復元可能な形式の鍵識別子、または証明書の秘密成分を決してログに記録してはなりません。操作の結果と非可逆の参照のみをログに記録します。SignatureAppliedEvent フックは、サポート対象の監査アンカーです。アクショントリガーを参照してください。

資産脅威緩和策
秘密鍵プロセスメモリへの流出契約による境界内署名の要求。PKCS#11 CKA_SENSITIVE / CKA_EXTRACTABLE による抽出不可能性の強制
署名オラクル無制限の署名リクエスト実装による呼び出し元のレート制限と認証
証明書チェーンすり替え実装による信頼されたルートまで構築されるチェーンの返却
署名バイトログを通じた開示フェイルクローズのエラーパス。テレメトリーへの署名素材の不含有
暗号期間を過ぎた鍵継続的な使用NIST SP 800-57 Part 1 Revision 5 §8.2.1.2 に従った鍵の破棄

署名レベルは PAdES プロファイルに適合します。ETSI EN 319 142-2 §5.1 は、EN 319 142-1 のベースライン構成ブロックの上に拡張 PAdES プロファイルを定義しています。エンジンは、お客様の署名者が提供する素材からそれらの構成ブロックを組み立てます。鍵管理は、§8.2.1.2 の破棄要件を含め、NIST SP 800-57 Part 1 Revision 5 のライフサイクルに準拠します。ハードウェアによる鍵保管は、PKCS#11 v3.1 の抽出不可能な鍵の属性に準拠します。引用はページのフロントマターに記録されています。

このページは PKCS#11 とハードウェアセキュリティモジュールによる鍵保管を扱うため、フロントマターでは export_control_class: legal-review-required を設定しています。ドキュメントレビューポリシー(plan §17 gate 6)の下では、セキュリティモードを持つページ、またはパスや内容が PAdESFIPSHSM、または PKCS#11 に一致するページは、公開前に @nextpdf-labs/crypto-reviewers GitHub チームの承認が必要です。この CODEOWNERS 承認は必須のマージゲートです。ページは publish: false のままとなり、法務による輸出管理レビューと @nextpdf-labs/crypto-reviewers によるレビューの両方が完了するまで公開可能になりません。

NextPDF Enterprise は、鍵管理システムによる保管、証明書チェーンの組み立て、監査統合を備えた、この契約のサポート対象の実装を提供します。Core では、HsmSignerInterface を独自に実装するか、コードを変更せずに同じ公開契約を通じて Enterprise の実装を利用します。

用語集では 鍵管理システム暗号期間HSM を定義しています。正式な定義は、公開されている用語集を参照してください。