该页面可以包含自动翻译的文本。
如何在 C# 和 VB.NET 中使用 USB 令牌和 HSM 设备对 PDF 进行签名
受信任的证书颁发机构不再允许下载文档签名证书的私钥。私钥必须存储在安全硬件中,例如 USB 令牌、智能卡或基于云的 HSM。
您可以使用 Docotic.Pdf 库 在 .NET 中使用 HSM 设备对 PDF 文档进行签名。本文介绍了如何与 Azure Key Vault、AWS KMS 以及符合 PKCS#11 标准的硬件集成。
9.7.18373 15,244 通过 NuGet 总下载量 5,976,723您可以在 下载 C# .NET PDF 库 页面获取该库和免费的限时许可证密钥。
先决条件
本文中的代码示例使用自签名证书对 PDF 文档进行签名。默认情况下,PDF 查看器不信任这些签名。自签名证书仅应用于测试,不应用于生产环境。要生成受信任的 PDF 签名,请使用由第三方证书颁发机构颁发的文档签名证书。
获取正确的证书
优先选择 Adobe 认可信任列表 (AATL) 中列出的公司颁发的证书。Adobe Acrobat 默认信任此类公司的证书,不会报告“证书有效性未知”的警告。
要创建符合欧盟 (eIDAS) 标准的文档,您需要使用由 欧盟信任列表 (EUTL) 中列出的提供商颁发的合格证书。
了解如何在 .NET 中使用您的证书
当您订购文档签名证书时,通常会获得 HSM 设备的访问权限。这可以是物理 USB 令牌,也可以是基于云的 HSM 解决方案。
然后,您应该学习如何使用此 HSM 签名数据。这通常是最棘手的部分,因为所有加密提供商都不同。您应该遵循 USB 令牌或云 API 的文档。
理想情况下,您需要找到一个对应的 .NET SDK。或者至少找到一个 原生 SDK 并通过 .NET 互操作使用它。
您的目标是编写能够使用您的证书对任何字节进行签名的 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.X509Store 或 Pkcs11Interop 库。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 安全地存储证书和密钥。您需要进行以下准备:
- 创建 Azure 帐户。
- 创建 Azure 用户。
- 创建 Key Vault。
- 允许所选 Azure 用户访问 Key Vault。
- 将文档签名证书添加到 Key Vault。您可以从集成证书颁发机构导入证书。
然后,使用 Azure.Security.KeyVault.Keys
NuGet 包在 .NET 中签名。使用 Azure Key Vault 中的密钥进行签名的 C# 示例代码如下:
var vaultUrl = new Uri("https://YOUR-VAULT.vault.azure.net/");
var credential = new DefaultAzureCredential();
var keyClient = new KeyClient(vaultUrl, credential);
KeyVaultKey key = keyClient.GetKey("YOUR-KEY");
var cryptoClient = new CryptographyClient(key.Id, credentials);
byte[] messageToSign = ..;
byte[] signature = cryptoClient.SignData(SignatureAlgorithm.RS512, messageToSign).Signature;
相关的“IPdfSigner”实现可能如下所示:
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, new[] { cert })
{
DigestAlgorithm = signer.DigestAlgorithm,
Field = field,
};
pdf.SignAndSave(options, ..);
在 GitHub 上探索完整的 使用 Azure Key Vault 签署 PDF 文档 代码示例。
使用 AWS KMS 签名 PDF 文档
AWS 密钥管理服务 (KMS) 是另一种安全的加密密钥存储方式。准备步骤与 Azure Key Vault 相同:
- 创建 AWS 账户。
- 创建 IAM 用户。
- 添加签名密钥。
- 允许所选 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, new[] { 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 文档部分。
如何将 SoftHSM 与我的 USB 令牌或智能卡配合使用?
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 文档 代码示例。