Files
EnvelopeGenerator/EnvelopeGenerator.Infrastructure.Crypt/README.md
TekH 3a71d57619 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.
2026-10-08 10:31:00 +02:00

390 lines
12 KiB
Markdown

# EnvelopeGenerator.Infrastructure.Crypt
PKCS#11-based cryptographic infrastructure for FES (Advanced Electronic Signature) workflows.
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) Scope
This project is the crypto layer only.
- Signs hashes via keys in HSM/SoftHSM.
- Reads certificates from HSM/SoftHSM.
- Verifies signatures.
- Exposes abstractions for Application/Server integration.
This project is not:
- a REST API,
- a UI module,
- a full document orchestration workflow.
---
## 2) SoftHSM and PKCS#11 basics
- 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.
Supported deployment patterns:
1. Direct module (`libsofthsm2.so`, `softhsm2-x64.dll`, etc.)
2. PKCS#11 proxy module (remote HSM/SoftHSM backend)
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`.
---
## 2.1) Windows Server + Linux SoftHSM architecture (decision TODO)
Current business requirement: `EnvelopeGenerator.Server` runs on Windows, SoftHSM runs on Linux.
This repository must use one of the following three paths:
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.
TODO - architecture lock-in:
- [ ] 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
---
## 4) Service surface
Main interfaces:
- `ISignatureProvider`
- `ICertificateProvider`
- `ISignatureVerifier`
- `ICryptographicHealthCheck`
DI entry point:
- `CryptDependencyInjection.AddCryptInfrastructure(...)`
---
## 5) Runtime configuration
Configuration section: `Crypt:Pkcs11`
```json
{
"Crypt": {
"Pkcs11": {
"LibraryPath": "/usr/lib/softhsm/libsofthsm2.so",
"SlotId": 1720207650,
"TokenLabel": "DevToken",
"UserPin": "***",
"PrivateKeyLabel": "sign-key",
"PrivateKeyIdHex": null,
"CertificateLabel": "sign-cert",
"CertificateIdHex": null
}
}
}
```
Security rules:
- 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) Test configuration source
Crypt tests read PKCS#11 values from `EnvelopeGenerator.Tests/appsettings.json` under `Crypt:Pkcs11`.
Required keys:
- `Crypt:Pkcs11:LibraryPath`
- `Crypt:Pkcs11:SlotId` (or `Crypt:Pkcs11:TokenLabel`)
- `Crypt:Pkcs11:UserPin`
- `Crypt:Pkcs11:PrivateKeyLabel`
- `Crypt:Pkcs11:CertificateLabel`
No environment variable is required for crypt tests in this repository anymore.
---
## 7) Full SoftHSM lifecycle runbook (project scope)
### 7.1 Discover module path
```bash
ldconfig -p | grep softhsm
find /usr -name "libsofthsm2.so" 2>/dev/null
```
### 7.2 List slots/tokens
```bash
softhsm2-util --show-slots
```
### 7.3 Create new token (recommended for new company/test tenant)
```bash
softhsm2-util --init-token --slot <uninitialized-slot> --label <token-label> --so-pin <so-pin> --pin <user-pin>
```
Important:
- After init, token is reassigned to a new slot id.
- Re-run `--show-slots` and use the new slot id.
### 7.4 Login and inspect objects
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> --list-objects
```
### 7.5 Generate company signing key pair
```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
```
### 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
```
### 7.7 Read object metadata (for diagnostics)
```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.8 Update operations
PKCS#11 objects are usually immutable for key/cert payload; updates are done by rotation:
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.
PIN operations:
- User PIN change (while logged in):
```bash
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <old-user-pin> --change-pin --new-pin <new-user-pin>
```
- User PIN reset via SO credentials:
```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>
```
---
## 8) New company onboarding checklist
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.
---
## 9) Rotation/update checklist
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.
---
## 10) Running tests
Before running, configure `EnvelopeGenerator.Tests/appsettings.json` -> `Crypt:Pkcs11` with real Linux SoftHSM values.
Run crypt tests:
```bash
dotnet test "EnvelopeGenerator.Tests/EnvelopeGenerator.Tests.csproj" -f net8.0 --filter "FullyQualifiedName~EnvelopeGenerator.Tests.Application.Crypt"
```
---
## 11) Common failures
- `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.
---
## 12) Integration note
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.
---
## 13) Production-first onboarding (single-line checkpoints)
This section is the authoritative path for production-style setup (critical documents, FES).
### 13.1 Step 1 - Initialize dedicated company token
One-line command:
```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:
- Token appears as initialized with label `CompanyProdToken`.
- SoftHSM reassigns token to a new slot ID; use that slot ID in all next commands.
Security note:
- Do not print/store PINs in shell history in real production operations.
- Persist secrets in a vault or protected secret store.
### 13.2 Step 2 - Generate company signing key pair inside HSM
One-line command:
```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
```
Checkpoint:
- `Private Key Object` and `Public Key Object` exist for label `company-sign-key-v1`, id `01`.
- Private key remains non-extractable in token.
### 13.3 Step 3 - Obtain CA-issued certificate for HSM key
Required policy:
- Certificate must be issued by enterprise CA for the key generated in token.
- Do not use self-signed cert for production signing identity.
Operational note:
- 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.
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"
```