DigiOHSDigiOHS
Trust Center

Securitatea semnăturilor electronice DigiOHS

Documentație tehnică și criptografică pentru semnătura electronică avansată (AES) implementată în DigiOHS, conformă cu Regulamentul UE 910/2014 (eIDAS) art. 26 și cu Legea 455/2001 din România.

Versiune document: 2026.04 · Operator: SC Moneta Digi SRL · CUI: 53243922 · Sediu: București · Contact: office@digiohs.com

1. Cadru legal & nivel AES

Toate fișele de instruire generate prin DigiOHS poartă o semnătură electronică avansată (AES) conform definiției din Regulamentul UE 910/2014 (eIDAS) art. 26. Concret, fiecare semnătură satisface simultan cele patru condiții:

  • Linkată unic la semnatar — certificatul X.509 are în câmpulsubject.serialNumber (RFC 5280) un identificator personal verificabil (telefon din dosarul de personal, validat prin SMS OTP).
  • Identifică semnatarulcommonNameconține numele complet, organizationName firma,organizationalUnitName conține CUI-ul.
  • Sub controlul exclusiv al semnatarului — cheia privată RSA se generează ephemeral în memoria serverului doar după ce utilizatorul introduce un cod OTP cu 6 cifre primit prin SMS pe telefonul lui. Cheia nu se stochează niciodată nici pe disc, nici în baza de date.
  • Detectează modificările ulterioare — semnătura PAdES Baseline B-B se calculează peste ByteRange (toate octeții PDF-ului mai puțin signature dictionary). Orice byte schimbat invalidează verificarea criptografică.
Concluzie: nivelul atins este AES (Advanced Electronic Signature) — calitate juridică egală cu o semnătură olografă în orice dispută civilă/de muncă din UE conform eIDAS art. 25(2). Pentru semnătura electronică calificată (QES) e necesar un certificat emis de Trust Service Provider acreditat — nu acoperim QES nativ, dar permitem integrarea cu un TSP la cerere (Adobe Sign, CertSign, DigiSign).

2. Ierarhie PKI

DigiOHS operează o autoritate de certificare internă cu trei niveluri, izolată de CA-uri publice (nu este cross-signed). Toate certificatele utilizator sunt emise de Intermediate CA, niciodată direct de Root CA.

        ┌──────────────────────────────────────┐
        │   DigiOHS Root CA                    │
        │   RSA 4096 · valabil 20 ani          │
        │   Self-signed · keyUsage=keyCertSign │
        │   Basic Constraints: CA=TRUE         │
        └──────────────────────────────────────┘
                          │ semnează
                          ▼
        ┌──────────────────────────────────────┐
        │   DigiOHS Issuing Intermediate CA    │
        │   RSA 4096 · valabil 5 ani           │
        │   keyUsage=keyCertSign + cRLSign     │
        │   pathLenConstraint=0                │
        └──────────────────────────────────────┘
                          │ semnează
                          ▼
        ┌──────────────────────────────────────┐
        │   User Certificate (per semnătură)   │
        │   RSA 2048 · valabil 48 ore          │
        │   keyUsage=digitalSignature +        │
        │     nonRepudiation                   │
        │   extKeyUsage=id-kp-documentSigning  │
        │     (OID 1.3.6.1.5.5.7.3.36)         │
        │   subject.serialNumber=<phone>       │
        └──────────────────────────────────────┘

Un PDF semnat conține întreaga chain (User cert + Intermediate + Root) într-o structură CMS SignedData conformă PKCS#7. La validare, orice viewer compatibil PAdES (Adobe Reader, Foxit, pdfsig) poate verifica chain-ul fără apel la rețea, atât timp cât are Root CA în trust store.

3. Algoritmi criptografici

ComponentăAlgoritmMod / parametri
Cheia Root CARSA4096 biți · e=65537 · semnare SHA-256-RSA
Cheia Intermediate CARSA4096 biți · semnare SHA-256-RSA
Cheia utilizator (per semnătură)RSA2048 biți · ephemeral
Hash semnăturăSHA-256FIPS 180-4 / RFC 6234
Format semnăturăPAdES B-BPKCS#7 detached · ETSI EN 319 142-1
Criptare cheia Intermediate la-restAES-256-GCMIV random 12 biți · authTag 128 biți · NIST SP 800-38D
PRNGCSPRNG OS/dev/urandom (Linux) via node:crypto
OTP6 cifrestocat ca SHA-256 hash · TTL 5 minute · max 5 încercări
Marcă temporalăRFC 3161 TSAFreeTSA / DigiCert TSA · best-effort

Roadmap criptografic: ECDSA P-256 ca alternativă la RSA-2048 (gain de performanță), RSA-PSS în loc de PKCS#1 v1.5 padding (rezistență la atacuri Bleichenbacher), Post-Quantum (CRYSTALS-Dilithium) când NIST publică standardele finale.

4. Emiterea certificatelor

