PD-36 — Implémenter un client HSM Cloud PKCS#11¶
📚 Navigation User Story
| Document | | | ---------- | -- | | 📋 **Spécification** | *(ce document)* | | 🛠️ [Plan d'implémentation](PD-36-plan.md) | | | ✅ [Critères d'acceptation](PD-36-acceptability.md) | | | 📝 [Retour d'expérience](PD-36-rex.md) | | [← Retour à crypto-proof](../PD-189-epic.md) · [↑ Index User Story](index.md)Références¶
- EPIC : PD-189 — CRYPTO
- JIRA : PD-36
- Repo(s) concernés : backend
Objectif¶
Mettre à disposition du backend ProbatioVault un client HSM Cloud compatible PKCS#11, permettant l'exécution d'opérations cryptographiques sensibles (signature, vérification, authentification mTLS) sans jamais exposer de clé privée hors du HSM.
Cette User Story constitue la brique d'accès sécurisé au HSM utilisée par l'ensemble des mécanismes probatoires et de confiance du système.
Description fonctionnelle¶
Le système doit être capable d'interagir avec un HSM Cloud via l'interface standard PKCS#11 afin de :
- utiliser des clés privées non exportables stockées dans le HSM ;
- signer des données via des algorithmes asymétriques standardisés ;
- établir des connexions sortantes sécurisées par authentification mTLS ;
- gérer de manière contrôlée les certificats X.509 associés aux clés.
Toutes les opérations cryptographiques critiques doivent être déléguées au HSM, le backend n'agissant que comme orchestrateur sans accès aux secrets.
Périmètre¶
Inclus¶
- Initialisation et gestion d'une connexion PKCS#11 vers un HSM Cloud.
- Ouverture et fermeture de sessions HSM sécurisées.
- Utilisation de clés asymétriques non exportables stockées dans le HSM.
- Génération ou import de paires de clés dédiées aux usages probatoires.
- Génération de requêtes de certificat (CSR) associées aux clés HSM.
- Association et utilisation de certificats X.509 pour l'authentification mTLS.
- Exposition d'une interface applicative pour :
- signature de données ;
- vérification de signatures ;
- authentification TLS côté client.
- Gestion fonctionnelle des erreurs HSM et des indisponibilités temporaires.
- Tests unitaires et d'intégration du client HSM.
- Documentation de configuration et d'usage.
Exclu¶
- Gestion d'un HSM on-premise.
- Génération ou gestion complète d'une autorité de certification.
- Opérations de chiffrement symétrique ou de déchiffrement.
- Gestion des enveloppes PRE ou d'autres mécanismes applicatifs.
- Rotation automatisée globale des clés HSM (traitée ailleurs).
Contraintes¶
- Sécurité
- Aucune clé privée ne doit être exportée hors du HSM.
- Les identifiants, PINs ou secrets HSM ne doivent jamais apparaître en clair.
- Les logs applicatifs ne doivent contenir aucune donnée sensible.
-
Les sessions HSM doivent être fermées proprement après usage.
-
Conformité cryptographique
- Utilisation exclusive de l'API PKCS#11 standard.
- Support des algorithmes asymétriques reconnus et robustes.
-
Compatibilité avec les exigences probatoires du projet.
-
Robustesse
- Gestion des erreurs PKCS#11 de manière contrôlée.
- Tolérance aux indisponibilités temporaires du HSM.
-
Aucune opération partielle ne doit être persistée en cas d'échec.
-
Compatibilité
- Compatibilité avec des HSM Cloud du marché supportant PKCS#11.
- Intégration avec l'environnement Node.js et TypeScript du backend.
Hypothèses¶
- Un HSM Cloud compatible PKCS#11 est disponible et correctement configuré.
- Les clés HSM nécessaires aux usages probatoires sont créées ou importées dans un slot dédié.
- Les certificats X.509 associés sont délivrés par une autorité de confiance.
- Les mécanismes de signature applicatifs consommeront ce client HSM comme dépendance unique.
- Les exigences de sécurité imposent une séparation stricte entre backend applicatif et secrets cryptographiques.
Architecture technique¶
┌─────────────────────────────────────────────────────────────────┐
│ Backend NestJS (Node.js 20) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ CryptoModule │ │
│ │ └─ HsmModule │ │
│ │ ├─ HsmService (Lifecycle NestJS) │ │
│ │ ├─ CloudHsmPkcs11Provider (PKCS#11 provider) │ │
│ │ └─ CloudHsmPkcs11Operations (pkcs11js operations) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ pkcs11js (Native Module) │ │
│ │ └─ libcloudhsm_pkcs11.so (AWS CloudHSM Client SDK) │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼ VPN Site-to-Site (OVH ↔ AWS)
┌─────────────────────────────────────────────────────────────────┐
│ AWS CloudHSM Cluster (eu-west-3) │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ HSM Instance │ │ HSM Instance │ │ HSM Instance │ │
│ │ (AZ-1) │ │ (AZ-2) │ │ (AZ-3) │ │
│ │ FIPS 140-2 │ │ FIPS 140-2 │ │ FIPS 140-2 │ │
│ │ Level 3 │ │ Level 3 │ │ Level 3 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Clés Stockées : │
│ - document-signing-key (ECDSA P-256) │
│ - audit-signing-key (ECDSA P-256) │
│ - management-key-1 (RSA-PSS 4096) │
└─────────────────────────────────────────────────────────────────┘
Configuration¶
Variables d'environnement¶
# HSM Configuration (OBLIGATOIRE)
CLOUDHSM_LIBRARY_PATH=/opt/cloudhsm/lib/libcloudhsm_pkcs11.so
CLOUDHSM_PIN=<crypto-user-pin>
CLOUDHSM_SLOT=0
CLOUDHSM_USER=crypto_user
CLOUDHSM_SESSION_TIMEOUT=300000
CLOUDHSM_MAX_SESSIONS=10
Exemples d'utilisation¶
Signature¶
const signature = await hsmService.sign(
documentHash,
'document-signing-key',
SignatureAlgorithm.ECDSA_SHA256,
);
const result = await hsmService.verify(
documentHash,
signature.signature,
'document-signing-key',
SignatureAlgorithm.ECDSA_SHA256,
);
Gestion Certificats X.509¶
// Créer un CSR dans le HSM
const csr = await hsmService.createCsr(
{ commonName: 'ProbatioVault', organization: 'ProbatioVault', country: 'FR' },
'document-signing-key',
);
// Importer un certificat signé
const certResult = await hsmService.storeCertificate(certBuffer, 'document-signing-key');
mTLS avec HSM¶
// Agent HTTPS avec clé HSM (clé privée ne quitte jamais le HSM)
const mtlsAgent = await hsmService.buildMtlsAgent({
keyLabel: 'mtls-client-key',
certChain: [clientCertBuffer],
endpoint: 'https://api.example.com',
});
5bis. Diagrammes¶
Diagramme d'états — Cycle de vie session PKCS#11¶
stateDiagram-v2
[*] --> LIBRARY_UNLOADED
LIBRARY_UNLOADED --> LIBRARY_LOADED : C_Initialize()
LIBRARY_LOADED --> LIBRARY_UNLOADED : C_Finalize()
LIBRARY_LOADED --> SESSION_OPENED : C_OpenSession(slot)
SESSION_OPENED --> LIBRARY_LOADED : C_CloseSession()
SESSION_OPENED --> SESSION_AUTHENTICATED : C_Login(CKU_USER, PIN)
SESSION_AUTHENTICATED --> SESSION_OPENED : C_Logout()
SESSION_AUTHENTICATED --> SESSION_AUTHENTICATED : C_SignInit / C_Sign\nC_VerifyInit / C_Verify\nC_FindObjectsInit / C_FindObjects\nC_CreateObject (cert)
SESSION_AUTHENTICATED --> SESSION_OPENED : C_Logout()
SESSION_OPENED --> LIBRARY_LOADED : C_CloseSession()
LIBRARY_LOADED --> LIBRARY_UNLOADED : C_Finalize() [onModuleDestroy]
LIBRARY_UNLOADED --> [*]
note right of SESSION_AUTHENTICATED
Toutes les opérations crypto
(sign, verify, CSR, cert import)
requièrent une session authentifiée.
La clé privée ne quitte JAMAIS le HSM.
end note
note right of LIBRARY_UNLOADED
État terminal atteint via
onModuleDestroy (NestJS lifecycle)
ou après erreur fatale non récupérable.
end note Diagramme d'états — Cycle de vie clé HSM¶
stateDiagram-v2
[*] --> KEY_ABSENT
KEY_ABSENT --> KEY_GENERATED : C_GenerateKeyPair()\n(ECDSA P-256 / RSA-PSS 4096)
KEY_ABSENT --> KEY_IMPORTED : Import paire de clés\n(CKA_EXTRACTABLE=false)
KEY_GENERATED --> KEY_ACTIVE : Label assigné\n(ex: document-signing-key)
KEY_IMPORTED --> KEY_ACTIVE : Label assigné
KEY_ACTIVE --> KEY_WITH_CSR : createCsr(subject, keyLabel)
KEY_WITH_CSR --> KEY_WITH_CERT : storeCertificate(cert, keyLabel)
KEY_ACTIVE --> KEY_ACTIVE : sign() / verify()
KEY_WITH_CERT --> KEY_WITH_CERT : sign() / verify() / buildMtlsAgent()
KEY_WITH_CERT --> KEY_ROTATED : Rotation (hors périmètre PD-36)\nPD-40 — HSM Rotation
KEY_ACTIVE --> KEY_ROTATED : Rotation (hors périmètre PD-36)
KEY_ROTATED --> [*]
note right of KEY_ACTIVE
CKA_EXTRACTABLE = false
CKA_SENSITIVE = true
La clé privée ne quitte
JAMAIS le HSM (INV sécurité).
end note
note left of KEY_WITH_CERT
mTLS possible uniquement
avec certificat X.509 associé.
end note Diagramme de séquence — Signature et vérification¶
sequenceDiagram
participant App as Backend NestJS
participant HSM as HsmService
participant Prov as CloudHsmPkcs11Provider
participant Ops as CloudHsmPkcs11Operations
participant HW as HSM Cloud (PKCS#11)
Note over App,HW: Initialisation (onModuleInit)
HSM->>Prov: initialize()
Prov->>HW: C_Initialize()
HW-->>Prov: CKR_OK
Prov->>HW: C_OpenSession(slot)
HW-->>Prov: sessionHandle
Prov->>HW: C_Login(CKU_USER, PIN)
HW-->>Prov: CKR_OK
Prov-->>HSM: session prête
Note over App,HW: Flux nominal — Signature
App->>HSM: sign(hash, 'document-signing-key', ECDSA_SHA256)
HSM->>Ops: findKey('document-signing-key')
Ops->>HW: C_FindObjectsInit(label filter)
HW-->>Ops: CKR_OK
Ops->>HW: C_FindObjects()
HW-->>Ops: keyHandle
Ops->>HW: C_FindObjectsFinal()
Ops-->>HSM: keyHandle
HSM->>Ops: signData(sessionHandle, keyHandle, hash, mechanism)
Ops->>HW: C_SignInit(mechanism, keyHandle)
HW-->>Ops: CKR_OK
Ops->>HW: C_Sign(hash)
HW-->>Ops: signature (DER)
Ops-->>HSM: signature
HSM-->>App: { signature, algorithm, keyLabel, timestamp }
Note over App,HW: Flux nominal — Vérification
App->>HSM: verify(hash, signature, 'document-signing-key', ECDSA_SHA256)
HSM->>Ops: findKey('document-signing-key')
Ops->>HW: C_FindObjectsInit / C_FindObjects / C_FindObjectsFinal
HW-->>Ops: keyHandle (public key)
HSM->>Ops: verifySignature(sessionHandle, keyHandle, hash, signature, mechanism)
Ops->>HW: C_VerifyInit(mechanism, keyHandle)
HW-->>Ops: CKR_OK
Ops->>HW: C_Verify(hash, signature)
HW-->>Ops: CKR_OK | CKR_SIGNATURE_INVALID
Ops-->>HSM: valid: boolean
HSM-->>App: { valid, algorithm, keyLabel }
Note over App,HW: Fermeture (onModuleDestroy)
HSM->>Prov: destroy()
Prov->>HW: C_Logout()
HW-->>Prov: CKR_OK
Prov->>HW: C_CloseSession(sessionHandle)
HW-->>Prov: CKR_OK
Prov->>HW: C_Finalize()
HW-->>Prov: CKR_OK Diagramme de séquence — Gestion certificats X.509 et mTLS¶
sequenceDiagram
participant App as Backend NestJS
participant HSM as HsmService
participant Ops as CloudHsmPkcs11Operations
participant HW as HSM Cloud (PKCS#11)
Note over App,HW: Création CSR (clé privée reste dans le HSM)
App->>HSM: createCsr(subject, 'document-signing-key')
HSM->>Ops: findKey('document-signing-key')
Ops->>HW: C_FindObjects(label)
HW-->>Ops: keyHandle
HSM->>Ops: generateCsr(keyHandle, subject)
Note right of Ops: CSR signé via C_Sign\navec la clé privée HSM
Ops->>HW: C_SignInit + C_Sign(csrTbsData)
HW-->>Ops: csrSignature
Ops-->>HSM: CSR (PEM)
HSM-->>App: { csr (PEM), keyLabel }
Note over App,HW: Import certificat signé par CA
App->>HSM: storeCertificate(certBuffer, 'document-signing-key')
HSM->>Ops: importCertificate(certBuffer, label)
Ops->>HW: C_CreateObject(CKO_CERTIFICATE, attrs)
HW-->>Ops: certHandle
Ops-->>HSM: certHandle
HSM-->>App: { stored: true, label }
Note over App,HW: Construction agent mTLS
App->>HSM: buildMtlsAgent({ keyLabel, certChain, endpoint })
HSM->>Ops: findKey('mtls-client-key')
Ops->>HW: C_FindObjects(label)
HW-->>Ops: keyHandle
Note right of HSM: L'agent HTTPS utilise\nle keyHandle pour TLS handshake.\nLa clé privée ne quitte\nJAMAIS le HSM.
HSM-->>App: https.Agent (mTLS prêt) Liens documentaires¶
- Architecture :
- Architecture Cryptographique — Accès HSM
- EPIC :
- PD-189 — CRYPTO
- Sécurité & conformité :
- PKCS#11 v2.x
- FIPS 140-2 Level 3
- AWS CloudHSM Documentation
- Bonnes pratiques HSM Cloud
- Spécifications ProbatioVault — Cryptographie