跳到內容
getnextpdf.com

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 Enterprisenextpdf/enterprise)提供,並以 Enterprise 層級的授權封套啟用。未持有該權利的部署不會載入此能力的類別。比較版本並取得授權

這三個型別都位於 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-Valuestring 原始簽章位元組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,並載入憑證HsmOperationExceptionproc_open 已停用、缺少 module/config/憑證檔、二進位檔失敗、無 backend);InvalidArgumentException$keyUri 中含 pin-valueOpenSslCliBackend::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> / OpenSslCliBackendgetCertificateDer, getCertificateChainDer, getPublicKeyAlgorithm, getCertificatePem, getResolvedBackend, getOpensslVersion
HsmSignerProviderAdapter::__construct()HsmSignerInterface $hsm, string $providerId, SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15將 HSM 具體型別包裝為 SignerProviderInterfaceProvider id 慣例:pkcs11-{module-id}openssl-cli
HsmSignerProviderAdapter::providerId()回傳建構子提供的 idnon-empty-string
HsmSignerProviderAdapter::supportsAlgorithm()SignatureAlgorithm $algo將 enum 對應為 OpenSSL 風格的名稱,然後與 backend 允許集取交集bool拒絕僅雜湊(digest-only)的演算法;openssl-engine id 不公告任何項目
HsmSignerProviderAdapter::sign()string $data, ?string $keyVersion = null以設定的演算法透過所包裝的簽章器派發non-empty-stringKeyManagementException$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'): string
public function signPqs(string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true): string
public function isPostQuantumEnabled(): bool
public function getCertificateDer(): string
public function getCertificateChainDer(): array
public 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'): string
public function getCertificateDer(): string
public function getCertificateChainDer(): array
public function getPublicKeyAlgorithm(): string
public function getCertificatePem(): string
public function getResolvedBackend(): OpenSslCliBackend
public function getOpensslVersion(): string
public function __construct(private HsmSignerInterface $hsm, private string $providerId, private SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15)
public function providerId(): string
public function supportsAlgorithm(SignatureAlgorithm $algo): bool
public 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 能取得新的登入。
  • 演算法集(封閉)。 兩個簽章器恰好接受:sha256WithRSAEncryptionsha384WithRSAEncryptionsha512WithRSAEncryptionRSASSA-PSSRSASSA-PSS-SHA256RSASSA-PSS-SHA384RSASSA-PSS-SHA512ecdsa-with-SHA256ecdsa-with-SHA384ecdsa-with-SHA512Pkcs11Signer 額外接受 ecdsa-raw。任何其他識別碼都會拋出 InvalidArgumentException——絕不會簽署任何替代演算法。
  • PSS salt 綁定。 對每個 PSS 變體,salt 長度都等於摘要長度——32、48 或 64 位元組——且 hash 與 MGF 參數與所選摘要相符。此舉遵循 PSS 機制參數結構,其中 salt 長度通常為訊息雜湊長度(PKCS#11 v3.1 §6.1.9)。兩個簽章器套用相同的配對,因此在一個 backend 上有效的組態在另一個上也有效。
  • ECDSA 轉換。 token 以 rs 的原始、零填補串接形式回傳 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 檔模式會以 HsmOperationException fail closed:那裡的檔案權限位元無法限制 ACL 讀取授權,因此簽章器拒絕在暫存目錄的 ACL 下留下明文 PIN。Legacy PIN 傳遞是為受信任的 Windows 主機記載的選用替代方案。
  • Backend 自動偵測的 provider 路徑需要 OpenSSL 3.x;LibreSSL 永遠不會解析為 provider。當 provider 與 engine 探測皆未成功時,建構會以 HsmOperationException 失敗,而非將失敗延後到簽章時。
  • 超過 $timeoutSeconds 的子行程會遭終止並回報為逾時;乾淨結束但輸出為空的子行程會回報為空簽章失敗。兩種情況都不會產生部分簽署的文件。
  • 位元組長度與所選參數集不符的後量子簽章會在進入 CMS 編碼之前遭拒絕。
  • 使用已退役的 openssl-engine provider id 的 HsmSignerProviderAdapter 不公告任何演算法,因此陳舊的組態會在 provider 選取時失敗,而非在簽章時。

兩個簽章器都接受一個選用的 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.1CKA_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 204HashML-DSA context handling
FIPS 140-3 驗證透過 CMVP 附著於密碼模組。FIPS 140-3CMVP 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() 是為了記錄證據而存在;當你的合規計畫要求可重現性時,請將其與簽章證據一併保存。

本頁僅記載外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表、runbook 檔名與工單前綴皆不在範圍內。