Niciun certificat utilizator nu este pre-emis sau cached. Fiecare semnătură generează un certificat nou, valabil 48 ore (durata standard pentru a permite reluarea fluxului în caz de erori temporare ale rețelei).

Pași la emitere (server-side):

  1. Validare OTP (6 cifre) introdus de utilizator vs hash SHA-256 stocat.
  2. Generare keypair RSA-2048 în memoria procesului Node (forge.pki.rsa.generateKeyPair).
  3. Construire CSR cu subject DN populat din dosarul de personal (CommonName, organizationName, OID 2.5.4.5 = telefon E.164).
  4. Adăugare extensii: basicConstraints CA=false, keyUsage digitalSignature + nonRepudiation, extKeyUsage id-kp-documentSigning.
  5. Decriptare cheie privată Intermediate CA (AES-256-GCM cu master key) și semnare CSR.
  6. Persistare în digiohs_issued_certificates: serial, certPem (publicul), publicKeyHash, validity. Niciodată cheia privată.
  7. Returnare în RAM a certificatului + cheii private către funcția de semnare PAdES.
  8. După aplicarea semnăturii, cheia privată iese din scope-ul funcției → garbage-collected.
Important: Cheia privată a utilizatorului nu trăiește mai mult de câteva milisecunde. Nu există nicio cale arhitecturală — admin, dezvoltator, atacator cu DB dump — prin care să fie recuperată după ce semnătura a fost aplicată.

5. Fluxul complet de semnare

Salariat (telefon)        DigiOHS (server)              MariaDB / Storage
─────────────────────     ───────────────────           ──────────────────
1. WhatsApp link  ────────▶
                           Verifică token OTP-link
                           Returnează formular semnătură

2. Trasează semnătura ────▶
                           Stochează signature image
                                               ───────▶ digiohs_stored_signatures
                                                        (imagine PNG)

3. Cere OTP       ────────▶
                           Generează 6 cifre random
                           SHA-256(OTP) → DB
                                               ───────▶ digiohs_training_events
                                                        (otpHash, otpSentAt)
                           Trimite SMS Infobip ────▶ telefon E.164

4. Introduce OTP  ────────▶
                           SHA-256(input) == DB hash?
                           ↓ DA + max 5min trecute
                           Setează otpVerifiedAt
                           Setează completedAt
                                               ───────▶ digiohs_training_events

5. Cere PDF       ────────▶
                           Verifică completedAt set
                           ↓ DA
                           buildFisaPdf() → PDF nesem.
                           applyOrganizationWatermark()
                           ─────────────────────────
                           issueUserCertificate():
                           - Generează RSA-2048 (RAM)
                           - Decriptează Intermediate
                             CA cu CA_MASTER_KEY
                           - Semnează cert utilizator
                           - Persistă DOAR certPem
                                               ───────▶ digiohs_issued_certificates
                           ─────────────────────────
                           signPdfWithPades():
                           - normalizeToLegacyXref()
                           - plainAddPlaceholder()
                           - PKCS#7 detached signature
                             cu cheia ephemeral
                           ─────────────────────────
                           Cere TSA timestamp (RFC 3161)
                           Embedează timestampToken
                           ─────────────────────────
                           Cheia privată iese din scope
                           → GC distruge octeții

                           Returnează PDF semnat ◀──── browser

                           audit log:
                           PADES_SIGNATURE_APPLIED
                                               ───────▶ digiohs_audit_events

Întregul ciclu de la prezentarea OTP-ului la PDF semnat: tipic 800-1500ms. Cheia privată trăiește efectiv ~50ms în memoria procesului Node.

6. Protecția cheilor private

Cheia utilizator (per semnătură)

Strategia: ephemeral, never-stored. Generată în memoria procesului, folosită pentru o singură operație de semnătură, distrusă la ieșirea din scope. Nu există persistență, nu există recuperabilitate. Aceasta e o postură mai puternică decât modelul KEK (Key Encryption Key) folosit de DocuSign, fiindcă nu există nimic de decriptat.

Cheia Intermediate CA

Singura cheie privată persistentă din sistem. Stocare:

  • Tabela digiohs_certificate_authorities, câmp encryptedKeyPem.
  • Format: v1:<iv>:<authTag>:<ciphertext_b64>
  • Cifrare: AES-256-GCM. IV unic per cheie (12 octeți random). AuthTag 128 biți.
  • Master key: 32 octeți (256 biți) stocată în /etc/digiohs/ca-master.key cu permisiuni 0440 root:digiohs — owner root (revocare instantă), citibil doar de procesul de aplicație care rulează sub user-ul digiohs, niciun alt user de shell nu îl poate accesa. Nu apare în .env.production, nu se loghează în deploy scripts, nu e listat de printenv pe sistem.

Atacul ipotetic care recuperează intermediate CA ar necesita simultan: (a) acces root la serverul aplicației, (b) acces la DB dump. Cu ambele, atacatorul ar putea decriptaoricine Intermediate CA și emite el însuși certificate. Pentru a închide și această zonă, oferim un upgrade opțional cu HSM hardware (FIPS 140-2 Level 3) la cerere pentru clienți enterprise — cheia nu părăsește niciodată cipul, semnarea se face prin PKCS#11 API.

