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.
390 lines
12 KiB
Markdown
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"
|
|
```
|