Aller au contenu
getnextpdf.com

Enterprise édition

Niveaux de garantie eIDAS

NextPDF Enterprise transforme les preuves des listes de confiance de l’UE en un niveau de garantie (LoA) eIDAS explicite. Le service NextPDF\Enterprise\Security\Eidas\LoaMapping classe une entrée de service de confiance comme Low, Substantial ou High. Il renvoie une LoaAssertion qui porte le niveau ainsi que des codes de motif lisibles par machine. Ton flux de travail peut conditionner l’accès sur la garantie — « exiger High » — et archiver les motifs comme preuve d’audit. Une protection compagnon, CertPiiGuard, expurge les champs d’identité du signataire avant que les enregistrements d’audit ne quittent le processus.

Deux limites encadrent honnêtement cette capacité. Premièrement, la qualification appartient toujours au prestataire de services de confiance (TSP) sous la supervision de l’État membre. NextPDF affirme une classification sur des preuves publiées ; il n’accorde, ne confère ni ne certifie jamais de qualification. Deuxièmement, cette page ne couvre que l’assertion et l’association de LoA. La politique structurelle PAdES eidasQualified(), y compris ses critères de réussite/échec, est documentée dans Validation.

Cette capacité est livrée avec NextPDF Enterprise (nextpdf/enterprise) et s’active avec une enveloppe de licence de niveau Enterprise. Un déploiement sans ce droit ne charge pas les classes de la capacité. Comparer les éditions et obtenir une licence.

Fenêtre de terminal
composer require nextpdf/enterprise

Le métapaquet nextpdf/premium résout également le paquet Enterprise. L’activation utilise ton enveloppe de licence Enterprise ; voir Licence et activation. Les classes eIDAS ne nécessitent aucune extension PHP au-delà de la base du moteur. Elles s’autochargent sous NextPDF\Enterprise\Security\Eidas et NextPDF\Enterprise\Signature\Eidas.

Le règlement (UE) n° 910/2014 (eIDAS) définit trois niveaux de garantie : faible, substantiel et élevé (Article 8(1)). Chaque niveau exprime un degré de confiance dans une identité revendiquée. Le niveau élevé ajoute des contrôles dont le but est de prévenir — et pas seulement de réduire — l’usage abusif ou l’altération d’identité (Article 8(2)(c)). L’article 8 définit ces niveaux pour les schémas d’identification électronique. NextPDF réutilise le même vocabulaire pour classer les preuves de service de confiance qui sous-tendent un certificat de signature. Cette réutilisation est une convention d’ingénierie pour le conditionnement de politique et l’audit, non une équivalence juridique.

L’énumération LoaLevel modélise les trois niveaux. Ses valeurs sous-jacentes sont les URI de LoA eIDAS plutôt que de simples étiquettes, de sorte qu’une assertion persistée porte l’identifiant complet. rank() fournit un ordre total (Low = 1, Substantial = 2, High = 3), et meetsOrExceeds() compare à un seuil requis.

LoaMapping calcule un niveau à partir d’une entrée de liste de confiance — un TspService issu du sous-système de listes de confiance Enterprise (NextPDF\Enterprise\Security\Tsl). L’association est déterministe :

Preuve de liste de confianceNiveauCodes de motif
Le statut du service n’est pas grantedLowservice_not_granted
Le type de service n’est pas CA/QCLowservice_not_qualified_ca
CA/QC accordé avec QCWithQSCD et sans QCNoQSCDHighca_qc_with_qscd plus esig_or_eseal ou qc_default
CA/QC accordé autrementSubstantialca_qc_no_qscd_or_unspecified

Le qualificateur QSCD (dispositif qualifié de création de signature) est le pivot. Selon l’article 3(12), une signature électronique qualifiée requiert à la fois un certificat qualifié et un dispositif de création qualifié. Une déclaration de liste de confiance selon laquelle les certificats sont gérés sur un QSCD est donc la preuve qui étaye une assertion High. Sans cette déclaration, une AC qualifiée accordée n’étaye que Substantial, jamais High.

Le résultat est une LoaAssertion : le niveau plus une liste de codes de motif. Les motifs permettent à un consommateur d’audit de re-dériver ultérieurement la classification à partir des mêmes preuves. Les évaluateurs de politique en aval peuvent enregistrer l’assertion aux côtés d’un résultat de validation de signature.