Root CA

Generată o singură dată la bootstrap, ținută offline sau pe HSM (după cerință). Folosită exclusiv pentru semnarea certificatelor Intermediate (operație rară, ~o dată la 5 ani). Nu e accesibilă proceselor aplicației în funcționarea normală.

7. Validarea semnăturii

O semnătură DigiOHS poate fi verificată în mai multe moduri:

a) Adobe Acrobat Reader (gratuit)

  1. Deschide PDF-ul semnat → tab Signatures (panou stânga).
  2. Click pe semnătură → Validity Status.
  3. La prima verificare: import Root CA în trust store (DigiOHS publică fingerprint-ul Root CA pe această pagină — vezi mai jos).
  4. Adobe verifică: integritatea bytes, lanțul de certificate, timestamp-ul TSA, dacă certificatele nu au fost revocate (CRL/OCSP).

b) Linie de comandă (pdfsig / openssl)

# pdfsig (poppler-utils)
pdfsig -nocert fisa.pdf

# extragere semnătură + verificare openssl
pdfsig -dump-sig fisa.pdf
openssl pkcs7 -inform DER -in signature.bin -print_certs

c-bis) Forensic monitoring continuu

Pe lângă semnătura propriu-zisă, un cron orar verifică integritatea audit log-ului:

  • Pentru fiecare TrainingEvent finalizat în ultimele 25h, verifică existența unui AuditEvent corespunzător cu acțiuneaTRAINING_OTP_VERIFIED. Dacă lipsește → posibil bypass prin SQL direct → alertă email la office@digiohs.com.
  • Pentru fiecare semnătură instructor/verificator atașată recent, verifică audit corespondent (TRAINING_BATCH_*_SIGNED sau PADES_SIGNATURE_APPLIED).
  • Recalculează SHA-256 hash chain pentru ultimele 5.000 evenimente audit. Orice divergență față de valoarea stocată (hashChain) detectează tampering pe tabela de audit însăși.

Toate anomaliile generează alertă email la office@digiohs.com + ping Slack. Cron-ul rulează cu lock distribuit (MariaDB GET_LOCK) → o singură execuție / oră, indiferent câte hosturi sunt active. Răspunde la întrebarea Q4 din checklist-ul de due diligence: chiar dacă un atacator cu acces SQL direct ar bypass OTP-ul, modificarea ar fi detectată în maxim 1 oră.

c) eIDAS proof bundle (pentru audit ITM)

DigiOHS oferă, prin endpoint-ul GET /api/training/eidas-proof?event=..., un PDF de probă care include: hash-ul fișei semnate, timestamp TSA, lanțul complet de certificate, log-ul de audit cu IP + user agent + timestamp OTP, plus referințele legale eIDAS art. 26 + Legea 455/2001.

d) DigiOHS Root CA — fingerprint public

Pentru a evita afișarea „semnătură de la emitent necunoscut" în Adobe Reader, importă Root CA-ul nostru în trust store-ul tău (Tools → Trust Manager → Trusted Certificates).

SHA-256 fingerprint Root CA: disponibil la cerere viaoffice@digiohs.com(publicăm fingerprint-ul exact după bootstrap-ul producției — momentan în staging).

8. Conformitate și standarde

  • Regulament UE 910/2014 (eIDAS) art. 26 — definiția AES. Toate cele patru condiții satisfăcute (vezi secțiunea 1).
  • Legea 455/2001 (RO) — privind semnătura electronică. Articolele 4 + 5 — semnătura electronică extinsă (echivalent AES) are aceeași valoare juridică ca semnătura olografă.
  • Codul Muncii (RO) art. 16 și HG 1425/2006 — cadrul pentru fișele individuale de instruire SSM/SU semnate digital.
  • ETSI EN 319 142-1 — formatul PAdES Baseline B-B implementat.
  • RFC 5280 — X.509 v3 certificate profile. Toate certificatele noastre respectă profilul.
  • RFC 3161 — Time-Stamp Protocol. Timestamp-uri din TSA externe (FreeTSA / DigiCert).
  • NIST SP 800-38D — modul GCM pentru AES-256.
  • FIPS 180-4 / RFC 6234 — SHA-256.
  • GDPR (UE 2016/679) — datele personale ale semnatarului (telefon, nume) sunt stocate exclusiv în certificat și DB-ul intern; nu sunt expuse public sau transmise către terți. Retenție: 5 ani conform legislației RO pentru documente SSM, apoi anonimizare.

9. Contact security & vulnerability disclosure

Pentru întrebări de due diligence, audit criptografic, sau raportare de vulnerabilități:

Răspundem la rapoarte de vulnerabilitate în maxim 5 zile lucrătoare. Pentru contracte enterprise oferim NDA-uri standard, audit acces la cod, și asistență la integrare HSM la cerere.