Enterprise 版本
HSM 簽章 — 深入參考
本頁是 NextPDF Enterprise HSM 簽章介面的深度參考,涵蓋三個公開型別。NextPDF\Enterprise\Security\Signature\Hsm\Pkcs11Signer 透過 ext-pkcs11 擴充套件經由 PKCS#11 token 進行簽章。NextPDF\Enterprise\Security\Signature\Hsm\OpenSslCliSigner 在子行程中透過 openssl 二進位檔簽章,用於 PHP ext-openssl 無法載入、由 provider 或 engine 支援的金鑰。NextPDF\Enterprise\Security\Signature\Hsm\Provider\HsmSignerProviderAdapter 將任一具體型別公開為統一的 SignerProviderInterface。在每一條路徑中,私鑰都留在 token 邊界內;NextPDF 交付待簽章的位元組並接收簽章。後量子路徑(signPqs)是預覽:預設停用、不帶任何符合性主張,且在目前的 PDF 驗證器中沒有受支援的驗證路徑。NextPDF 不持有任何認證、也不授予任何認證;支援不等於符合性,符合性也不等於認證。
供應與授權
標題為「供應與授權」的區段此能力隨 NextPDF Enterprise(nextpdf/enterprise)提供,並以 Enterprise 層級的授權封套啟用。未持有該權利的部署不會載入此能力的類別。比較版本並取得授權。
公開 API 介面
標題為「公開 API 介面」的區段這三個型別都位於 NextPDF\Enterprise\Security\Signature\Hsm;轉接器則位於其 Provider 子命名空間。兩個簽章器都實作 Core 的 NextPDF\Contracts\HsmSignerInterface 合約。
| 符號 | 參數 | 預設行為 | 回傳 | 拋出或失敗於 | 備註 |
|---|---|---|---|---|---|
Pkcs11Signer::__construct() | string $libraryPath, int $slotId, string $pin, string $certLabel, ?string $keyLabel = null, array $chainDer = [], bool $enablePostQuantum = false, ?FipsSignatureEnforcer $fipsEnforcer = null | 開啟廠商函式庫、登入 slot,並從 token 載入憑證與金鑰演算法中繼資料 | — | ext-pkcs11 不存在或 token 存取失敗時拋出 HsmOperationException | 每個行程對每個函式庫路徑快取一個 module handle;PIN 與標籤為 #[SensitiveParameter] |
Pkcs11Signer::sign() | string $data, string $algorithm = 'sha256WithRSAEncryption' | 在 token 上簽章;原始 ECDSA 輸出會轉換為 DER ECDSA-Sig-Value | string 原始簽章位元組 | HsmOperationException(找不到金鑰、token 失敗);InvalidArgumentException(未對應的演算法);當已接上 enforcer 時,於簽章前拋出 FIPS 閘門例外 | 封閉演算法集;參見行為合約 |
Pkcs11Signer::signPqs() | string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true | 除非已設定 $enablePostQuantum,否則拒絕執行;派發臨時的 PKCS#11 PQ 機制 | string 原始簽章位元組 | HsmOperationException(已停用、token 失敗、簽章長度不符);InvalidArgumentException(context 超過 255 位元組) | 預覽;不帶符合性主張;機制識別碼為臨時性 |
Pkcs11Signer::isPostQuantumEnabled() | 無 | 回報建構子的選用啟用旗標 | bool | 無 | — |
Pkcs11Signer::getCertificateDer() | 無 | 回傳從 token 讀取的簽章者憑證 | string(DER) | 無 | 於建構時載入一次 |
Pkcs11Signer::getCertificateChainDer() | 無 | 回傳建構子提供的中繼憑證 | array<string>(DER) | 無 | 不含簽章者憑證 |
OpenSslCliSigner::__construct() | string $keyUri, string $certPath, string $pin, array $extraCertPaths = [], OpenSslCliBackend $backend = OpenSslCliBackend::Auto, string $opensslBinary = 'openssl', int $timeoutSeconds = 30, ?string $modulePath = null, ?string $configPath = null, bool $legacyPinDelivery = false, ?FipsSignatureEnforcer $fipsEnforcer = null | 驗證 proc_open、探測二進位檔與版本、解析 backend,並載入憑證 | — | HsmOperationException(proc_open 已停用、缺少 module/config/憑證檔、二進位檔失敗、無 backend);InvalidArgumentException($keyUri 中含 pin-value) | OpenSslCliBackend::Auto 優先使用 OpenSSL 3.x provider,其次為 engine |
OpenSslCliSigner::sign() | string $data, string $algorithm = 'sha256WithRSAEncryption' | 在子行程中執行 openssl dgst;預設 PIN 透過短暫的 0600 pin-source 檔傳遞 | string 原始簽章位元組 | HsmOperationException(逾時、PIN 遭拒、找不到金鑰、module 載入失敗、輸出為空、pin 檔失敗);InvalidArgumentException(未對應的演算法);於簽章前拋出 FIPS 閘門例外 | 子行程在 $timeoutSeconds 後遭終止;stderr 在進入訊息前先經編修遮蔽 |
OpenSslCliSigner 存取器介面 | 無 | 唯讀的建構結果 | string / array<string> / OpenSslCliBackend | 無 | getCertificateDer, getCertificateChainDer, getPublicKeyAlgorithm, getCertificatePem, getResolvedBackend, getOpensslVersion |
HsmSignerProviderAdapter::__construct() | HsmSignerInterface $hsm, string $providerId, SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15 | 將 HSM 具體型別包裝為 SignerProviderInterface | — | 無 | Provider id 慣例:pkcs11-{module-id}、openssl-cli |
HsmSignerProviderAdapter::providerId() | 無 | 回傳建構子提供的 id | non-empty-string | 無 | — |
HsmSignerProviderAdapter::supportsAlgorithm() | SignatureAlgorithm $algo | 將 enum 對應為 OpenSSL 風格的名稱,然後與 backend 允許集取交集 | bool | 無 | 拒絕僅雜湊(digest-only)的演算法;openssl-engine id 不公告任何項目 |
HsmSignerProviderAdapter::sign() | string $data, ?string $keyVersion = null | 以設定的演算法透過所包裝的簽章器派發 | non-empty-string | KeyManagementException($keyVersion 非 null);SignatureFailedException(無法對應的演算法、driver 失敗、簽章為空) | Fail-closed SPI 合約;每個 driver 錯誤都以型別化方式浮現 |
public function __construct(private readonly string $libraryPath, private readonly int $slotId, #[SensitiveParameter] private readonly string $pin, #[SensitiveParameter] private readonly string $certLabel, #[SensitiveParameter] private readonly ?string $keyLabel = null, array $chainDer = [], private readonly bool $enablePostQuantum = false, ?FipsSignatureEnforcer $fipsEnforcer = null)public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): stringpublic function signPqs(string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true): stringpublic function isPostQuantumEnabled(): boolpublic function getCertificateDer(): stringpublic function getCertificateChainDer(): arraypublic function __construct(private string $keyUri, string $certPath, #[SensitiveParameter] private string $pin, array $extraCertPaths = [], private OpenSslCliBackend $backend = OpenSslCliBackend::Auto, private string $opensslBinary = 'openssl', private int $timeoutSeconds = 30, private ?string $modulePath = null, private ?string $configPath = null, private bool $legacyPinDelivery = false, private ?FipsSignatureEnforcer $fipsEnforcer = null)public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): stringpublic function getCertificateDer(): stringpublic function getCertificateChainDer(): arraypublic function getPublicKeyAlgorithm(): stringpublic function getCertificatePem(): stringpublic function getResolvedBackend(): OpenSslCliBackendpublic function getOpensslVersion(): stringpublic function __construct(private HsmSignerInterface $hsm, private string $providerId, private SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15)public function providerId(): stringpublic function supportsAlgorithm(SignatureAlgorithm $algo): boolpublic function sign(string $data, ?string $keyVersion = null): string行為合約
標題為「行為合約」的區段- 金鑰託管。 私鑰永遠不離開 token 邊界。
Pkcs11Signer將操作委派給 token;OpenSslCliSigner則將金鑰參照——一個 PKCS#11 URI——傳遞給openssl子行程。兩個簽章器都無法匯出金鑰。 - 工作階段與登入。
Pkcs11Signer對每個行程的每個函式庫路徑快取一個 PKCS#11 module handle,因為 token 介面在每個行程中必須恰好初始化一次。每次操作都會開啟一個工作階段並以 PIN 登入;登入會在任何私鑰使用之前驗證使用者(PKCS#11 v3.1 §5.6.8)。當 slot 回報已存在登入時,簽章器會登出後再重新登入,讓要求每次操作都用新 PIN 的 token 能取得新的登入。 - 演算法集(封閉)。 兩個簽章器恰好接受:
sha256WithRSAEncryption、sha384WithRSAEncryption、sha512WithRSAEncryption;RSASSA-PSS、RSASSA-PSS-SHA256、RSASSA-PSS-SHA384、RSASSA-PSS-SHA512;ecdsa-with-SHA256、ecdsa-with-SHA384、ecdsa-with-SHA512。Pkcs11Signer額外接受ecdsa-raw。任何其他識別碼都會拋出InvalidArgumentException——絕不會簽署任何替代演算法。 - PSS salt 綁定。 對每個 PSS 變體,salt 長度都等於摘要長度——32、48 或 64 位元組——且 hash 與 MGF 參數與所選摘要相符。此舉遵循 PSS 機制參數結構,其中 salt 長度通常為訊息雜湊長度(PKCS#11 v3.1 §6.1.9)。兩個簽章器套用相同的配對,因此在一個 backend 上有效的組態在另一個上也有效。
- ECDSA 轉換。 token 以 r 與 s 的原始、零填補串接形式回傳 ECDSA 簽章(PKCS#11 v3.1 §6.3.1)。
Pkcs11Signer::sign()會將該輸出轉換為 PDF 驗證器與 OpenSSL 所預期的 DER 編碼ECDSA-Sig-Value形式。呼叫方永遠不會處理到原始形式。 - PIN 傳遞(CLI 路徑)。 在安全的預設模式中,PIN 會寫入一個以僅擁有者權限獨佔建立的短暫檔案,透過 PKCS#11 URI 的
pin-source屬性參照,並在子行程結束後解除連結。在此模式下,PIN 不會放入命令列,也不會匯出至子行程環境。當$legacyPinDelivery = true時,PIN 會以pin-value內嵌於 URI,可從行程命令列觀察到;此模式僅供選用。 - 子行程紀律。
OpenSslCliSigner以引數陣列衍生該二進位檔——不做 shell 插補——強制$timeoutSeconds、在逾時時終止子行程,並將 stderr 分類為型別化錯誤。祕密在 stderr 被引用於例外訊息之前先經編修遮蔽。 - 轉接器語意。 HSM token 沒有受管理的金鑰版本概念;token 上的金鑰即是版本。因此
HsmSignerProviderAdapter::sign()會以KeyManagementException拒絕任何非 null 的$keyVersion,而非予以忽略。supportsAlgorithm()將 enum 對應與所包裝 backend 的接受集取交集,因此轉接器絕不會公告 backend 在簽章時會拒絕的機制。來自 driver 的空簽章會拋出SignatureFailedException。 - 後量子預覽。
signPqs()受$enablePostQuantum建構子旗標把關,否則拒絕執行。context 字串限制為 255 位元組,與 ML-DSA context 界限相符(FIPS 204)。回傳的簽章必須與所選Pkcs11PqsAlgorithm參數集的確切位元組長度相符,否則呼叫失敗。機制識別碼遵循臨時的 PKCS#11 PQ 擴充,尚非最終版本。PAdES 設定檔不辨識後量子套件,多數 PDF 驗證器會拒絕此類簽章,而 NextPDF 未為其提供任何驗證路徑。不主張任何符合性。
邊界情況與失敗模式
標題為「邊界情況與失敗模式」的區段- 在沒有
ext-pkcs11的情況下建構Pkcs11Signer會立即拋出HsmOperationException;標準 PHP 發行版並未內建此擴充。 - 在 token 上找不到對應物件的憑證或私鑰標籤會拋出
HsmOperationException,並指出缺少的物件類別。在某些 token 上,金鑰標籤合理地可能與憑證標籤不同。 - 重複登入失敗可能會在 token 端鎖定 PIN;施行該政策的是 token,而非 NextPDF。對金鑰要求每次使用都驗證的 token,會透過登出後重試的路徑取得新的登入(PKCS#11 v3.1,always-authenticate 語意)。
OpenSslCliSigner會在建構時以 fail-closed 拒絕已含pin-value的$keyUri,因為該傳遞方式會繞過安全的 PIN 路徑。- 在 Windows 上,安全的 pin 檔模式會以
HsmOperationExceptionfail closed:那裡的檔案權限位元無法限制 ACL 讀取授權,因此簽章器拒絕在暫存目錄的 ACL 下留下明文 PIN。Legacy PIN 傳遞是為受信任的 Windows 主機記載的選用替代方案。 - Backend 自動偵測的 provider 路徑需要 OpenSSL 3.x;LibreSSL 永遠不會解析為 provider。當 provider 與 engine 探測皆未成功時,建構會以
HsmOperationException失敗,而非將失敗延後到簽章時。 - 超過
$timeoutSeconds的子行程會遭終止並回報為逾時;乾淨結束但輸出為空的子行程會回報為空簽章失敗。兩種情況都不會產生部分簽署的文件。 - 位元組長度與所選參數集不符的後量子簽章會在進入 CMS 編碼之前遭拒絕。
- 使用已退役的
openssl-engineprovider id 的HsmSignerProviderAdapter不公告任何演算法,因此陳舊的組態會在 provider 選取時失敗,而非在簽章時。
FIPS 模式行為
標題為「FIPS 模式行為」的區段兩個簽章器都接受一個選用的 FipsSignatureEnforcer。接上後,該簽章器的 FIPS 模式即為啟用:sign() 會在任何 token 或子行程簽章發生之前,拒絕不被允許的簽章演算法或低於下限的金鑰。這些下限遵循簽章產生表——不允許 RSA 模數低於 2048 位元、ECDSA 階數低於 224 位元(NIST SP 800-131A Rev.2 §3 Table 2)。未接上 enforcer 時,行為不變。此閘門僅涵蓋傳統的 sign() 路徑;signPqs() 由其自身的預覽旗標管制。這些是關於 NextPDF 程式碼的能力主張:FIPS 140-3 驗證是透過 CMVP 附著於某個密碼模組,在此部署中即為操作者的 HSM 或 provider——NextPDF 不是經驗證的模組、不持有任何認證,也不授予任何認證。
符合性
標題為「符合性」的區段| 主張 | 標準 | 條款 |
|---|---|---|
| 登入會在私鑰操作之前向 token 驗證使用者;錯誤的 PIN 會拒絕存取。 | PKCS#11 v3.1 | §5.6.8 |
| always-authenticate 金鑰每次使用都需要新的登入;重複的重新驗證失敗可能鎖定 PIN。 | PKCS#11 v3.1 | CKA_ALWAYS_AUTHENTICATE re-authentication |
| token 的 ECDSA 簽章是原始的 r‖s 串接;簽章器會將其轉換為 DER 以便 PDF 互通。 | PKCS#11 v3.1 | §6.3.1 |
| PSS 參數綁定 hash、MGF 與 salt 長度;簽章器將 salt 設為等於摘要長度。 | PKCS#11 v3.1 | §6.1.9 |
| FIPS 閘門拒絕以低於 2048 位元的 RSA 或低於 224 位元階數的 ECDSA 產生簽章。 | NIST SP 800-131A Rev.2 | §3 Table 2 |
| 後量子 context 字串限制為 255 位元組。 | FIPS 204 | HashML-DSA context handling |
| FIPS 140-3 驗證透過 CMVP 附著於密碼模組。 | FIPS 140-3 | CMVP program scope |
所有條款皆為改述;未重製任何規範性文字。NextPDF 不作任何認證主張。 簽章器將其行為與所引條款對齊,作為一種能力。所產生的簽章是否通過驗證,是驗證方依其信任錨點所做的決定;金鑰安全取決於 token、HSM 與操作者——而非僅取決於 NextPDF。
開發備註
標題為「開發備註」的區段-
PIN 傳遞機制遵循 PKCS#11 URI 的
pin-source慣例(RFC 7512);該 RFC 不在所引語料之內,因此上述行為以產品原始碼為依據,而非規格引用。 -
在建構
Pkcs11Signer之前,確認執行環境已載入ext-pkcs11;擴充不存在時建構會快速失敗。CLI 簽章器需要啟用proc_open,以及一個已安裝 PKCS#11 provider 或 engine 的openssl二進位檔。 -
PIN、憑證標籤與金鑰標籤為
#[SensitiveParameter],因此會從堆疊追蹤中排除。請從祕密管理器提供 PIN;切勿將其寫入原始碼、提交至版本控制的組態,或日誌。 -
建構是兩個簽章器上昂貴的步驟:PKCS#11 路徑會登入並讀取憑證,CLI 路徑則探測二進位檔與 backend。請建構一次並重複使用該實例;以函式庫為單位的 module 快取讓針對同一函式庫的重複建構是安全的。
-
當呼叫方透過
SignerProviderInterface運作時,請以HsmSignerProviderAdapter包裝簽章器。為所包裝的類別傳入標準的 provider id——pkcs11-{module-id}或openssl-cli——讓能力檢查使用正確的 backend 允許集。 -
在啟用後量子預覽之前,請將 token 韌體的機制識別碼與 NextPDF 註冊的臨時值核對;不符會在簽章時失敗。請勿為正式的 PAdES 輸出啟用此預覽。
-
getResolvedBackend()與getOpensslVersion()是為了記錄證據而存在;當你的合規計畫要求可重現性時,請將其與簽章證據一併保存。
另請參閱
標題為「另請參閱」的區段- 硬體安全模組簽章(PKCS#11) — 含設定、組態與驗證步驟的能力頁面。
- 安全性 — 深度參考 — 整合的 Enterprise 安全性介面。
- 簽章 — 深度參考 — PAdES B-LT / B-LTA 長期產生器。
- FIPS 140 — 深度參考 — 密碼政策、自我測試套組,以及
FipsSignatureEnforcer閘門。 - PQC 預覽 — 深度參考 — 後量子預覽介面及其邊界。
- 安全性/簽章(Core) — Core 的 CMS 簽章器與簽章合約。
發布邊界
標題為「發布邊界」的區段本頁僅記載外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表、runbook 檔名與工單前綴皆不在範圍內。