Tutoriel : signature XML XAdES en Delphi

Une marche à suivre pratique qui prend une facture XML simple et produit un document XAdES-B-T — le niveau requis par FatturaPA, FACTUR-X et la plupart des portails de facturation électronique en Europe. Vous choisirez entre l'encapsulation enveloped, enveloping et detached, attacherez un horodatage RFC 3161, et vérifierez le résultat pour savoir exactement ce que votre contrepartie verra.

XAdES-B-B / B-T / B-LT / B-LTA
Enveloped / Enveloping / Detached
Delphi 7 – RAD Studio 13

Trois modes d'encapsulation, un moteur

XAdES étend la spécification W3C XML-DSig avec horodatage et données de validation long terme. Avant de signer quoi que ce soit, décidez où l'élément ds:Signature vivra.

Enveloped

L'élément ds:Signature est ajouté à l'intérieur de la racine du document. C'est le format utilisé par FatturaPA, FACTUR-X-XAdES et la plupart des flux de facturation électronique. Le XML signé reste une copie valide du document original — les lecteurs ignorent l'élément de signature qu'ils ne comprennent pas.

Enveloping

Le XML original devient un enfant ds:Object à l'intérieur du ds:Signature. Utile quand vous avez besoin d'une seule enveloppe autonome et que vous ne vous souciez pas que les consommateurs existants puissent continuer à parser le document.

Detached

La signature se trouve dans un fichier séparé et pointe vers le document original via une référence URI. Courant dans SAML, ebXML et tout flux où le document source doit rester octet pour octet intact.

Ajouter les unités

XAdES a besoin du signeur XML, du profil XAdES et d'un fournisseur de clés. Nous utilisons PFX dans ce tutoriel ; remplacez par n'importe lequel des 10 fournisseurs si vous avez une carte à puce ou un HSM cloud.

Clause uses Delphi

sgcSign_XML est le cœur de signature XML. sgcSign_Profile_XAdES expose la configuration du niveau, des transforms et de l'horodatage. sgcSign_KeyProvider_PFX charge un fichier PKCS#12 depuis le disque — le point de départ le plus courant.

Pour les niveaux long terme (B-LT, B-LTA) vous avez aussi besoin de sgcSign_OCSP et sgcSign_CRL pour que les données de révocation puissent être récupérées et embarquées.

uXAdESSigning.pas
uses
  Classes, SysUtils,
  // sgc
  sgcSign_KeyProvider_PFX,
  sgcSign_XML,
  sgcSign_Profile_XAdES,
  sgcSign_TSA,
  sgcSign_OCSP,
  sgcSign_CRL,
  sgcSign_Verifier;

Charger le XML et la clé

Deux entrées — le XML que vous voulez signer et le certificat PFX avec lequel vous signez.

Fournisseur de clés PFX

TsgcPFXKeyProvider importe le fichier PKCS#12 via Windows CNG, ce qui signifie que vous obtenez un handle de signature moderne quel que soit le CSP pour lequel le certificat a été initialement émis. Le même fournisseur fonctionne sur Windows 7 et ultérieur, 32-bit et 64-bit.

Gardez le fournisseur vivant pendant toute l'opération de signature — le signeur XML référence le handle de clé sous-jacent jusqu'au retour de SignXML.

step1-load.pas
var
  vXML: string;
  vKeyProvider: TsgcPFXKeyProvider;
begin
  vXML := TFile.ReadAllText('invoice.xml', TEncoding.UTF8);

  vKeyProvider := TsgcPFXKeyProvider.Create(nil);
  vKeyProvider.FileName := 'certificate.pfx';
  vKeyProvider.Password := 'secret';
  vKeyProvider.LoadFromFile;
end;

Choisir XAdES-B-T ou XAdES-B-LT

B-T ajoute un horodatage de signature. B-LT embarque en plus la chaîne CA et les données de révocation pour que la signature reste vérifiable pendant des années.

Configuration du profil

Level choisit le niveau AdES : xalB, xalT, xalLT, xalLTA. Packaging choisit xpkEnveloped (défaut), xpkEnveloping ou xpkDetached.

La liste Transforms par défaut est xtEnvelopedSignature + xtC14NExclusive, ce qui est ce que la plupart des schémas de facturation électronique imposent. Ne le surchargez que quand un profil national spécifique demande une C14N inclusive ou une transformation XSLT personnalisée.

Pour B-LT, OCSP.AutoFetch := True indique au signeur de récupérer les réponses OCSP pour chaque certificat de la chaîne et de les embarquer dans l'élément RevocationValues. Le XML signé porte alors tout ce dont un vérificateur a besoin — aucun appel réseau requis au moment de la validation.

step2-profile.pas
var
  vProfile: TsgcSignProfile_XAdES;
begin
  vProfile := TsgcSignProfile_XAdES.Create(nil);
  vProfile.Level := xalT;            // or xalLT, xalLTA
  vProfile.Packaging := xpkEnveloped;
  vProfile.HashAlgorithm := shaSHA256;

  // Timestamp authority for B-T and above
  vProfile.TSA.URL := 'https://freetsa.org/tsr';
  vProfile.TSA.HashAlgorithm := shaSHA256;

  // For B-LT: embed full chain and revocation data
  vProfile.OCSP.AutoFetch := True;
  vProfile.CRL.AutoFetch := True;
end;

Signer — enveloped, enveloping et detached

