該頁面可以包含自動翻譯的文字。

如何在 C# 和 VB.NET 中使用 USB 權杖與 HSM 裝置為 PDF 簽署

受信任的憑證授權單位不再允許匯出用於文件簽章憑證的私密金鑰。取而代之的是,私密金鑰必須儲存在安全硬體中,例如 USB 權杖、智慧卡或雲端 HSM。

您可以使用 Docotic.Pdf 程式庫 在 .NET 中透過 HSM 裝置簽署 PDF 文件。本文說明與 Azure Key Vault、AWS KMS,以及相容 PKCS#11 的硬體整合。

您可以在 下載 C# .NET PDF 程式庫 頁面取得程式庫和免費的限時授權金鑰。

外部 PDF 簽署

先決條件

本文中的程式碼範例使用自簽憑證來簽署 PDF 文件。預設情況下,PDF 檢視器不信任這些簽章。自簽憑證只應用於測試,不應用於生產環境。若要產生受信任的 PDF 簽章,請使用第三方憑證授權單位簽發的文件簽章憑證。

取得正確的憑證

優先選擇列於 Adobe Approved Trust List (AATL) 的公司所提供的憑證。Adobe Acrobat 預設信任這些公司的憑證,且不會報告「憑證有效性未知」警告。

若要建立符合 EU(eIDAS)的文件,您需要使用列於 European Union Trusted Lists (EUTL) 的提供者所簽發的合格憑證。

學會在 .NET 中使用您的憑證

當您訂購文件簽章憑證時,通常會獲得 HSM 裝置的存取權。這可能是實體 USB 權杖,或雲端 HSM 解決方案。

接著,您應該學會如何使用這個 HSM 來簽署資料。這通常是最棘手的部分,因為各家加密提供者都不同。您應該參考 USB 權杖或雲端 API 的文件。

理想情況下,您需要找到對應的 .NET SDK。至少也要找到原生 SDK,並在 .NET 中透過 interop 使用它。

您的目標是取得可使用憑證簽署任意位元組的 C# 或 VB.NET 程式碼。

PDF 外部簽署 API

一旦您可以使用 HSM 裝置簽署任意位元組,就可以開始使用 Docotic.Pdf 簽署 PDF 文件。為此,請實作 IPdfSigner 介面 並用它進行簽署。

實作 IPdfSigner 介面

IPdfSigner 宣告兩個成員。SignatureAlgorithm 屬性會回傳用於簽署的演算法。通常會使用 RSA 或 ECDSA 演算法。

Sign 方法是外部簽署流程的關鍵部分。Docotic.Pdf 會將 PDF 文件位元組的摘要傳遞給此方法。您應使用 HSM 裝置對這個摘要簽章,並回傳數位簽章的位元組。請注意,這裡不能回傳 PKCS#7 物件。只需產生並回傳密碼學簽章即可。

Docotic.Pdf 會針對每個簽章呼叫兩次 Sign 方法。第一次是計算簽章所需空間。第二次則是實際對文件簽章。

建立用於外部簽署的 PdfSigningOptions

使用 PdfSigningOptions(IPdfSigner, X509Certificate2[]) 建構函式 透過您的 IPdfSigner 實作來簽署 PDF 文件。取得憑證鏈,並將其作為第二個引數提供。至少需要一個憑證,且簽章憑證應排在第一位。

拼圖的最後一塊是 PdfSigningOptions.DigestAlgorithm 屬性。此屬性的值必須與 IPdfSigner.SignatureAlgorithm 以及 IPdfSigner.Sign 的實作一致。

讓我們來看看在常見情境下如何使用外部簽章來簽署 PDF。

使用 C# 中的 PKCS#11 驅動程式簽署 PDF 文件

如果您的加密 USB 權杖或智慧卡附帶 PKCS#11 驅動程式,則可以在 .NET 中使用 Pkcs11Interop.X509StorePkcs11Interop 程式庫。Pkcs11Interop 與 Atos CardOS 智慧卡、YubiKey PIV、SmartCard-HSM、SafeNet ProtectServer HSM 及其他 HSM 相容。

使用 PKCS#11 驅動程式簽署 PDF 文檔 程式碼範例示範如何使用 USB 權杖或智慧卡簽署 PDF 文件。您需要自訂以下幾行:

