介紹與動機
在企業級系統中實作數位簽章時,安全性是絕對不可妥協的。
將憑證存放於本機的 PFX 或 P12 檔案雖然方便,卻會使私鑰面臨被擷取或洩漏的風險。相較之下,PKCS#11 硬體代幣(如 USB 加密狗、智慧卡與 HSM)會將金鑰保留在防篡改的邊界內,確保金鑰永不離開裝置。
本篇文章示範如何結合 GroupDocs.Signature for .NET 與 Pkcs11Interop,使用硬體代幣為 PDF 文件簽章。此方式兼具便利性與合規性:GroupDocs 處理所有 PDF 層級的封裝(簽章欄位、摘要計算、嵌入),而代幣則負責實際的密碼學簽署。
⚠️ 早期實作說明
此解決方案目前以早期實作的形式提供,用於在 GroupDocs.Signature 中使用 PKCS#11 數位簽章加密狗。
雖然它已能讓文件透過硬體代幣簽署,我們仍強烈建議在自己的環境中進行額外測試,以確保符合您的合規與安全需求。
歡迎提供回饋、測試結果與改進建議,我們將不勝感激。
挑戰:將 PKCS#11 與 PDF 簽章銜接
將 PKCS#11 代幣整合至文件簽章工作流程時,會面臨多項非平凡的挑戰:
- 低階複雜度 ─ PKCS#11 API(Cryptoki)需要管理槽位、會話、句柄與屬性,以找出正確的私鑰。
- PDF 層級封裝 ─ 簽署 PDF 不只是對位元組簽名:程式庫必須正確計算選取位元組範圍的摘要、將簽章包裝於 CMS/PKCS#7 容器、加入時間戳記,並嵌入驗證資訊。
- 廠商差異 ─ 不同代幣/廠商模組可能需要自訂屬性對映或額外的中介軟體。
- 合規與稽核 ─ 生產系統需要健全的 PIN 處理、會話生命週期管理、錯誤復原與日誌記錄。
此範例專案透過結合 GroupDocs.Signature 中的 ICustomSignHash 介面與 Pkcs11Interop,將簽署工作交給代幣,同時讓 GroupDocs 處理 PDF 結構。
範例專案的功能
- 示範如何使用 PKCS#11 代幣(加密狗、智慧卡、HSM)簽署 PDF 文件。
- 支援 Windows 憑證存儲 後備:若 Windows 已安裝憑證,程式碼也能改用該憑證。
- 實作 自訂雜湊簽署:GroupDocs 計算摘要;代幣僅簽署雜湊值。
- 私鑰 始終保留在硬體上,絕不匯出。
- 將代幣邏輯(會話、金鑰查找、簽署)封裝於
Pkcs11DigitalSigner.cs。 - 提供
Helpers.cs(例如在 Windows 存儲中查找憑證)的輔助程式碼。 - 設定集中於
Settings.cs。 - 作為可依需求調整的參考實作。
設定與先決條件
先決條件
- .NET 6.0 或以上(或 .NET Framework 4.6.2)
- 來自代幣廠商的有效 PKCS#11 函式庫(DLL)
- 具備有效憑證的硬體代幣(USB 加密狗、智慧卡或 HSM)
- GroupDocs.Signature for .NET(試用版或正式授權)
- Pkcs11Interop 函式庫
安裝
git clone https://github.com/groupdocs-signature/esign-documents-with-pkcs11-using-groupdocs-signature-dotnet.git
cd esign-documents-with-pkcs11-using-groupdocs-signature-dotnet
dotnet restore
在 Visual Studio 或您偏好的 IDE 中開啟解決方案,確保相依性已正確解析。
Repository 結構深度說明
GroupDocs.Signature-for-.NET-PKCS11-Sample/
├── GroupDocs.Signature-for-.NET-PKCS11-Sample.csproj # 專案檔
├── Program.cs # 入口點與使用流程
├── Settings.cs # PKCS#11 / 代幣設定
├── Helpers.cs # 工具函式(Windows 存儲、憑證篩選)
├── Pkcs11DigitalSigner.cs # 透過 PKCS#11 實作 ICustomSignHash
└── README.md # 說明與使用指引
- Program.cs ─ 協調簽署流程;示範代幣與 Windows 憑證兩種情境。
- Settings.cs ─ 含有
Pkcs11LibraryPath、TokenPin、CertificateSubject等常數/佔位符。 - Helpers.cs ─ 包含在 Windows 存儲中依主旨名稱尋找憑證的程式碼(用於後備流程)。
- Pkcs11DigitalSigner.cs ─ 核心邏輯:載入 PKCS#11 模組、開啟會話、定位私鑰物件、簽署摘要,並回傳
X509Certificate2或簽章回呼實作。 - README.md ─ 提供概觀、挑戰與使用說明(本部落格即為補充說明)。
程式碼說明與導覽
Settings.cs
public static class Settings
{
public const string Pkcs11LibraryPath = "<PKCS11_LIBRARY_PATH>";
public const string TokenPin = "<TOKEN_PIN>";
public const string CertificateSubject = "<CERT_SUBJECT>";
}
此檔案將設定細節抽離,方便在部署環境中替換。
Pkcs11DigitalSigner.cs ─ 高階流程
public class Pkcs11DigitalSigner : ICustomSignHash
{
public byte[] SignHash(byte[] hash)
{
// 此方法在 GroupDocs.Signature 需要代幣簽署雜湊時被呼叫
using (var pkcs11 = new Pkcs11(Settings.Pkcs11LibraryPath, AppType.SingleThreaded))
{
// 載入模組、開啟會話、使用 PIN 登入、尋找金鑰並執行簽署
}
}
public X509Certificate2 GetCertificateFromPkcs11()
{
// 從代幣取得公鑰憑證,以便設定簽章選項
}
}
SignHash為核心方法:接收 GroupDocs 計算好的摘要,然後使用 PKCS#11 API 進行簽署。GetCertificateFromPkcs11取得代幣內的憑證(含公鑰),確保簽章的元資料正確。
Program.cs ─ 使用流程
class Program
{
static void Main()
{
string inputFile = "sample.pdf";
string outputFile = "signed.pdf";
// (1) PKCS#11 簽署
var tokenSigner = new Pkcs11DigitalSigner();
var cert = tokenSigner.GetCertificateFromPkcs11();
using (var signature = new Signature(inputFile))
{
var options = new DigitalSignOptions(cert)
{
Comments = "Signed with PKCS#11 token",
SignTime = DateTime.Now,
CustomSignHash = tokenSigner // 連結代幣簽署
};
signature.Sign(outputFile, options);
}
// (2) Windows 憑證存儲後備(可選)
// var storeCert = Helpers.GetCertificateFromWindowsStore(Settings.CertificateSubject);
// using (var signature2 = new Signature(inputFile))
// {
// var options2 = new DigitalSignOptions(storeCert) { ... };
// signature2.Sign("signed_store.pdf", options2);
// }
}
}
重點說明:
- 透過
DigitalSignOptions的CustomSignHash屬性指派tokenSigner,讓 GroupDocs 將實際的雜湊簽署委派給代幣。 - 註解掉的後備流程示範在硬體代幣不可用時,如何改用 Windows 存儲中的憑證。
使用情境與實務案例
- 印度與 CA 發行的 USB 簽章加密狗
在印度,許多具法律效力的 eSignature 必須使用由認證機構發行、存放於 USB 加密狗的憑證。本範例讓文件閘道、入口網站等應用直接與此類加密狗整合。 - 企業文件工作流程
於合約管理或審批流程等內部系統中,硬體簽署可確保未授權使用者無法偽造文件簽章。 - 法律/合規導向的簽署
政府與受規範產業常要求簽章必須來自硬體受控金鑰,此整合有助滿足嚴格的稽核與合規需求。
常見問題與除錯
- 函式庫路徑錯誤 → PKCS#11 DLL 必須與代幣廠商提供的模組相符(例如
softhsm2.dll、cryptoki.dll)。 - PIN 鎖定或失敗 → 重複錯誤的 PIN 可能導致代幣被鎖,請參考廠商政策。
- 找不到金鑰 → 確認提供的憑證主旨正確;代幣必須包含相符主旨的憑證。
- 缺少驅動或中介軟體 → 某些代幣需要先安裝廠商驅動,才能讓 Pkcs11Interop 通訊。
- 執行緒問題 → PKCS#11 操作可能不支援多執行緒;除非廠商明確支援,請使用單執行緒模式。
- 逾時或會話重置 → 長時間操作可能導致會話關閉或逾時,請確保正確的會話管理與資源釋放。
安全性與最佳實踐
- 絕不要在程式碼中硬編碼生產環境的機密(PIN、函式庫路徑);使用安全的組態或祕密管理。
- 使用 強 PIN,並在政策允許的情況下定期更換。
- 記錄操作與錯誤(但不要記錄敏感 PIN)。
- 限制代幣會話,簽署完畢後立即登出。
- 簽署後驗證簽章(鏈結檢查、時間戳記)。
- 在不同環境與代幣類型(加密狗/智慧卡/HSM)中進行測試。
後續步驟與資源
準備好自行嘗試了嗎?克隆儲存庫、更新佔位符,然後執行範例。您可能想進一步探索的主題:
- 自訂雜湊簽署(將摘要與簽署委派給代幣)
- 時間戳記與 LTV / DSS 嵌入
- 迭代簽署(在同一文件中加入多個簽章)
- 與遠端 HSM 服務或雲端代幣儲存的整合