KMS 提供者契約
HsmSignerInterface 是供第三方實作的公開契約,用以為 NextPDF 提供金鑰託管。私密金鑰由你的程式碼保管,引擎則負責建構 CMS 結構。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段NextPDF 將簽章組裝與金鑰託管分離。引擎負責準備位元組範圍,並組裝 CMS SignedData 結構;它並不持有私密金鑰。金鑰託管則透過公開契約委派給簽章後端。
你會實作以下三種公開契約之一:
SignerInterface。 基礎契約,會針對給定資料回傳SignatureResult、套用時間戳記,並回報長期驗證(LTV)的支援情形。當簽章邏輯在行程內執行時,請使用此契約。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() 方法可將原始金鑰位元組讀入行程記憶體,契約的安全性質便會喪失。
API 介面
標題為「API 介面」的區段NextPDF\Contracts\HsmSignerInterface(穩定,自 1.0.0 起):
| 方法 | 回傳 | 用途 |
|---|---|---|
sign(string $data, string $algorithm) | string | 在硬體邊界內簽署資料,並回傳原始簽章位元組。失敗時擲出 RuntimeException。 |
getCertificateDer() | string | 以 DER 形式回傳簽署者的 X.509 憑證。 |
getCertificateChainDer() | array<string> | 回傳中介憑證,從簽發者排序至根憑證,且不含簽署者憑證。 |
getPublicKeyAlgorithm() | string | 回傳公開金鑰演算法識別碼。 |
NextPDF\Contracts\SignerInterface(穩定,自 1.0.0 起):
| 方法 | 回傳 | 用途 |
|---|---|---|
sign(string $data) | SignatureResult | 回傳 DER 編碼的 CMS SignedData。 |
timestamp(string $signatureValue) | string | 回傳 DER 編碼的 RFC 3161 時間戳記權杖。 |
supportsLtv() | bool | 回報是否能嵌入長期驗證(LTV)資料。 |
NextPDF\Contracts\DeferredSignerInterface(實驗性,自 3.0.0 起)延伸 SignerInterface,加入 submitForSigning()、retrieveSignature() 與 isComplete(),供非同步後端使用。
引擎產生的簽章層級遵循 PAdES 設定檔。ETSI EN 319 142-2 定義了延伸的 PAdES 設定檔,建構於 EN 319 142-1 的基準建構區塊之上。引擎會將 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'; }}程式碼範例 — 正式環境
標題為「程式碼範例 — 正式環境」的區段正式環境的提供者會驗證輸入;遇到權杖錯誤時,會以 fail-closed(失敗即拒)方式處理,且絕不會記錄金鑰或簽章材料。
<?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 演算法名稱。 - 失敗即拒(fail-closed)。 遇到任何權杖或 KMS 錯誤時,請擲出
RuntimeException。切勿回傳不完整或空的簽章。 - 金鑰銷毀。 當金鑰達到其密碼週期終點時,請依你的金鑰管理程序加以銷毀。NIST SP 800-57 Part 1 Revision 5 §8.2.1.2 指出,金鑰一旦不再需要就應加以銷毀。銷毀本身則依循 §8.3.4。
資料落地與 PII 緩解措施
標題為「資料落地與 PII 緩解措施」的區段契約僅傳輸待簽署的資料與憑證材料,並不會將文件內容或個人資料傳輸給簽章後端。請讓位元組範圍維持最小。切勿在簽章原因或地點欄位中放入個人資料;那些欄位在已簽署的檔案中是可被觀察到的。
安全的遙測與日誌清除
標題為「安全的遙測與日誌清除」的區段切勿記錄傳入 sign() 的資料、回傳的簽章位元組、可還原形式的金鑰識別碼,或任何憑證私密成分。只記錄運算結果與不可逆的參考即可。SignatureAppliedEvent 掛鉤是受支援的稽核錨點。請參閱 動作觸發器一節。
威脅模型
標題為「威脅模型」的區段| 資產 | 威脅 | 緩解措施 |
|---|---|---|
| 私密金鑰 | 外洩至行程記憶體 | 契約要求在邊界內簽章;PKCS#11 CKA_SENSITIVE / CKA_EXTRACTABLE 強制金鑰不可匯出 |
| 簽章預言機(signing oracle) | 無上限的簽章請求 | 實作會對呼叫端進行速率限制與身分驗證 |
| 憑證鏈 | 替換 | 實作會回傳可建構至受信任根憑證的鏈 |
| 簽章位元組 | 透過日誌洩漏 | 失敗即拒的錯誤路徑;遙測中不含任何簽章材料 |
| 已逾密碼週期的金鑰 | 持續使用 | 依 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 設定檔。引擎會使用你的簽章器所提供的材料組裝出那些建構區塊。金鑰管理與 NIST SP 800-57 Part 1 Revision 5 的生命週期一致,包含 §8.2.1.2 的銷毀需求。硬體金鑰託管與 PKCS#11 v3.1 的金鑰不可匯出屬性一致。引用文獻記錄於本頁的前置資料中。
出口管制與審查治理
標題為「出口管制與審查治理」的區段本頁涵蓋 PKCS#11 與硬體安全模組(HSM)的金鑰託管,因此其前置資料設定 export_control_class: legal-review-required。依文件審查政策(plan §17 gate 6),任何具有安全模式,或路徑或內容符合 PAdES、FIPS、HSM 或 PKCS#11 的頁面,都必須先取得 @nextpdf-labs/crypto-reviewers GitHub 團隊核可,才能發布。該 CODEOWNERS 核可是一道硬性合併 gate:本頁會維持 publish: false,直到法務出口管制審查與 @nextpdf-labs/crypto-reviewers 審查兩者都完成為止。
商業脈絡
標題為「商業脈絡」的區段NextPDF Enterprise 提供此契約的受支援實作,具備金鑰管理系統託管、憑證鏈組裝與稽核整合。你可在 Core 中自行實作 HsmSignerInterface,或透過同一份公開契約使用 Enterprise 的實作,且無需變更程式碼。
另請參閱
標題為「另請參閱」的區段相關契約與模組
標題為「相關契約與模組」的區段- 簽章契約參考 —
SignerInterface、HsmSignerInterface、DeferredSignerInterface與時間戳記提供者。 - 安全政策契約參考 — 同屬安全性敏感的 SPI 介面。
- SPI 穩定性規則 —
stable簽章契約背後的介面承諾。 - 動作觸發器與事件監聽器 —
SignatureAppliedEvent,受支援的稽核錨點。 - 擴充功能撰寫總覽 — 完整的公開 SPI 介面。
詞彙表定義了 key management system、cryptoperiod 與 HSM;各自的標準定義請參閱已發布的詞彙表。