string pkcsLibraryPath = @"C:\Program Files\SoftHSM2\lib\softhsm2-x64.dll";
ulong slotId = 1743347971;
string alias = "YOUR-ALIAS";
string certLabel = "YOUR-CERTIFICATE";
string pin = "YOUR-PIN";
var digestAlgorithm = PdfDigestAlgorithm.Sha512;

我們的範例設定使用 SoftHSM2 作為 USB 權杖的模擬器。在實際應用中,您不需要 SoftHSM2。請改用 USB 權杖隨附的驅動程式。

使用 Azure Key Vault 簽署 PDF 文件

Microsoft Azure Key Vault 會安全地儲存憑證與金鑰。您需要完成以下準備:

  1. 建立 Azure 帳戶。
  2. 建立 Azure 使用者。
  3. 建立 Key Vault。
  4. 允許所選 Azure 使用者存取 Key Vault。
  5. 將文件簽章憑證新增至 Key Vault。您可以從整合式憑證授權單位 匯入 憑證。

接著,使用 Azure.Security.KeyVault.Keys NuGet 套件在 .NET 中進行簽署。以下是使用 Azure Key Vault 中金鑰進行簽署的 C# 範例:

var vaultUrl = new Uri("https://YOUR-VAULT.vault.azure.net/");
var credentials = new DefaultAzureCredential();

var keyClient = new KeyClient(vaultUrl, credentials);
KeyVaultKey key = keyClient.GetKey("YOUR-KEY");
var cryptoClient = new CryptographyClient(key.Id, credentials);
byte[] messageToSign = ..;
byte[] signature = cryptoClient.SignData(SignatureAlgorithm.RS512, messageToSign).Signature;

相關的 IPdfSigner 實作可能如下所示:

using AzureSignatureAlgorithm = SignatureAlgorithm;

class AzureSigner : IPdfSigner
{
    private readonly CryptographyClient m_client;
    private readonly AzureSignatureAlgorithm m_signingAlgorithm;

    public AzureSigner(CryptographyClient client, AzureSignatureAlgorithm signingAlgorithm)
    {
        m_client = client;
        m_signingAlgorithm = signingAlgorithm;
    }

    public PdfSignatureAlgorithm SignatureAlgorithm
    {
        get
        {
            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS256 ||
                m_signingAlgorithm == AzureSignatureAlgorithm.RS384 ||
                m_signingAlgorithm == AzureSignatureAlgorithm.RS512)
                return PdfSignatureAlgorithm.Rsa;

            throw new NotSupportedException($"Unsupported {nameof(SignatureAlgorithm)} value: {m_signingAlgorithm}");
        }
    }

    public PdfDigestAlgorithm DigestAlgorithm
    {
        get
        {
            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS256)
                return PdfDigestAlgorithm.Sha256;

            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS384)
                return PdfDigestAlgorithm.Sha384;

            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS512)
                return PdfDigestAlgorithm.Sha512;

            throw new NotSupportedException($"Unsupported {nameof(SignatureAlgorithm)} value: {m_signingAlgorithm}");
        }
    }

    public byte[] Sign(byte[] message)
    {
        return m_client.SignData(m_signingAlgorithm, message).Signature;
    }
}

最後,使用這個 AzureSigner 類別來簽署 PDF 文件:

CryptographyClient cryptoClient = ..;
using X509Certificate2 cert = ..;
var signer = new AzureSigner(cryptoClient, SignatureAlgorithm.RS512);

using var pdf = new PdfDocument();

PdfPage page = pdf.Pages[0];
PdfSignatureField field = page.AddSignatureField(50, 50, 200, 200);

var options = new PdfSigningOptions(signer, [cert])
{
    DigestAlgorithm = signer.DigestAlgorithm,
    Field = field,
};

pdf.SignAndSave(options, ..);

在 GitHub 上查看完整的 使用 Azure Key Vault 簽署 PDF 文檔 程式碼範例。

使用 AWS KMS 簽署 PDF 文件

AWS Key Management Service (KMS) 是另一種用於加密金鑰的安全儲存方式。準備步驟與 Azure Key Vault 相同:

  1. 建立 AWS 帳戶。
  2. 建立 IAM 使用者。
  3. 新增簽署金鑰。
  4. 允許所選 IAM 使用者存取金鑰。

