A new project, `EnvelopeGenerator.Infrastructure.Crypt`, has been added to the solution. This project targets `.NET 8.0` and includes several `Microsoft.Extensions` dependencies (e.g., `Configuration.Binder`, `DependencyInjection.Abstractions`, etc.) and the `Pkcs11Interop` package. The solution file (`EnvelopeGenerator.sln`) has been updated to include the new project, its build configurations, and its nesting under the same parent as `EnvelopeGenerator.Infrastructure.Doc`.
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"