# TODO - Conformité PAdES Complète (Niveau AES eIDAS) ## 🎯 Objectif Améliorer la conformité de la signature Odentas Sign pour atteindre le niveau **AES (Advanced Electronic Signature)** selon le règlement eIDAS et la norme ETSI TS 102 778 (PAdES). --- ## 🔴 Problèmes détectés par le validateur EU DSS ### 1. **FORMAT_FAILURE: PDF-NOT-ETSI au lieu de PAdES-BASELINE-B** **Statut actuel :** ❌ Non conforme **Impact :** La signature n'est pas reconnue comme PAdES standard **Priorité :** 🔥 CRITIQUE **Erreurs spécifiques :** - `The signed attribute: 'signing-certificate' is absent!` - `The /ByteRange dictionary is not consistent!` - `The reference data object is not intact!` --- ### 2. **Attribut signing-certificate-v2 manquant** **Description :** L'attribut `signing-certificate-v2` (OID: 1.2.840.113549.1.9.16.2.47) est **obligatoire** selon ETSI TS 102 778-3 pour PAdES-BASELINE-B. **Ce qu'il contient :** - Hash SHA-256 du certificat de signature - IssuerSerial du certificat **Modifications nécessaires :** #### Fichier : `lambda-odentas-pades-sign/helpers/pades.js` ```javascript // 1. Ajouter l'OID (DÉJÀ FAIT) const OID_ATTR_SIGNING_CERTIFICATE_V2 = '1.2.840.113549.1.9.16.2.47'; // 2. Modifier la signature de buildSignedAttributesDigest export function buildSignedAttributesDigest(pdfWithRevision, byteRange, signingTime, signerCertDer) { // ... code existant ... // Calculer le hash du certificat const certHash = crypto.createHash('sha256').update(signerCertDer).digest(); // Construire ESSCertIDv2 const essCertIDv2 = new asn1js.Sequence({ value: [ new asn1js.Sequence({ // hashAlgorithm value: [ new asn1js.ObjectIdentifier({ value: '2.16.840.1.101.3.4.2.1' }), // SHA-256 ] }), new asn1js.OctetString({ valueHex: certHash }), // certHash new asn1js.Sequence({ // issuerSerial value: [ // Extraire issuer et serial du certificat signerCert.issuer, signerCert.serialNumber ] }) ] }); // Construire SigningCertificateV2 const signingCertV2 = new asn1js.Sequence({ value: [ new asn1js.Sequence({ value: [essCertIDv2] }) ] }); const attrSigningCertV2 = new Attribute({ type: OID_ATTR_SIGNING_CERTIFICATE_V2, values: [signingCertV2] }); // Ajouter dans signedAttrs const signedAttrsForDigest = new asn1js.Set({ value: [ attrContentType.toSchema(), attrSigningTime.toSchema(), attrMessageDigest.toSchema(), attrSigningCertV2.toSchema() // ← NOUVEAU ] }); // ... return { signedAttrs: [attrContentType, attrSigningTime, attrMessageDigest, attrSigningCertV2], signedAttrsDigest, byteRange, pdfDigest }; } ``` #### Fichier : `lambda-odentas-pades-sign/index.js` ```javascript // Passer le certificat à buildSignedAttributesDigest const { signedAttrs, signedAttrsDigest, pdfDigest } = pades.buildSignedAttributesDigest( pdfWithRevision, byteRange, signingTime, chainPem // ← Passer la chaîne de certificats ); ``` **Tests à effectuer :** - [ ] Rebuild Lambda avec modifications - [ ] Tester génération de signature - [ ] Valider sur https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation - [ ] Vérifier que `signing-certificate-v2` apparaît dans les attributs signés --- ### 3. **ByteRange inconsistant** **Problème actuel :** ``` Document ByteRange : [0, 30578, 96114, 647] ``` Le dernier segment (647 bytes) semble trop court. Le ByteRange devrait couvrir **tout le document** sauf le placeholder de signature. **Formule correcte :** ``` ByteRange = [0, a, b, c] où: - a = position de /Contents < - b = position après le placeholder - c = taille du fichier - b ``` **Vérification à ajouter :** ```javascript // Dans finalizePdfWithCms() const fileSize = finalPdf.length; const expectedEnd = byteRange[2] + byteRange[3]; if (expectedEnd !== fileSize) { console.warn(`⚠️ ByteRange ne couvre pas tout le fichier!`); console.warn(` - Taille fichier: ${fileSize}`); console.warn(` - ByteRange end: ${expectedEnd}`); console.warn(` - Différence: ${fileSize - expectedEnd} bytes`); } ``` **Cause possible :** - Le placeholder de signature est trop grand (65536 bytes) - Des bytes supplémentaires sont ajoutés après la signature - Le calcul du ByteRange dans `preparePdfWithPlaceholder()` est incorrect **Tests à effectuer :** - [ ] Vérifier la taille du placeholder vs taille réelle du CMS - [ ] Ajuster la taille du placeholder si nécessaire - [ ] S'assurer que `byteRange[3]` va jusqu'à EOF --- ### 4. **SubFilter doit être /ETSI.CAdES.detached** **Statut actuel :** ✅ Déjà correct dans le code ```javascript /SubFilter /ETSI.CAdES.detached ``` Pas de modification nécessaire. --- ## 🟡 Améliorations optionnelles (pour AES complet) ### 5. **Ajouter l'attribut content-hints (optionnel)** Pour indiquer le type de contenu signé : ```javascript const OID_ATTR_CONTENT_HINTS = '1.2.840.113549.1.9.16.2.4'; const attrContentHints = new Attribute({ type: OID_ATTR_CONTENT_HINTS, values: [ new asn1js.Sequence({ value: [ new asn1js.UTF8String({ value: 'Contrat de travail' }), new asn1js.ObjectIdentifier({ value: OID_ID_DATA }) ] }) ] }); ``` --- ### 6. **Intégrer le timestamp TSA dans le CMS (pour PAdES-LT)** **Objectif :** Passer de PAdES-B à PAdES-LT (Long Term) **Modifications nécessaires :** 1. Obtenir le TSA **après** la signature 2. L'ajouter comme **unsignedAttrs** dans le SignerInfo ```javascript // Dans buildCmsSignedData(), après avoir créé signerInfo const tsaResponse = await fetch(process.env.TSA_URL, { method: 'POST', headers: { 'Content-Type': 'application/timestamp-query' }, body: createTSQ(signatureBytes) }); const tsr = await tsaResponse.arrayBuffer(); signerInfo.unsignedAttrs = new SignedAndUnsignedAttributes({ type: 1, // unsigned attributes: [ new Attribute({ type: '1.2.840.113549.1.9.16.2.14', // id-aa-signatureTimeStampToken values: [new asn1js.OctetString({ valueHex: Buffer.from(tsr) })] }) ] }); ``` **Impact :** - ✅ Résout le problème "L'heure de signature est déterminée à partir de l'horloge" - ✅ Conforme PAdES-LT (Long Term validation) - ✅ Plus robuste pour archivage long terme --- ## 📋 Plan d'action ### Phase 1 : Conformité PAdES-BASELINE-B (PRIORITÉ HAUTE) - [ ] Implémenter `signing-certificate-v2` - [ ] Corriger le ByteRange - [ ] Tester avec validateur EU DSS - [ ] Vérifier que format = "PAdES-BASELINE-B" **Temps estimé :** 4-6 heures **Complexité :** Moyenne --- ### Phase 2 : Intégration TSA dans CMS (PRIORITÉ MOYENNE) - [ ] Ajouter unsignedAttrs avec timestamp - [ ] Modifier workflow pour obtenir TSA après signature - [ ] Tester conformité PAdES-LT **Temps estimé :** 6-8 heures **Complexité :** Élevée --- ### Phase 3 : Certification (PRIORITÉ BASSE) - [ ] Décider si viser AES ou rester en SES - [ ] Évaluer coût certification ISO 27001 - [ ] Audit externe du système **Temps estimé :** Plusieurs mois **Coût estimé :** 10-20k€ --- ## 🧪 Tests de validation ### Validateur officiel EU DSS https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation **Critères de succès :** - [ ] Format = "PAdES-BASELINE-B" (au lieu de "PDF-NOT-ETSI") - [ ] Indication = "TOTAL_PASSED" (au lieu de "TOTAL_FAILED") - [ ] Aucune erreur "FORMAT_FAILURE" - [ ] Attribut `signing-certificate-v2` présent ### Outils complémentaires - [ ] Adobe Acrobat Reader (vérification visuelle) - [ ] pdfsig (Poppler) - validation technique - [ ] VeraPDF - conformité PDF/A --- ## 📚 Références ### Standards eIDAS - **Règlement eIDAS** : (UE) N°910/2014 - **ETSI TS 102 778** : PAdES (PDF Advanced Electronic Signatures) - **ETSI TS 119 172** : PAdES Baseline Profile - **RFC 5035** : ESS - Enhanced Security Services (ESSCertIDv2) ### Documentation technique - **PKI.js** : https://github.com/PeculiarVentures/PKI.js - **ASN.1 JavaScript** : https://github.com/PeculiarVentures/ASN1.js - **PDF Reference 1.7** : https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf ### Validateurs - **EU DSS Validator** : https://ec.europa.eu/digital-building-blocks/DSS/webapp-demo/validation - **ANSSI** : https://www.ssi.gouv.fr/ - **VeraPDF** : https://verapdf.org/ --- ## 💡 Notes importantes 1. **Ne pas casser l'existant** : Les signatures actuelles fonctionnent, gardons la compatibilité 2. **Tests progressifs** : Valider chaque modification avec le validateur EU 3. **Backup des certificats** : Les clés privées sont critiques, bien les sauvegarder 4. **Documentation** : Documenter chaque changement pour audit futur --- ## ✅ Statut actuel **Niveau eIDAS :** SES (Signature Électronique Simple) **Format signature :** PDF-NOT-ETSI (non conforme PAdES) **Validation EU DSS :** ❌ TOTAL_FAILED **Objectif :** PAdES-BASELINE-B conforme, validation ✅ TOTAL_PASSED --- _Dernière mise à jour : 28 octobre 2025_ _Créé par : GitHub Copilot_