Aller au contenu

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