Le même composant TsgcSignXML gère les trois modes d'encapsulation — seule la propriété Packaging diffère.

Signature enveloped

Le défaut pour les factures. La signature est ajoutée à l'intérieur de la racine du document et utilise la transformation enveloped-signature pour s'exclure du digest.

Pour les payloads conscients du schéma (FatturaPA, TicketBAI, KSeF), définissez RootNamespace pour que la signature hérite du bon namespace XML — les profils nationaux définissent cela automatiquement.

step3-enveloped.pas
var
  vSigner: TsgcSignXML;
  vSigned: string;
begin
  vSigner := TsgcSignXML.Create(nil);
  try
    vSigner.KeyProvider := vKeyProvider;
    vSigner.Profile := vProfile;          // Packaging = xpkEnveloped
    vSigned := vSigner.SignXML(vXML);
    TFile.WriteAllText('invoice-signed.xml', vSigned, TEncoding.UTF8);
  finally
    vSigner.Free;
  end;
end;

Enveloping et detached

Basculez l'encapsulation sur le profil, gardez tout le reste identique. Pour les signatures detached, définissez DetachedURI au chemin ou à l'URL du document signé ; le vérificateur a besoin de cette référence pour récupérer les données.

Quand vous recevez une signature detached, passez à la fois le XML de signature et les octets du document original à VerifyDetached — sgcSign recalcule le digest et confirme le lien.

step3-other.pas
// Enveloping: original XML wrapped in a ds:Object
vProfile.Packaging := xpkEnveloping;
vSigned := vSigner.SignXML(vXML);

// Detached: signature file points at invoice.xml
vProfile.Packaging := xpkDetached;
vProfile.DetachedURI := 'invoice.xml';
vSigned := vSigner.SignXML(vXML);
TFile.WriteAllText('invoice.sig.xml', vSigned, TEncoding.UTF8);

Vérifier le XML signé

Chaque document signé doit être vérifié avant de livrer le workflow.

Un appel, rapport complet

VerifyXML retourne un TsgcSignatureReport avec le niveau AdES détecté, le sujet du signataire, l'horodatage de signature et tout problème de chaîne ou de révocation. Pour les signatures enveloped, le vérificateur localise l'élément ds:Signature automatiquement ; pour les signatures detached, utilisez VerifyDetached et fournissez les octets du document original.

Si Status est svValid, le digest correspond, la chaîne de certificats remonte à une racine de confiance et l'horodatage est intact. svInvalid avec la raison dans StatusDetail est le mode d'échec typique ; svUnknown signifie que le vérificateur n'a pas pu joindre un répondeur OCSP ou un point de distribution CRL.

step4-verify.pas
var
  vVerifier: TsgcSignatureVerifier;
  vReport: TsgcSignatureReport;
begin
  vVerifier := TsgcSignatureVerifier.Create(nil);
  try
    vReport := vVerifier.VerifyXML(vSigned);
    Memo1.Lines.Add('Level:   ' + vReport.Signatures[0].LevelAsString);
    Memo1.Lines.Add('Status:  ' + vReport.Signatures[0].StatusAsString);
    Memo1.Lines.Add('Signer:  ' + vReport.Signatures[0].Subject);
    Memo1.Lines.Add('TSA:     ' +
      DateTimeToStr(vReport.Signatures[0].TimestampUTC));
  finally
    vVerifier.Free;
  end;
end;

Ce qui se passe mal habituellement

Problèmes que nous voyons le plus souvent quand des développeurs signent leur première enveloppe XAdES.

Espaces blancs et canonicalisation

Pretty-printer un XML signé — dans un éditeur de texte, dans un IDE, ou en le passant par un XSLT — casse le digest. La C14N exclusive est sensible à chaque octet. Stockez le XML signé en octets, transportez-le en octets, ne le reformatez jamais.

BOM dans le document d'entrée

Un BOM UTF-8 en début de XML est une source fréquente de digests non concordants, en particulier quand le document est écrit par Notepad. Retirez le BOM avant de signer — sgcSign normalise en interne mais d'autres vérificateurs peuvent ne pas le faire.

FatturaPA nécessite xalLT

Le portail italien de facturation électronique rejette XAdES-B-T parce qu'il ne peut pas effectuer de recherches de révocation à long terme au moment de l'archivage. Utilisez xalLT avec OCSP.AutoFetch = True pour que la réponse OCSP voyage avec le document.

Signatures detached et résolution d'URI

Si DetachedURI est un chemin relatif, le vérificateur le résout par rapport à son propre répertoire de travail. Pour les flux entre machines, utilisez une URL absolue ou passez les octets du document directement à VerifyDetached.

Où aller à partir d'ici

Les profils nationaux font la configuration XAdES pour vous. Le serveur centralise la signature sur la ferme de build.

Profils nationaux

VeriFactu, FatturaPA, KSeF, FACTUR-X, TicketBAI — une constante bascule chaque transform, algorithme et attribut requis.

En savoir plus →

Tutoriel PAdES

Même moteur, appliqué au PDF au lieu du XML. Lisez la marche à suivre PAdES pour comparer les deux formats côte à côte.

En savoir plus →

Serveur sgcSign

API REST, intégration GitHub Actions, Docker et Helm. Déchargez la signature des développeurs individuels vers un service contrôlé.

En savoir plus →

Prêt à signer votre premier XML ?

Téléchargez l'essai, exécutez ce tutoriel contre votre propre facture, licenciez sgcSign quand vous livrez.