對於 AWS,您需要在 .NET 中使用 AWSSDK.KeyManagementService NuGet 套件。PDF 簽署的範例程式碼與 Azure 情境相當接近:

using var pdf = new PdfDocument();

PdfPage page = pdf.Pages[0];
PdfSignatureField field = page.AddSignatureField(50, 50, 200, 200);

var signingAlgorithm = SigningAlgorithmSpec.RSASSA_PSS_SHA_512;
var signer = new AwsSigner("arn:aws:kms:YOUR-id", signingAlgorithm);
var options = new PdfSigningOptions(signer, [cert])
{
    DigestAlgorithm = signer.DigestAlgorithm,
    Field = field,
};

pdf.SignAndSave(options, ..);


class AwsSigner : IPdfSigner
{
    private readonly string m_keyId;
    private readonly SigningAlgorithmSpec m_signingAlgorithm;

    public AwsSigner(string keyId, SigningAlgorithmSpec signingAlgorithm)
    {
        m_keyId = keyId;
        m_signingAlgorithm = signingAlgorithm;
    }

    public PdfSignatureAlgorithm SignatureAlgorithm
    {
        get
        {
            if (m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_256 ||
                m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_384 ||
                m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_512)
                return PdfSignatureAlgorithm.Rsa;

            throw new NotSupportedException($"Unsupported {nameof(SigningAlgorithmSpec)} value: {m_signingAlgorithm}");
        }
    }

    public PdfDigestAlgorithm DigestAlgorithm
    {
        get
        {
            string alg = m_signingAlgorithm.Value;
            if (alg.EndsWith("256"))
                return PdfDigestAlgorithm.Sha256;

            if (alg.EndsWith("384"))
                return PdfDigestAlgorithm.Sha384;

            if (alg.EndsWith("512"))
                return PdfDigestAlgorithm.Sha512;

            throw new NotSupportedException($"Unsupported {nameof(SigningAlgorithmSpec)} value: {m_signingAlgorithm}");
        }
    }

    public byte[] Sign(byte[] message)
    {
        using var kmsClient = new AmazonKeyManagementServiceClient();
        using var stream = new MemoryStream(message);
        var signRequest = new SignRequest()
        {
            SigningAlgorithm = m_signingAlgorithm,
            KeyId = m_keyId,
            MessageType = MessageType.RAW,
            Message = stream,
        };
        SignResponse signResponse = kmsClient.SignAsync(signRequest).GetAwaiter().GetResult();
        return signResponse.Signature.ToArray();
    }
}

完整的 使用 AWS KMS 簽署 PDF 文檔 程式碼範例也可在 GitHub 上取得。

結論

利用 Docotic.Pdf 程式庫 透過 USB 權杖、智慧卡或雲端 HSM 簽署 PDF 文件。為此,請在 C# 或 VB.NET 程式碼中實作 IPdfSigner 介面。

從 GitHub 下載並試用 PDF 中數位簽章的程式碼範例。另請參閱相關文章以取得更多資訊:

常見問題

如何使用 USB 權杖或智慧卡簽署 PDF?

使用 Docotic.Pdf 和 Pkcs11Interop .NET 程式庫,透過權杖或智慧卡簽署 PDF。更多細節請參閱 使用 C# 中的 PKCS#11 驅動程式簽署 PDF 文件 章節。

如何在我的 USB 權杖或智慧卡中使用 SoftHSM?

SoftHSM 會模擬真實的 HSM 裝置。如果您有 USB 權杖或智慧卡,就不需要 SoftHSM。請改用 HSM 裝置隨附的 PKCS#11 驅動程式。

如何使用外部簽章簽署 PDF 文件?

請查看 使用 Azure Key Vault 簽署 PDF 文件使用 AWS KMS 簽署 PDF 文件 章節。使用其他雲端 HSM 進行外部 PDF 簽署的流程也類似。

如何在 .NET 中使用 Azure Key Vault?

使用 Azure.Security.KeyVault.Keys NuGet 套件。試試來自 GitHub 的 使用 Azure Key Vault 簽署 PDF 文檔 程式碼範例。