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.
12 KiB
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-tooldocs: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:
- Direct module (
libsofthsm2.so,softhsm2-x64.dll, etc.) - PKCS#11 proxy module (remote HSM/SoftHSM backend)
Critical platform rule:
- A Windows process cannot load a Linux
.sodirectly. Crypt:Pkcs11:LibraryPathmust always point to a native library compatible with the host OS ofEnvelopeGenerator.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:
- Linux signing service (recommended)
- Windows
EnvelopeGenerator.Servercalls HTTPS signing API. - Linux service performs PKCS#11 operations against SoftHSM.
- Private keys stay on Linux.
- Windows
- 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.
- 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 (
1is recommended). - Document final runtime topology (hosts, ports, TLS, auth).
- Define where audit metadata (IP/email/phone/device) is stored and signed/linked.
- Update
SignatureController.Submitintegration 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:
ISignatureProviderICertificateProviderISignatureVerifierICryptographicHealthCheck
DI entry point:
CryptDependencyInjection.AddCryptInfrastructure(...)
5) Runtime configuration
Configuration section: Crypt:Pkcs11
{
"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:LibraryPathCrypt:Pkcs11:SlotId(orCrypt:Pkcs11:TokenLabel)Crypt:Pkcs11:UserPinCrypt:Pkcs11:PrivateKeyLabelCrypt: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
ldconfig -p | grep softhsm
find /usr -name "libsofthsm2.so" 2>/dev/null
7.2 List slots/tokens
softhsm2-util --show-slots
7.3 Create new token (recommended for new company/test tenant)
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-slotsand use the new slot id.
7.4 Login and inspect objects
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --slot <slot-id> --login --pin <user-pin> --list-objects
7.5 Generate company signing key pair
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
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)
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:
- Create/import new key/cert with new label or id.
- Update app config/env labels.
- Validate with integration tests.
- Delete old objects when safe.
PIN operations:
- User PIN change (while logged in):
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:
softhsm2-util --init-pin --slot <slot-id> --so-pin <so-pin> --pin <new-user-pin>
7.9 Delete operations
Delete by label/id/type:
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):
softhsm2-util --delete-token --token <token-label>
8) New company onboarding checklist
For company-level signing model (one company key/cert pair):
- Create token with company label (or assign dedicated token in remote HSM).
- Generate key pair in token.
- Obtain CA certificate and import into token.
- Record secure runtime values:
- module path
- slot id or token label
- user pin
- private key label
- certificate label
- Run crypt integration tests.
- Store values in vault and wire app environment.
9) Rotation/update checklist
- Create new key/cert (
sign-key-v2,sign-cert-v2). - Configure app env to new labels.
- Run integration tests.
- Deploy.
- Keep old key/cert during grace period.
- 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:
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:
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:
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 ObjectandPublic Key Objectexist for labelcompany-sign-key-v1, id01.- 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:
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:
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 Objectexists with id01and 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
.sopath
Crypt:Pkcs11:SlotId=2021549410(or current company slot)Crypt:Pkcs11:TokenLabel=CompanyProdTokenCrypt:Pkcs11:UserPin= real user pinCrypt:Pkcs11:PrivateKeyLabel=company-sign-key-v1Crypt:Pkcs11:CertificateLabel=company-sign-cert-v1
Test runtime (EnvelopeGenerator.Tests/appsettings.json) must mirror the same Crypt:Pkcs11 values.
Then run tests:
dotnet test "EnvelopeGenerator.Tests/EnvelopeGenerator.Tests.csproj" -f net8.0 --filter "FullyQualifiedName~EnvelopeGenerator.Tests.Application.Crypt"