Un élément de plus est livré dans ce module : CertPiiGuard. Lorsque les artefacts de validation sont sérialisés dans des lots d’audit JSON, le certificat du signataire porte des données personnelles — le CN du sujet, les attributs d’e-mail et l’attribut serialNumber, qui peut encoder un identifiant national pour les personnes physiques. L’article 5(1)(c) du RGPD exige que le traitement soit limité à ce qui est nécessaire. La protection expurge donc ces champs par défaut, en remplaçant les valeurs par [REDACTED] tout en préservant l’enveloppe structurelle (champs organisation, pays, chaîne et statut). Les consommateurs peuvent toujours vérifier si une signature a réussi sans apprendre qui a signé.

La décision porteuse est de séparer l’assertion de garantie du verdict de validation. La validation de signature, selon ETSI EN 319 102-1, se termine par une indication de statut — TOTAL-PASSED, TOTAL-FAILED ou INDETERMINATE — et ce verdict appartient à la couche de validation. L’association de LoA est une classification distincte et rejouable sur les preuves de liste de confiance, avec des codes de motif au lieu d’une simple étiquette. Cela empêche NextPDF de jamais présenter une revendication de garantie comme un résultat de validation, ou un résultat de validation comme un octroi de qualification. Cela rend aussi l’association conservatrice par construction : une preuve absente ou ambiguë abaisse le niveau, ne le relève jamais.

Contexte de conception : Les signatures qualifiées, expliquées.

Tous les symboles ci-dessous sont de l’API publique dans nextpdf/enterprise 3.1.0.

enum LoaLevel: string
{
case Low = 'http://eidas.europa.eu/LoA/low';
case Substantial = 'http://eidas.europa.eu/LoA/substantial';
case High = 'http://eidas.europa.eu/LoA/high';
public function rank(): int
public function meetsOrExceeds(self $required): bool
}

Lève ou échoue avec : rien de la part de rank() ou meetsOrExceeds(). La construction native d’énumération via LoaLevel::from() lève \ValueError sur un URI non reconnu ; LoaLevel::tryFrom() renvoie null à la place.

final class LoaMapping
{
public function loaForService(TspService $service): LoaAssertion
}

Lève ou échoue avec : rien. La méthode est totale — chaque entrée TspService produit une LoaAssertion.

Les DTO d’entrée NextPDF\Enterprise\Security\Tsl\TspService et NextPDF\Enterprise\Security\Tsl\TspServiceQualifier sont des DTO publics stables (@api). L’association consulte TspService::STATUS_GRANTED, TspService::TYPE_CA_QC, et les constantes de qualificateur TspServiceQualifier::QSCD_STATEMENT (QCWithQSCD), TspServiceQualifier::NO_QSCD (QCNoQSCD), TspServiceQualifier::FOR_ESIG et TspServiceQualifier::FOR_ESEAL.

final readonly class LoaAssertion
{
/**
* @param list<non-empty-string> $reasons Machine-readable reason codes for the assertion.
*/
public function __construct(
public LoaLevel $level,
public array $reasons,
) {}
}

Lève ou échoue avec : rien. Objet valeur immuable.

final readonly class CertPiiGuard
{
public function __construct(
private bool $disclosePii = false,
) {}
public function disclosesPii(): bool
public function guardSignerCommonName(string $signer): string
public function guardDistinguishedName(string $dn): string
public function guardTsaName(string $tsaName): string
public function guardRootIssuer(string $issuer): string
public function guardChainIssue(string $issue): string
}

Lève ou échoue avec : rien. Les protections sont des transformations de chaîne pures. Sur un composant de DN qui ne peut pas être tokenisé avec certitude, la protection échoue de manière fermée et réduit le composant à [REDACTED] plutôt que d’émettre une valeur partiellement masquée.

Analyser un URI de LoA et le comparer à un seuil requis.

loa-quick-start.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Eidas\LoaLevel;
// A LoA URI as persisted in an audit record or received from a peer system.
$uri = 'http://eidas.europa.eu/LoA/substantial';
try {
$level = LoaLevel::from($uri);
} catch (\ValueError $e) {
// Unknown URI: refuse to classify. Never guess an assurance level.
echo "Unrecognized LoA URI: {$uri}\n";
exit(1);
}
echo 'Level: ' . $level->name . ' (rank ' . $level->rank() . ")\n";
echo 'Meets substantial: ' . ($level->meetsOrExceeds(LoaLevel::Substantial) ? 'yes' : 'no') . "\n";
echo 'Meets high: ' . ($level->meetsOrExceeds(LoaLevel::High) ? 'yes' : 'no') . "\n";

