Add PKCS#11-based PDF signing infrastructure

Introduced a cryptographic infrastructure for PDF signing and validation using PKCS#11-based hardware security modules (HSMs) or software tokens. Key changes include:

- Added `PdfDigitalSignatureService` for signing PDFs, multi-signature support, and signature validation.
- Implemented `Pkcs11ExternalSigner` and `Pkcs11PdfSigningRsa` for PKCS#11-based signing with DevExpress PDF infrastructure.
- Added utilities like `Pkcs11SessionRunner`, `Pkcs11InteropBootstrap`, and `Pkcs11ToolBridge` for session management, library initialization, and fallback mechanisms.
- Introduced `Pkcs11Options` for managing PKCS#11 configurations.
- Updated project dependencies to include `DevExpress.Document.Processor`, `System.Security.Cryptography.Pkcs`, and `Pkcs11Interop`.
- Enhanced error handling and fallback support for PKCS#11 operations.

These changes enable robust and extensible PDF signing capabilities with detailed signature metadata extraction and validation.
This commit is contained in:
2026-10-13 14:45:47 +02:00
parent 7766e2ea25
commit f361898ba4
9 changed files with 897 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
using EnvelopeGenerator.Application.Common.Configurations;
namespace EnvelopeGenerator.Infrastructure.Crypt.Services;
internal static class Pkcs11InteropBootstrap
{
private static readonly object Sync = new();
private static bool Initialized;
public static void EnsureLoaded(Pkcs11Options options)
{
if (Initialized)
return;
lock (Sync)
{
if (Initialized)
return;
EnsureNativeDependencySearchPath(options.LibraryPath);
if (!string.IsNullOrWhiteSpace(options.ProxySocket))
{
Environment.SetEnvironmentVariable("PKCS11_PROXY_SOCKET", options.ProxySocket);
Environment.SetEnvironmentVariable("PKCS11_DAEMON_SOCKET", options.ProxySocket);
}
if (!string.IsNullOrWhiteSpace(options.ToolPath))
{
var toolDir = Path.GetDirectoryName(options.ToolPath);
if (!string.IsNullOrWhiteSpace(toolDir))
EnsurePathContains(toolDir);
}
WarmupWithPkcs11Tool(options);
Initialized = true;
}
}
private static void WarmupWithPkcs11Tool(Pkcs11Options options)
{
if (string.IsNullOrWhiteSpace(options.ToolPath))
return;
if (!File.Exists(options.ToolPath))
return;
var process = new System.Diagnostics.Process
{
StartInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = options.ToolPath,
Arguments = $"--module \"{options.LibraryPath}\" -L",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
process.Start();
process.WaitForExit(15000);
}
private static void EnsureNativeDependencySearchPath(string libraryPath)
{
var directory = Path.GetDirectoryName(libraryPath);
if (string.IsNullOrWhiteSpace(directory))
return;
EnsurePathContains(directory);
}
private static void EnsurePathContains(string directory)
{
var currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
var entries = currentPath.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (entries.Any(p => string.Equals(p, directory, StringComparison.OrdinalIgnoreCase)))
return;
var updatedPath = string.IsNullOrWhiteSpace(currentPath)
? directory
: directory + ";" + currentPath;
Environment.SetEnvironmentVariable("PATH", updatedPath);
}
}