Enhance FES docs, plans, and SoftHSM integration

Updated README.md for clarity and added official references. Introduced detailed infrastructure and integration plans for FES, including phased implementation, deployment options, and risk mitigations. Added FES workflow diagrams in draw.io and PDF formats. Documented SoftHSM testing process and integration details in ticket.md. Improved alignment with clean architecture principles and production readiness.
This commit is contained in:
2026-10-08 10:31:00 +02:00
parent 73b6c2712c
commit 3a71d57619
6 changed files with 1022 additions and 247 deletions

View File

@@ -1,67 +1,83 @@
# EnvelopeGenerator.Infrastructure.Crypt
This project provides PKCS#11-based cryptographic infrastructure for FES (Advanced Electronic Signature) workflows.
PKCS#11-based cryptographic infrastructure for FES (Advanced Electronic Signature) workflows.
If you are new to HSM/SoftHSM: this README is written to let you start from zero and still understand the full path from setup to testing.
Official references:
- SoftHSMv2 (official): `https://github.com/softhsm/SoftHSMv2`
- OpenSC `pkcs11-tool` docs: `https://github.com/OpenSC/OpenSC/wiki/Using-pkcs11-tool-and-OpenSSL`
- PKCS#11 standard overview (OASIS): `https://www.oasis-open.org/committees/pkcs11/`
---
## 1) What this project is (and what it is not)
## 1) Scope
This project is the cryptography layer only.
This project is the crypto layer only.
- It signs hashes using private keys stored in HSM/SoftHSM.
- It reads certificates from HSM/SoftHSM.
- It verifies signatures.
- It exposes service abstractions for Application/Server integration.
- Signs hashes via keys in HSM/SoftHSM.
- Reads certificates from HSM/SoftHSM.
- Verifies signatures.
- Exposes abstractions for Application/Server integration.
It is **not**:
This project is not:
- a REST API by itself,
- a UI component,
- a full document workflow orchestrator.
- a REST API,
- a UI module,
- a full document orchestration workflow.
---
## 2) SoftHSM and PKCS#11 in one minute
## 2) SoftHSM and PKCS#11 basics
- `SoftHSMv2` is a software HSM implementation.
- It does **not** provide a REST endpoint.
- You access it through a PKCS#11 library/module (`.so`/`.dll`).
- Your app loads that library and calls PKCS#11 functions.
- SoftHSM is a software implementation of an HSM.
- It does not expose REST endpoints.
- Access is via PKCS#11 shared library (`.so`/`.dll`).
- App loads module path and performs PKCS#11 operations.
Two deployment patterns are possible:
Supported deployment patterns:
1. **Direct local module**
- Example library path: `libsofthsm2.so`
2. **PKCS#11 proxy module (remote HSM/SoftHSM)**
- Example library path: `libpkcs11-proxy.so` or proxy DLL
1. Direct module (`libsofthsm2.so`, `softhsm2-x64.dll`, etc.)
2. PKCS#11 proxy module (remote HSM/SoftHSM backend)
Both are valid for this project.
Critical platform rule:
- A Windows process cannot load a Linux `.so` directly.
- `Crypt:Pkcs11:LibraryPath` must always point to a native library compatible with the host OS of `EnvelopeGenerator.Server`.
---
## 3) How this maps to the FES diagram (Ablauf SignFlow Unternehmenszertifikat)
## 2.1) Windows Server + Linux SoftHSM architecture (decision TODO)
From the shared draw.io/PDF process:
Current business requirement: `EnvelopeGenerator.Server` runs on Windows, SoftHSM runs on Linux.
- Step 6: Backend computes document hash and sends it with PIN context to HSM.
- Implemented by: `ISignatureProvider` / `Pkcs11SignatureProvider`
- Step 7: HSM returns digital signature (and cert context can be resolved).
- Implemented by: `Pkcs11SignatureProvider` + `ICertificateProvider`
- Step 8: Backend embeds signature + certificate into PDF and creates audit trail.
- This project provides crypto primitives for this step.
- PDF embedding and audit document composition are integrated in later layers.
This repository must use one of the following three paths:
So this project is the cryptographic core for diagram steps 6-8.
1. Linux signing service (recommended)
- Windows `EnvelopeGenerator.Server` calls HTTPS signing API.
- Linux service performs PKCS#11 operations against SoftHSM.
- Private keys stay on Linux.
2. PKCS#11 proxy bridge
- Windows app loads a Windows PKCS#11 proxy client DLL.
- Proxy forwards operations to Linux-side PKCS#11 endpoint connected to SoftHSM.
3. Local Windows SoftHSM (dev/test only, optional)
- SoftHSM is also installed on Windows.
- App talks to local Windows PKCS#11 module.
Actor mapping from the diagram:
TODO - architecture lock-in:
- `Ersteller` (sender): creates envelope and defines recipients.
- `Signierer` (recipient/signer): completes 2FA and signs.
- `Backend` (our stack): computes hash, requests HSM signature, embeds proof in PDF.
- `HSM/SoftHSM`: stores private keys and performs cryptographic signing.
- `CA`: issues/renews certificates.
- [ ] Select one path as production standard (`1` is recommended).
- [ ] Document final runtime topology (hosts, ports, TLS, auth).
- [ ] Define where audit metadata (IP/email/phone/device) is stored and signed/linked.
- [ ] Update `SignatureController.Submit` integration contract for FES branch.
- [ ] Add environment-specific runbook for Windows host + Linux HSM operations.
---
## 3) FES flow mapping
- Step 6: hash is sent to HSM signing operation -> `ISignatureProvider`
- Step 7: signature and certificate context are resolved -> `Pkcs11SignatureProvider`, `ICertificateProvider`
- Step 8: PDF embedding + audit trail are completed by upper layers; this project provides crypto primitives
---
@@ -80,19 +96,17 @@ DI entry point:
---
## 5) Required configuration
## 5) Runtime configuration
Configuration section: `Crypt:Pkcs11`
Typical keys:
```json
{
"Crypt": {
"Pkcs11": {
"LibraryPath": "/usr/lib/softhsm/libsofthsm2.so",
"SlotId": 0,
"TokenLabel": null,
"SlotId": 1720207650,
"TokenLabel": "DevToken",
"UserPin": "***",
"PrivateKeyLabel": "sign-key",
"PrivateKeyIdHex": null,
@@ -103,297 +117,273 @@ Typical keys:
}
```
Security rule:
Security rules:
- Do not commit real PINs to source control.
- Use environment variables / secret vault for production.
- Never commit real PINs.
- Use environment variables or secret vault.
- Use test-only tokens for development.
Production naming convention used in this repository:
- Token label: `CompanyProdToken`
- Private key label: `company-sign-key-v1`
- Certificate label: `company-sign-cert-v1`
These names are also exposed as constants in `EnvelopeGenerator.Infrastructure.Crypt/Options/Pkcs11ProviderOptions.cs`.
---
## 6) The 5 test values you must provide
## 6) Test configuration source
Integration tests need these environment variables:
Crypt tests read PKCS#11 values from `EnvelopeGenerator.Tests/appsettings.json` under `Crypt:Pkcs11`.
- `FES_TEST_PKCS11_LIBRARY_PATH`
- `FES_TEST_PKCS11_SLOT_ID`
- `FES_TEST_PKCS11_USER_PIN`
- `FES_TEST_PKCS11_PRIVATE_KEY_LABEL`
- `FES_TEST_PKCS11_CERTIFICATE_LABEL`
Required keys:
Without these values, integration tests are skipped by design.
- `Crypt:Pkcs11:LibraryPath`
- `Crypt:Pkcs11:SlotId` (or `Crypt:Pkcs11:TokenLabel`)
- `Crypt:Pkcs11:UserPin`
- `Crypt:Pkcs11:PrivateKeyLabel`
- `Crypt:Pkcs11:CertificateLabel`
## 6.1 Which certificate/key should be created?
This is the most important business decision for IT and architecture.
Based on your shared diagram, the **primary model** is:
- one **company certificate + company key pair** in HSM,
- reused for all signers,
- signer identity proven by audit-trail + 2FA evidence.
What this means in practical terms:
- Not one key per sender.
- Not one key per recipient by default.
- One company signing identity, many signing events.
Alternative model (future/optional):
- per-signer certificate/key pair (more complex CA lifecycle).
For current fast validation and your FES flow, use the **company-level key/certificate** model.
No environment variable is required for crypt tests in this repository anymore.
---
## 7) How to obtain these values (IT-friendly)
## 7) Full SoftHSM lifecycle runbook (project scope)
## 7.1 If you use direct SoftHSM module
### A) Find module path
What it is:
- PKCS#11 shared library file used by the app to talk to SoftHSM.
Why needed:
- Without this exact file path, PKCS#11 cannot be loaded.
Linux examples:
### 7.1 Discover module path
```bash
ldconfig -p | grep softhsm
find /usr -name "libsofthsm2.so" 2>/dev/null
```
Use the resolved full path as `FES_TEST_PKCS11_LIBRARY_PATH`.
### B) List slots/tokens
What it is:
- Slot: logical container position in PKCS#11.
- Token: initialized security container in a slot.
Why needed:
- The app must know which slot/token contains the company key and certificate.
### 7.2 List slots/tokens
```bash
softhsm2-util --show-slots
```
Pick your slot as `FES_TEST_PKCS11_SLOT_ID`.
### C) Identify key and certificate labels
What it is:
- `Private key label`: name of the signing private key object.
- `Certificate label`: name of the matching X.509 certificate object.
Why needed:
- Tests and runtime use labels to select correct objects in HSM.
Use `pkcs11-tool` against your module:
### 7.3 Create new token (recommended for new company/test tenant)
```bash
pkcs11-tool --module /path/to/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> --list-objects
softhsm2-util --init-token --slot <uninitialized-slot> --label <token-label> --so-pin <so-pin> --pin <user-pin>
```
From output:
Important:
- private key label -> `FES_TEST_PKCS11_PRIVATE_KEY_LABEL`
- certificate label -> `FES_TEST_PKCS11_CERTIFICATE_LABEL`
- After init, token is reassigned to a new slot id.
- Re-run `--show-slots` and use the new slot id.
### D) User PIN
What it is:
- User authorization secret to open authenticated session on token.
Why needed:
- HSM will not allow signing with private key without login.
PIN is assigned during token initialization and must be provided by IT/security team.
### E) Optional: Initialize token and create objects (Linux quick bootstrap)
Only if token/keys are not already provisioned by IT PKI team.
### 7.4 Login and inspect objects
```bash
# 1) Initialize token in slot 0 (example)
softhsm2-util --init-token --slot 0 --label "CompanySignToken"
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> --list-objects
```
# 2) Verify slot/token
softhsm2-util --show-slots
### 7.5 Generate company signing key pair
# 3) Generate RSA key pair inside token (example)
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot 0 --login --pin <USER_PIN> \
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> \
--keypairgen --key-type rsa:3072 --label sign-key --id 01
```
# 4) Import certificate (already issued by CA)
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot 0 --login --pin <USER_PIN> \
### 7.6 Import company certificate
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> \
--write-object company-sign-cert.der --type cert --label sign-cert --id 01
# 5) List objects and confirm labels
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot 0 --login --pin <USER_PIN> --list-objects
```
Notes:
### 7.7 Read object metadata (for diagnostics)
- Use this only in non-production unless approved.
- In production, key generation/import should follow company security policy.
```bash
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-mechanisms
```
## 7.2 If you use PKCS#11 proxy module
### 7.8 Update operations
What it is:
PKCS#11 objects are usually immutable for key/cert payload; updates are done by rotation:
- A local PKCS#11 bridge library that forwards calls to remote HSM/SoftHSM service.
1. Create/import new key/cert with new label or id.
2. Update app config/env labels.
3. Validate with integration tests.
4. Delete old objects when safe.
Why needed:
PIN operations:
- App still loads a local module path, but crypto operations happen remotely.
- User PIN change (while logged in):
Ask IT for:
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <old-user-pin> --change-pin --new-pin <new-user-pin>
```
- proxy module path (`libpkcs11-proxy.*`)
- slot id or token label
- user pin
- private key label
- certificate label
- User PIN reset via SO credentials:
In proxy mode, values come from proxy-backed token, not local SoftHSM token files.
```bash
softhsm2-util --init-pin --slot <slot-id> --so-pin <so-pin> --pin <new-user-pin>
```
### 7.9 Delete operations
Delete by label/id/type:
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> \
--delete-object --type cert --label sign-cert
```
Token-level destructive reset (do not use unless approved):
```bash
softhsm2-util --delete-token --token <token-label>
```
---
## 7.3 Linux handover checklist for IT (copy/paste friendly)
## 8) New company onboarding checklist
1. Confirm module path exists (`libsofthsm2.so` or proxy module).
2. Confirm token exists and slot id is known.
3. Confirm company private key label and certificate label.
4. Confirm user PIN for test environment.
5. Share these five values securely with development team.
For company-level signing model (one company key/cert pair):
1. Create token with company label (or assign dedicated token in remote HSM).
2. Generate key pair in token.
3. Obtain CA certificate and import into token.
4. Record secure runtime values:
- module path
- slot id or token label
- user pin
- private key label
- certificate label
5. Run crypt integration tests.
6. Store values in vault and wire app environment.
---
## 8) Test execution checklist (single command flow)
## 9) Rotation/update checklist
## 8.1 Set environment variables
1. Create new key/cert (`sign-key-v2`, `sign-cert-v2`).
2. Configure app env to new labels.
3. Run integration tests.
4. Deploy.
5. Keep old key/cert during grace period.
6. Remove old key/cert after legal/operational confirmation.
PowerShell example:
---
```powershell
$env:FES_TEST_PKCS11_LIBRARY_PATH = "<module-path>"
$env:FES_TEST_PKCS11_SLOT_ID = "<slot-id>"
$env:FES_TEST_PKCS11_USER_PIN = "<user-pin>"
$env:FES_TEST_PKCS11_PRIVATE_KEY_LABEL = "<private-key-label>"
$env:FES_TEST_PKCS11_CERTIFICATE_LABEL = "<certificate-label>"
```
## 10) Running tests
Linux (bash) example:
Before running, configure `EnvelopeGenerator.Tests/appsettings.json` -> `Crypt:Pkcs11` with real Linux SoftHSM values.
Run crypt tests:
```bash
export FES_TEST_PKCS11_LIBRARY_PATH="/usr/lib/softhsm/libsofthsm2.so"
export FES_TEST_PKCS11_SLOT_ID="0"
export FES_TEST_PKCS11_USER_PIN="<user-pin>"
export FES_TEST_PKCS11_PRIVATE_KEY_LABEL="sign-key"
export FES_TEST_PKCS11_CERTIFICATE_LABEL="sign-cert"
```
Verify env values quickly:
```bash
echo "$FES_TEST_PKCS11_LIBRARY_PATH"
echo "$FES_TEST_PKCS11_SLOT_ID"
echo "$FES_TEST_PKCS11_PRIVATE_KEY_LABEL"
echo "$FES_TEST_PKCS11_CERTIFICATE_LABEL"
```
## 8.2 Run only crypt tests
```powershell
dotnet test "EnvelopeGenerator.Tests/EnvelopeGenerator.Tests.csproj" -f net8.0 --filter "FullyQualifiedName~EnvelopeGenerator.Tests.Application.Crypt"
```
Expected:
---
- Unit tests pass always.
- Integration tests pass when env values are correct.
- Integration tests skip when env values are missing.
## 11) Common failures
If all five values are correct:
- integration tests run automatically,
- no code change is required,
- only environment setup is required.
- `CKR_PIN_INCORRECT`: wrong user pin for that token.
- `CRYPT_PROVIDER_UNAVAILABLE`: bad module path or native load issue.
- `CRYPT_SLOT_NOT_FOUND`: wrong slot id or token label.
- `CRYPT_LOGIN_FAILED`: login failed (pin/token mismatch).
- `CRYPT_KEY_NOT_FOUND`: key label/id mismatch.
- `CRYPT_CERT_NOT_FOUND`: certificate label/id mismatch.
---
## 9) Common failure meanings
## 12) Integration note
- `CRYPT_PROVIDER_UNAVAILABLE`
- invalid library path, missing native dependency, provider cannot load
- `CRYPT_SLOT_NOT_FOUND`
- wrong slot id or token label
- `CRYPT_LOGIN_FAILED`
- wrong PIN
- `CRYPT_KEY_NOT_FOUND`
- private key label/id mismatch
- `CRYPT_CERT_NOT_FOUND`
- certificate label/id mismatch
This is exactly what IT needs for first-level diagnostics.
Current implementation uses `CKM_SHA256_RSA_PKCS` while caller provides hash bytes. Validate mechanism semantics in your provider/HSM policy. If provider expects raw payload instead of digest for selected mechanism, signing flow must be adjusted.
---
## 10) Integration in the full stack architecture
## 13) Production-first onboarding (single-line checkpoints)
Current role by layer:
This section is the authoritative path for production-style setup (critical documents, FES).
- `Infrastructure.Crypt` (this project): PKCS#11 crypto operations
- `Application`: orchestration use-cases and business process steps
- `Server`: API endpoints and presentation concerns
### 13.1 Step 1 - Initialize dedicated company token
Recommended integration path:
One-line command:
1. Stabilize crypt tests and environment setup (this stage).
2. Integrate `ISignatureProvider` + `ICertificateProvider` into Application command flow.
3. Connect Application flow to Server endpoints.
4. Add PDF embedding and audit-trail generation for full step-8 compliance.
5. Add archiving/distribution orchestration for steps 9-10.
```bash
mkdir -p ./fes-prod && cd ./fes-prod && export SO_PIN="$(openssl rand -hex 16)" USER_PIN="$(openssl rand -hex 12)" && softhsm2-util --init-token --slot <uninitialized-slot> --label CompanyProdToken --so-pin "$SO_PIN" --pin "$USER_PIN" && softhsm2-util --show-slots
```
---
Checkpoint:
## 11) Important note about signing semantics
- Token appears as initialized with label `CompanyProdToken`.
- SoftHSM reassigns token to a new slot ID; use that slot ID in all next commands.
PKCS#11 mechanisms differ in whether they expect raw data or a precomputed digest.
Security note:
Current implementation signs with `CKM_SHA256_RSA_PKCS` and receives hash bytes from caller.
This must be validated in your real provider environment.
- Do not print/store PINs in shell history in real production operations.
- Persist secrets in a vault or protected secret store.
If provider expects raw data for this mechanism, integration may require adjustment.
This is why integration tests are critical.
### 13.2 Step 2 - Generate company signing key pair inside HSM
---
One-line command:
## 12) Quick FAQ
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <company-slot-id> --login --pin "$USER_PIN" --keypairgen --key-type rsa:3072 --label company-sign-key-v1 --id 01 && pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <company-slot-id> --login --pin "$USER_PIN" --list-objects
```
**Q: Do I need REST calls to SoftHSM?**
Checkpoint:
No. SoftHSM is not a REST server. Use PKCS#11 library calls.
- `Private Key Object` and `Public Key Object` exist for label `company-sign-key-v1`, id `01`.
- Private key remains non-extractable in token.
**Q: Can I use proxy and still call it SoftHSM setup?**
### 13.3 Step 3 - Obtain CA-issued certificate for HSM key
Yes. Proxy can forward to SoftHSM-backed token infrastructure.
Required policy:
**Q: Once I provide the 5 values, can tests run directly?**
- Certificate must be issued by enterprise CA for the key generated in token.
- Do not use self-signed cert for production signing identity.
Yes. Set env vars and run the single `dotnet test` command above.
Operational note:
**Q: Will one company cert/key be enough for this phase?**
- CSR generation method depends on installed PKCS#11 OpenSSL integration (`engine_pkcs11` / provider).
- After CA issues certificate, import certificate DER into token with matching id/label family.
Yes, for the diagram-aligned company-signing model, one company key/certificate is enough for initial implementation and validation.
OpenSSL 3 prerequisite check:
```bash
openssl version && openssl engine -t -c 2>/dev/null | grep -i pkcs11 || true
```
If no PKCS#11 engine/provider is listed, install integration packages first (distribution specific), then continue.
### 13.4 Step 4 - Import CA certificate into token
One-line command:
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <company-slot-id> --login --pin "$USER_PIN" --write-object ./company-sign-cert-v1.der --type cert --id 01 --label company-sign-cert-v1 && pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <company-slot-id> --login --pin "$USER_PIN" --list-objects
```
Checkpoint:
- `Certificate Object` exists with id `01` and expected label.
### 13.5 Step 5 - Wire runtime/test configuration
Server runtime (`EnvelopeGenerator.Server/EnvelopeGenerator.Server/appsettings.json`) must include:
- `Crypt:Pkcs11:LibraryPath` = native PKCS#11 library path for the server host OS
- Windows host: Windows DLL path (vendor DLL or proxy client DLL)
- Linux host: Linux `.so` path
- `Crypt:Pkcs11:SlotId` = `2021549410` (or current company slot)
- `Crypt:Pkcs11:TokenLabel` = `CompanyProdToken`
- `Crypt:Pkcs11:UserPin` = real user pin
- `Crypt:Pkcs11:PrivateKeyLabel` = `company-sign-key-v1`
- `Crypt:Pkcs11:CertificateLabel` = `company-sign-cert-v1`
Test runtime (`EnvelopeGenerator.Tests/appsettings.json`) must mirror the same `Crypt:Pkcs11` values.
Then run tests:
```bash
dotnet test "EnvelopeGenerator.Tests/EnvelopeGenerator.Tests.csproj" -f net8.0 --filter "FullyQualifiedName~EnvelopeGenerator.Tests.Application.Crypt"
```