Sortie attendue :

Level: Substantial (rank 2)
Meets substantial: yes
Meets high: no

Classer une entrée de liste de confiance, conditionner sur un niveau requis, et émettre un enregistrement d’audit expurgé.

loa-audit-gate.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Eidas\LoaLevel;
use NextPDF\Enterprise\Security\Eidas\LoaMapping;
use NextPDF\Enterprise\Security\Tsl\TspService;
use NextPDF\Enterprise\Security\Tsl\TspServiceQualifier;
use NextPDF\Enterprise\Signature\Eidas\CertPiiGuard;
// Normally produced by the Enterprise trusted-list subsystem from a
// member-state TSL; constructed inline here for a self-contained example.
$caPem = (string) file_get_contents(__DIR__ . '/example-qc-ca.pem');
$service = new TspService(
tspName: 'Example Qualified TSP',
serviceName: 'Example Qualified CA G2',
serviceTypeIdentifier: TspService::TYPE_CA_QC,
serviceStatus: TspService::STATUS_GRANTED,
statusStartingTime: '2024-01-01T00:00:00Z',
serviceCertificatePem: $caPem,
qualifiers: [
new TspServiceQualifier(qualifierUri: TspServiceQualifier::QSCD_STATEMENT),
new TspServiceQualifier(qualifierUri: TspServiceQualifier::FOR_ESIG),
],
additionalServiceInformation: [],
);
try {
// Required floor from deployment configuration; defaults to High.
$required = LoaLevel::from(getenv('LOA_REQUIRED') ?: LoaLevel::High->value);
} catch (\ValueError $e) {
echo "Invalid LOA_REQUIRED URI; refusing to continue.\n";
exit(1);
}
$mapping = new LoaMapping();
$assertion = $mapping->loaForService($service);
// Privacy by default: signer identity fields are redacted in audit output.
$guard = new CertPiiGuard();
$audit = [
'loa' => $assertion->level->value,
'reasons' => $assertion->reasons,
'meets_required' => $assertion->level->meetsOrExceeds($required),
'signer' => $guard->guardSignerCommonName('CN=Jane Example, O=Example Corp, C=DE'),
];
echo json_encode($audit, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";

Sortie attendue :

{
"loa": "http://eidas.europa.eu/LoA/high",
"reasons": [
"ca_qc_with_qscd",
"esig_or_eseal"
],
"meets_required": true,
"signer": "CN=[REDACTED], O=Example Corp, C=DE"
}
  • LoaLevel::from() lève \ValueError sur les URI inconnus. Utilise LoaLevel::tryFrom() là où la gestion de null est préférable.
  • Une preuve de dispositif contradictoire reste conservatrice. Un service portant à la fois QCWithQSCD et QCNoQSCD est associé à Substantial, pas à High.
  • Un service CA/QC accordé sans qualificateur est associé à Substantial avec le motif ca_qc_no_qscd_or_unspecified — qualifié par défaut, dispositif non prouvé.
  • Les URI de qualificateur en dehors de l’ensemble suivi n’affectent pas la classification. Les qualificateurs inconnus ou futurs ne relèvent jamais le niveau.
  • L’association lit uniquement le statut de service courant. Elle n’évalue pas l’historique de statusStartingTime ; les fenêtres temporelles ponctuelles appartiennent à la couche de validation.
  • Persiste l’URI sous-jacent de l’énumération, pas l’entier de rank(). Les rangs n’existent que pour la comparaison.
  • CertPiiGuard réduit entièrement à [REDACTED] un nom brut sans = ; les chaînes vides traversent inchangées toutes les protections.
  • Les DN hérités d’OpenSSL séparés par des barres obliques sont détectés et masqués de manière structurelle. Une / à l’intérieur d’une valeur RFC 4514 est traitée comme du contenu, non comme un séparateur.
  • Les attributs de DN non personnels (O, OU, C, ST, L) sont préservés, de sorte que le raisonnement de juridiction survit à l’expurgation.
  • Confidentialité par défaut. Le constructeur de la protection utilise par défaut disclosePii: false. Ne construis new CertPiiGuard(disclosePii: true) que là où tu disposes d’une base légale documentée pour traiter l’identité du signataire. Cela met en œuvre la minimisation des données de l’article 5(1)(c) du RGPD à la frontière de sérialisation.
  • Expurgation à sécurité intégrée. Lorsqu’un composant de DN ne peut pas être tokenisé avec certitude, le composant entier est réduit à [REDACTED]. Un contrôle de confidentialité n’échoue jamais de manière ouverte.
  • Sortie déterministe. Les protections utilisent un traitement de chaîne pur — pas d’horloge, pas d’aléa — de sorte que la sortie masquée est stable octet pour octet pour une entrée identique. Une sortie stable garde des diffs d’audit significatifs.
  • L’expurgation n’est pas du chiffrement. [REDACTED] retire la valeur de l’enregistrement. Si tu as besoin de récupérer l’identité, stocke-la séparément sous sa propre base légale et son propre contrôle d’accès.
  • Ordures en entrée, ordures en sortie. Une LoaAssertion n’est fiable que dans la mesure où les preuves de liste de confiance qui la sous-tendent le sont. Acquiers et vérifie la signature des listes de confiance via le sous-système de listes de confiance Enterprise avant d’alimenter des entrées dans l’association.

NextPDF Enterprise met en œuvre un comportement inspiré du règlement (UE) n° 910/2014 Article 8 (niveaux de garantie) et Article 3(12) (éléments d’une signature électronique qualifiée), ainsi que du vocabulaire de qualificateurs de liste de confiance ETSI. La prise en charge n’est pas la conformité, et la conformité n’est pas la certification. NextPDF ne détient aucune certification et n’en accorde aucune. NextPDF n’est pas un prestataire de services de confiance qualifié, ni un organisme d’évaluation de la conformité, ni un organe de contrôle. Une LoaAssertion est une classification logicielle de preuves publiées. Ce n’est pas une détermination juridique de qualification ni de garantie, et elle ne peut pas rendre une signature qualifiée.

Le règlement (UE) 2024/1183 (eIDAS 2) continue de référencer les niveaux de l’article 8 et exige que les portefeuilles européens d’identité numérique soient fournis au niveau de garantie élevé. Cette page cite cela comme contexte réglementaire ; NextPDF ne formule aucune revendication de capacité liée aux portefeuilles.

La question de savoir si une signature donnée satisfait une politique structurelle orientée eIDAS est une question distincte, à laquelle répond le module de validation ; voir Validation.

Les classes eIDAS LoA n’effectuent aucune opération cryptographique — pas de hachage, pas de vérification de signature, pas d’aléa. La politique de mode FIPS Enterprise conditionne les choix cryptographiques, elle n’a donc rien à conditionner dans ce module. Activer le mode FIPS ne change pas l’association de LoA ni le comportement de la protection des données personnelles. La vérification cryptographique des signatures et des listes de confiance est régie par les modules de vérification et de sécurité, où s’applique la politique de mode FIPS.

  • LoaMapping::loaForService() est totale et déterministe. Chaque TspService produit une LoaAssertion ; la méthode ne lève jamais et ne consulte aucune horloge, aucun réseau, ni aucun état global.
  • La classification est conservatrice. Une preuve manquante, inconnue ou contradictoire abaisse le niveau ; rien ne le relève sauf une preuve explicite CA/QC accordé avec QSCD.
  • Les codes de motif sont lisibles par machine et stables : service_not_granted, service_not_qualified_ca, ca_qc_with_qscd, esig_or_eseal, qc_default, ca_qc_no_qscd_or_unspecified.
  • L’ordre des niveaux est fixe : Low < Substantial < High, exposé via rank() et meetsOrExceeds().
  • CertPiiGuard utilise l’expurgation par défaut et échoue de manière fermée en cas de doute sur la tokenisation. Avec disclosePii: true, chaque protection renvoie son entrée telle quelle.
  • La sortie de la protection est stable octet pour octet pour une entrée identique.

NextPDF Core vérifie les signatures PDF de manière cryptographique et échoue de manière fermée sur des preuves corrompues. Core n’a pas de modèle de liste de confiance de l’UE, pas de vocabulaire LoaLevel, pas d’association de LoA, et pas de protection des données personnelles de couche eIDAS pour la sérialisation d’audit. Sur Core seul, tu dois dériver toi-même les classifications de garantie à partir des données de confiance que tu maintiens, et appliquer ta propre expurgation avant que les enregistrements d’audit ne quittent le processus.

Cette page documente uniquement le comportement observable de l’extérieur et la surface d’API publique prise en charge. Les chemins d’espaces de noms internes, les classes utilitaires, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de tickets sont hors périmètre.