Refactored `IPdfDigitalSignatureService` to replace `byte[]` with `Stream` for input and output, improving memory efficiency and scalability. Updated `PdfDigitalSignatureService` implementation to support stream-based operations, including signing, checking, and extracting PDFs. Modified `SignPdfAsync` to handle multiple signatures sequentially using streams and ensured proper disposal of intermediate streams. Updated `CheckPdfSignatureAsync` and `ExtractOriginalPdfAsync` to use streams, reducing memory overhead. Refactored `PdfSignatureController` to use streams for file handling, enabling efficient processing of large files. Introduced helper methods for stream handling and enhanced cancellation support. Improved code maintainability by consolidating common logic and reducing duplication.
EnvelopeGenerator.Infrastructure.Crypt
Umfassende technische Dokumentation der PKCS#11/SoftHSM-Integration fuer EnvelopeGenerator.
Dieses Dokument konsolidiert und ersetzt die verteilten Runbooks aus:
pkcs11-proxy-production/README.mdpkcs11-proxy-production/SSH_TEMP_ACCESS_MUST_DO.md- aeltere Abschnitte in diesem README
Zielgruppe: Senior Developer, Platform Engineers, DevOps, Security Owner.
1. Architekturueberblick
1.1 Ziel
EnvelopeGenerator.Infrastructure.Crypt stellt die kryptographische Infrastruktur fuer PDF-Signaturen bereit:
- Signaturerzeugung mit Schluesseln in SoftHSM/PKCS#11
- Zertifikat-Auslese aus Token
- Signaturpruefung auf PDF-Ebene
- Health/Startup-Selbsttest fuer Betriebsfaehigkeit
1.2 Topologie (aktueller Betriebsmodus)
Der aktuell implementierte Produktions-nahe Pfad ist:
- Linux hostet SoftHSM +
pkcs11-daemon(Proxy-Server). - Windows hostet
EnvelopeGenerator.Server. - Windows laedt ein Windows-kompatibles Proxy-PKCS#11-Modul (
libpkcs11-proxy.dll). - PKCS#11-Operationen werden ueber Socket (
PKCS11_PROXY_SOCKET) an Linux weitergeleitet.
Wichtig:
- Ein Windows-Prozess kann kein Linux
.sodirekt laden. - Private Keys verbleiben im Token (nicht exportierbar).
1.3 Laufzeitpfade in der Anwendung
Pkcs11InteropBootstrap loest Assets relativ zu Crypt:Pkcs11:AssetsRootPath auf:
- Modul:
pkcs11\windows\proxy\libpkcs11-proxy.dll - Tool-Fallback:
pkcs11\windows\opensc\tools\pkcs11-tool.exe
Wenn AssetsRootPath leer ist, wird AppContext.BaseDirectory verwendet.
2. Linux-Seite: SoftHSM + Proxy Setup
2.1 Basispakete
Beispiel (Debian/Ubuntu):
sudo apt-get update && sudo apt-get install -y softhsm2 opensc p11-kit libengine-pkcs11-openssl
2.2 SoftHSM-Modulpfad pruefen
ldconfig -p | grep softhsm
find /usr -name "libsofthsm2.so" 2>/dev/null
2.3 Token initialisieren
softhsm2-util --show-slots
softhsm2-util --init-token --slot <uninitialized-slot> --label CompanyProdToken --so-pin <so-pin> --pin <user-pin>
softhsm2-util --show-slots
Hinweis: Nach --init-token wird der Slot i. d. R. neu zugewiesen.
2.4 Schluesselpaar erzeugen
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
--slot <slot-id> --login --pin <user-pin> \
--keypairgen --key-type rsa:3072 --label company-sign-key-v1 --id 01
2.5 Zertifikat importieren
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
--slot <slot-id> --login --pin <user-pin> \
--write-object ./company-sign-cert-v1.der --type cert --id 01 --label company-sign-cert-v1
2.6 Objektzustand verifizieren
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> --list-objects
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> --list-objects --type cert
2.7 Proxy-Daemon endpoint stabilisieren
Ein wiederkehrendes Betriebsproblem war ein zufaelliger/local-only Listener. Fix:
sudo mkdir -p /etc/systemd/system/pkcs11-proxy.service.d
printf "[Service]\nEnvironment=PKCS11_DAEMON_SOCKET=tcp://0.0.0.0:5657\n" | sudo tee /etc/systemd/system/pkcs11-proxy.service.d/override.conf
sudo systemctl daemon-reload
sudo systemctl restart pkcs11-proxy.service
Pruefung:
systemctl show pkcs11-proxy.service -p Environment --no-pager
ss -ltnp | grep 5657
3. Web-API/Server-Seite: Installation und Asset-Bereitstellung
3.1 Bezugsquellen der Windows-Artefakte
Fuer den Proxy-Betrieb werden auf Windows benoetigt:
- Proxy-PKCS#11-DLL (
libpkcs11-proxy.dll) - Abhaengige Runtime-DLLs (z. B. MinGW runtime, je nach Build)
pkcs11-tool.exe+ zugehoerige OpenSC-DLLs fuer Fallback
Beschaffung:
- Build aus derselben
pkcs11-proxy-Codebasis/Commit wie Linux-Daemon (Kompatibilitaet sicherstellen) - oder kontrolliert bereitgestellte, intern freigegebene Binaries
3.2 Empfohlene Ablage im Repo
Bundled Assets liegen unter:
EnvelopeGenerator.Infrastructure.Crypt/resources/pkcs11/windows/proxy/EnvelopeGenerator.Infrastructure.Crypt/resources/pkcs11/windows/opensc/tools/EnvelopeGenerator.Infrastructure.Crypt/resources/pkcs11/windows/opensc/pkcs11/
Diese werden beim Build in den Output kopiert (siehe .csproj-Copy-Regeln).
3.3 Deployment-Muster
Es gibt zwei gueltige Wege:
-
Bundled (Dev/QA, schnelle Reproduzierbarkeit)
- Assets werden mit dem Build ausgeliefert.
AssetsRootPath = null-> Aufloesung viaAppContext.BaseDirectory.
-
Zentraler Asset-Root (Prod, betriebliche Kontrolle)
- Assets in zentralem Pfad (z. B.
E:\DataFiles\Digital Data\EnvelopeGenerator). AssetsRootPathinappsettings.jsonauf diesen Root setzen.
- Assets in zentralem Pfad (z. B.
3.4 Anwendungskonfiguration (Crypt:Pkcs11)
Beispiel:
{
"Crypt": {
"Pkcs11": {
"AssetsRootPath": "E:\\DataFiles\\Digital Data\\EnvelopeGenerator",
"SlotId": 2021549410,
"TokenLabel": "CompanyProdToken",
"ProxySocket": "tcp://172.24.12.64:5657",
"UserPin": "<secret>",
"PrivateKeyLabel": "company-sign-key-v1",
"CertificateLabel": "company-sign-cert-v1",
"ApplicationName": "signFLOW",
"DefaultReason": null,
"UseUtcSigningTime": true,
"DefaultCertificationLevel": "NoCertification"
}
}
}
Sicherheitsregel:
UserPinniemals in versionierte Dateien einchecken.- Secrets nur in Secret Store / nicht versionierte
appsettings.Secrets.json/ Vault.
4. Implementierte Services, Modelle, DTOs
4.1 DI und Startverhalten
AddCryptInfrastructure(...)inDependencyInjection/ServiceCollectionExtensions.cs- bindet
Pkcs11Options - registriert
IPdfDigitalSignatureService - registriert Startup-Self-Test Hosted Service
- bindet
4.2 Konfigurationsmodell
Pkcs11Options(EnvelopeGenerator.Application/Common/Configurations/Pkcs11Options.cs)AssetsRootPath,SlotId,TokenLabel,ProxySocketUserPin,PrivateKeyLabel,CertificateLabelApplicationName,DefaultReason,UseUtcSigningTime,DefaultCertificationLevelResolvedLibraryPath,ResolvedToolPath(Runtime-resolved)
4.3 Kernservices
-
PdfDigitalSignatureService- Signiert PDF via DevExpress
PdfDeferredSignatureBuilder - Nutzt
Pkcs11ExternalSignerfuer externe Signatur - Erzwingt
ApplicationNameaus Config (nicht client-overridable) - Erweitert
Reasonum strukturiertes Metaformat:SFMETA:v1;doc=<uuid>;sha256=<hash>
- Parse beim Verify und Mapping in DTO
- Signiert PDF via DevExpress
-
Pkcs11InteropBootstrap- Loest Modul-/Toolpfade auf
- Setzt
PKCS11_PROXY_SOCKET/PKCS11_DAEMON_SOCKET - PATH-Ergaenzung fuer native Abhaengigkeiten
- Warmup via
pkcs11-tool -L
-
Pkcs11SessionRunner- Interop Library-Load mit mehreren Init-Modi (Fallback-Reihenfolge)
- Slot-Resolution (
SlotId/TokenLabel) - Session Login/Logout
-
Pkcs11ToolBridge- Fallback via Prozessaufruf von
pkcs11-tool - Cert read und DigestInfo-Signatur
- Fallback via Prozessaufruf von
-
Pkcs11StartupSelfTestHostedService- Fuehrt beim Start Interop-Probe aus
- Fallback-Probe bei Interop-Fehler
- Schreibt strukturierten Status
4.4 Wichtige Interfaces/DTOs
IPdfDigitalSignatureServiceSignPdfAsync(...),CheckPdfSignatureAsync(...),ExtractOriginalPdfAsync(...)
IPkcs11SelfTestStatusProviderPdfSignOptionsDtoUUID,Reason,Name,Location,Email,Phone,CertificationLevel
PdfSignatureCheckResultDto/PdfSignatureCheckItemDto- inklusive
BusinessReason,UUID,OriginalDocumentSha256
- inklusive
Pkcs11SelfTestStatusDto
5. Betriebserfahrungen und Challenges
5.1 Interop vs Tool-Verhalten
Beobachtetes Muster in der realen Umgebung:
pkcs11-toolkonnte erfolgreich auf Token zugreifen und signieren.Pkcs11Interopscheiterte mitCKR_FUNCTION_NOT_SUPPORTEDbzw. fehlendemC_InitializePointer in bestimmten Modi.
Konsequenz:
- Architektur wurde auf interop-first + controlled tool-fallback erweitert.
- Health-Endpunkt differenziert Modus (
interop,pkcs11-tool,failed).
5.2 Typische Fehlbilder
CKR_DEVICE_ERRORbei Socket-/Proxy-ProblemenCRYPT_PROVIDER_UNAVAILABLEbei Pfad/ABI/Dependency-MismatchCRYPT_SLOT_NOT_FOUNDbei Slot-/Label-MismatchCKR_PIN_INCORRECTbei PIN-Fehler- 307 Redirects bei HTTP/HTTPS-Mix in manuellen Tests
5.3 Plattformfallen
- MSVC vs MinGW Build-Unterschiede fuer Proxy-Client
- fehlende Laufzeit-DLLs (
PATH/Side-by-side) - Architektur-Mismatch x86 vs x64
- Daemon-Bindings nicht stabil (random localhost port statt fixed endpoint)
6. Health, Monitoring und Verifikation
6.1 API Health
- Endpoint:
GET /api/health/crypt - Liefert:
success,mode,summary,interopError,toolError,checkedAtUtc
Empfehlung:
- In Monitoring aufnehmen und bei
mode=pkcs11-toolueber laengere Zeit Warnung triggern.
6.2 Smoke-Test (empfohlen vor Release)
GET /api/health/crypt->success=truePOST /api/PdfSignature/signmit Test-PDFPOST /api/PdfSignature/is-signed- Pruefen:
signed=trueallSignaturesCryptographicallyValid=trueUUID/OriginalDocumentSha256korrekt gemappt
7. Sicherheits- und Betriebsrichtlinien
7.1 Geheimnisse
- PIN, SO-PIN, Root-Creds niemals in Doku/Screenshots/Repo.
- Nach Troubleshooting: Credentials rotieren.
7.2 Temporare SSH-Freigaben zwingend rueckbauen
Wenn SSH temporaer geoeffnet wurde, MUSS danach Rollback erfolgen:
sudo systemctl disable --now ssh && sudo ufw delete allow from 172.24.10.11 to any port 22 proto tcp && sudo ufw status verbose && sudo systemctl status ssh --no-pager
Optional harte Bereinigung:
sudo apt-get purge -y openssh-server && sudo apt-get autoremove -y
7.3 Schluesselrotation
- Neue Labels (
...-v2) anlegen - App umkonfigurieren
- Sign/Verify Regression testen
- Altmaterial erst nach Freigabe entfernen
8. API-Verhalten bei Signaturmetadaten
Aktueller Standard:
UUIDkommt aus Sign-Input (nullable string). Wenn ungeltig/leer -> neue Guid wird erzeugt.Reasonwird intern angereichert mit strukturiertem Meta (SFMETA:v1).ApplicationNameist rein serverseitig ausappsettingsgesetzt.SigningTimeist rein serverseitig und wird automatisch gesetzt (UseUtcSigningTime=true=>UtcNow, sonst lokale Zeit).CertificationLevelwird im Controller serverseitig gesetzt (FormFillingAndAnnotations), nicht vom Client.
Damit ist das Verhalten deterministisch und manipulationsaermer fuer Integrationsclients.
9. Troubleshooting-Checkliste (praxisnah)
- Erreichbarkeit Linux Proxy:
Test-NetConnection <linux-host> -Port 5657
- Token sichtbar?
pkcs11-tool --module <dll> -L -v
- Zertifikat lesbar?
--read-object --type cert --label <cert-label>
- Signatur technisch moeglich?
--sign -m SHA256-RSA-PKCS ...
- App-Health check:
/api/health/crypt
- App-sign/verify end-to-end:
/api/PdfSignature/sign+/api/PdfSignature/is-signed
Wenn Interop scheitert, aber Tool funktioniert:
- Betrieb bleibt verfuegbar (Fallback),
- aber Incident/Bug fuer Interop-Kompatibilitaet erfassen.
10. Ausblick / empfohlene Verbesserungen
- Binary Transport statt Base64 bei grossen PDFs in Render/Sign-Pipelines.
- Persistente Metriken fuer Fallback-Quote (
interopvstool). - Zertifikatskette/OCSP/CRL Validierung als optionales Policy-Modul.
- CI-Pruefung auf unsichere SQL-Muster (parallel zum Security-Workstream).
- Standardisierte Toast/UX-Rueckmeldungen fuer Signierfehler in UI.
11. Referenzen
- SoftHSMv2:
https://github.com/softhsm/SoftHSMv2 - OpenSC / pkcs11-tool:
https://github.com/OpenSC/OpenSC/wiki/Using-pkcs11-tool-and-OpenSSL - PKCS#11 Standard:
https://www.oasis-open.org/committees/pkcs11/