1. Introduction

La signature électronique est maintenant un outil de tous les jours, qu'elle soit utilisée avec les mails, les documents PDF mais aussi les applications métier dans le cadre d'échanges électroniques spécifiques.

Au cours de ce tutoriel nous mettrons en pratique la signature électronique sous la forme XMLDSig qui est un standard du W3C. On parle aussi de signature XML. Afin de boucler la boucle, nous verrons également comment vérifier nos signatures.

2. La signature électronique

2.1. Pourquoi signer électroniquement ?

La signature électronique permet d'accomplir plusieurs choses.

  • Vérifier l'intégrité des données signées
  • Identifier et garantir l'identité du signataire
  • Assurer la non-répudiation

2.2. Comment ça marche ?

Avant de nous attaquer au code, commençons par un peu de théorie. La signature électronique repose sur l'utilisation de deux familles d'algorithmes.

2.2.1. Algorithmes de hachage

L'algorithme de hachage calcule une empreinte (hash, digest) à partir de données fournies en entrée. Si deux empreintes sont identiques c'est qu'elles ont été calculées d'après les mêmes données, tout du moins dans la théorie. En pratique il est possible d'avoir une même empreinte pour des données différentes. On parle alors de collision. Néanmoins, les algorithmes utilisés en cryptographie ont très peu de chance de générer des collisions.


Parmi ces algorithmes on peut citer le très connu MD5, mais aussi la famille SHA (Secure Hash Algorithm) avec par exemple SHA-1, SHA-256 et SHA-512.


Dans le cadre de la signature cet algorithme est utilisé pour créer une empreinte des informations à signer.

2.2.2. Algorithmes de chiffrement

L'algorithme de chiffrement permet de protéger des données en les rendant illisibles si on ne possède pas la clé pour les déchiffrer. Il existe 3 types d'algorithmes de chiffrement :

  • Les algorithmes symétriques (comme AES)
    • Rapides
    • Basés sur une clé unique, ce qui impose une transmission sécurisée de la clé
  • Les algorithmes asymétriques (comme RSA)
    • Lents
    • Basés sur un système clé publique / clé privée
    • Permettent la signature
  • Les algorithmes hybrides
    • Il ne s'agit pas d'algorithmes en tant que tels. Le principe c'est d'utiliser un algorithme symétrique et un algorithme asymétrique dans le même processus (d'ou le terme hybride) en ne prenant que le meilleur de chacun. L'idée générale c'est de générer une clé pour l'algorithme symétrique. On chiffre le message avec cette clé (avantage = rapidité). Ensuite on chiffre la clé avec un algorithme asymétrique (avantage = transmission sécurisée de la clé par le biais d'un système clé publique/clé privée). Côté destinataire il suffit de déchiffrer la clé avec la clé publique pour déchiffrer ensuite le message


Dans le cadre de la signature, cet algorithme est utilisé d'une part pour chiffrer l'empreinte des informations à signer et d'autre part pour déchiffrer l'empreinte signée lors de la vérification de la signature.

2.2.3. Schéma de principe

Maintenant que nous avons vu les algorithmes mis en jeux dans la signature électronique, voyons comment l'ensemble s'emboite.

image
Copyright Lex Persona - Reproduction autorisée par l'auteur
  1. Calcul de l'empreinte des données à signer.
  2. Chiffrement de l'empreinte à l'aide de la clé privée. On obtient alors la signature.
  3. Déchiffrement de la signature avec la clé publique. Cela permet de retrouver l'empreinte associée aux données signées.
  4. Calcul de l'empreinte des données signées. On vérifie que cette empreinte correspond à la précédente, auquel cas la signature est valide : les données sont donc intègres et l'identité de l'expéditeur est vérifiée.

2.3. Clés privées, clés publiques ? Késako ?!

Pour pouvoir effectuer une signature il nous faut donc une clé privée et une clé publique. Mais qu'est-ce donc me direz-vous ? Une clé est tout simplement un paramètre d'entrée pour une fonction cryptographique (chiffrement, déchiffrement). Les clés sont soit symétriques, la même clé sert à chiffrer et à déchiffrer, soit asymétriques et on a alors une clé pour chiffrer (clé privée) et une clé pour déchiffrer (clé publique). En fonction du type de clé on utilisera l'algorithme de chiffrement adapté.


Ceci étant posé, vous vous demandez certainement où trouver de telles clés. Cela passe généralement par l'utilisation d'un certificat électronique. Il faut voir le certificat comme une pièce d'identité numérique afin d'identifier une personne mais aussi une machine (certificat SSL sur un serveur par exemple). Un certificat est délivré par une autorité de certification (AC ou CA). Une AC est un organisme chargé de gérer les certificats ainsi que les listes de révocation (liste des identifiants des certificats révoqués ou qui ne sont plus valables).


Suivant l'importance de la signature, différents certificats peuvent être nécessaires. Par exemple si la signature électronique est l'équivalent d'une signature papier, on utilise en général des certificats de classe 3+ (certificat sur support - USB ou carte à puce - avec code PIN). La saisie du code PIN représente à ce moment l'action de signer, comme on signerait un document avec son stylo.


Il existe également différents formats de certificats. Dans notre cas nous travaillerons avec des certificats au format X.509, qui est le principal format utilisé pour les certificats.

2.4. Les certificats

2.4.1. Installer un certificat

Un certificat se présente sous la forme d'un simple fichier. On reconnaît en général un fichier certificat à son extension (.cer, .crt, .pfx, .p12, ...) ainsi qu'à l'icône associé à l'extension par Windows. Pour installer un certificat, il suffit de double cliquer sur le fichier afin de lancer l'assistant. Une fois installé le certificat se retrouve dans le magasin de certificats.

Les certificats peuvent être installés soit sur la machine locale soit sur le compte utilisateur. De plus le magasin de certificats possède plusieurs dossiers (Personnel, Autorités de certification racines de confiance, ...). L'assistant sélectionne le dossier par défaut le plus adapté, mais il est possible de spécifier un autre dossier. A noter également que l'installation d'un certificat peut nécessiter la saisie d'un mot de passe ou d'un code PIN.

2.4.2. Accéder aux certificats sous Windows

On peut accéder à la liste des certificats de deux manières : en passant par Internet Explorer ou bien par la console MMC.

2.4.2.1. Internet Explorer

Allez dans le menu Outils puis sélectionnez Options Internet. Sélectionnez ensuite l'onglet Contenu et cliquez sur le bouton Certificats.

image


Vous aurez ainsi accès à la liste des certificats, par dossier.

image

2.4.2.2. Console MMC

La console MMC offre un composant pour l'accès au certificat. Pour ouvrir la console tapez mmc dans la fenêtre Exécuter. du menu Démarrer de Windows. Vous devriez alors voir la fenêtre suivante s'afficher.

image


Allez dans le menu Fichier > Ajouter / Supprimer un composant logiciel enfichable. ou bien utilisez le raccourci clavier CTRL+M. Dans la nouvelle fenêtre qui vient de s'ouvrir, cliquez sur le bouton Ajouter qui se trouve en bas à gauche.

L'étape précédente peut varier suivant la version de Windows installée. Sous Windows VISTA, par exemple, vous arriverez immédiatement à l'étape suivante après avoir sélectionné le menu Ajouter / Supprimer un composant logiciel enfichable.


Une nouvelle fenêtre, avec la liste des composants, apparaît. Sélectionnez le composant Certificats et cliquez sur Ajouter. Indiquez que vous voulez utiliser le compte utilisateur puis cliquez sur Terminer. De retour sur la liste des composants cliquez sur Fermer, puis sur OK afin de revenir à la console.


Vous devriez maintenant avoir accès à la liste des certificats de l'utilisateur actuel, classés par dossier.

image

2.4.3. Les informations d'un certificat

Afin d'avoir plus de détails sur un certificat il suffit de double cliquer dessus (sous Internet Explorer ou depuis la console MMC) afin d'afficher la fenêtre donnant accès aux informations du certificat.


Si vous allez sur l'onglet Détails de la fenêtre vous verrez alors une liste avec les champs du certificat, comme l'image ci-dessous.

image


On constate ainsi que le certificat est au format X.509 v3 (champ version) et que ce certificat a été signé avec un algorithme sha1RSA par l'autorité de certification.


L'émetteur indique l'AC ayant délivrée le certificat et les dates de validités définissent la période pendant laquelle le certificat sera valide (l'AC garantit les informations du certificat pendant cette période). L'objet représente le destinataire du certificat. L'émetteur et l'objet sont renseignés sous la forme d'un Distinguished Name (DN).


Pour finir on retrouve les informations sur la clé publique : algorithme à utiliser (RSA), taille de la clé (1024 bits) ainsi que l'utilisation que l'on peut faire de la clé (Signature numérique, Non-répudiation).

2.5. La signature XML

Maintenant que vous êtes presque incollables sur le fonctionnement de la signature électronique, voyons à quoi ressemble une signature XML.

Bloc de signature XML
Sélectionnez
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
  <SignedInfo>
     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
     <Reference URI="">
        <Transforms>
           <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>xPnaYRIxXLjvsGUFjX3HtnZgndA=</DigestValue>
     </Reference>
  </SignedInfo>
  <SignatureValue>h20gBNYsljCFWGJwSkIbBYEG6a+fnl...</SignatureValue>
  <KeyInfo>
     <X509Data>
          <X509Certificate>MIIB8DCCAV2gAwIBAgIQV6ptLvla...</X509Certificate>
     </X509Data>
  </KeyInfo>
</Signature>


Une signature XML commence toujours par un élément Signature. Cet élément possède un namespace particulier, indiquant un bloc de signature XML.

L'élément SignedInfo contient l'algorithme de canonicalisation qui sera appliqué à l'élément SignedInfo avant le calcul de la signature, l'algorithme de chiffrement utilisé pour le calcul de la signature ainsi que les données à signer (éléments Reference).

L'élément Reference contient l'algorithme de hachage et l'empreinte calculée avec cet algorithme. L'URI référence l'identifiant des données à signer. L'URI et la transformation (élément Transform) indiquent comment les données à hacher ont été obtenues.

L'élément SignatureValue contient la valeur de la signature.

Pour finir l'élément KeyInfo contient les informations sur le certificat utilisé pour signer, ce qui permet de déchiffrer la signature et de la vérifier. Dans l'exemple les informations sont sous la forme d'éléments X509Data, mais ce n'est pas la seule représentation possible.

Algorithme de canonicalisation
Un bloc XML peut avoir plusieurs représentations. Par exemple, les espaces ne sont pas significatifs s'ils sont présents dans un élément. <MonElement> et <MonElement  > sont identiques du point de vue logique. Néanmoins l'empreinte générée sera différente dans les deux cas. L'algorithme de canonicalisation va permettre que des XML identiques au niveau logique produisent la même empreinte.

Il existe 3 façons d'utiliser la signature XML.

  • La signature enveloppée : l'élément signé contient la signature
  • La signature enveloppante : la signature contient l'élément signé
  • La signature détachée : la signature et la ressource à signer sont indépendantes


De par son principe, la signature enveloppée est uniquement utilisable lorsque l'on veut signer un document XML.

3. Mise en pratique

Les espaces de noms utilisés pour la signature sont les suivants :


Il faut également référencer l'assembly System.Security dans votre projet.

3.1. Les certificats

Avant de signer il nous faut un certificat. Voyons comment accéder à nos certificats avec .Net.

Première chose, il nous faut un accès au magasin de certificats. Pour cela on utilise la classe X509Store. Comme nous l'avons vu un peu plus tôt, les certificats peuvent être installés par compte utilisateur ou sur la machine locale et le magasin de certificats dispose de plusieurs dossiers.
La classe X509Store nous fourni des constructeurs adaptés à ces cas de figures. Ils permettent ainsi d'accéder, d'une part au magasin de l'utilisateur en cours ou de la machine locale (énumération StoreLocation) et d'autre part à un dossier du magasin (énumération StoreName).

Pour accéder au magasin de l'utilisateur courant il suffit donc de faire.

 
Sélectionnez
X509Store store = new X509Store(StoreLocation.CurrentUser);


Pour accéder au dossier personnel du magasin de l'utilisateur courant on utilisera.

 
Sélectionnez
X509Store store = new X509Store(StoreName.My);


Il est possible de combiner les deux. Ainsi, si l'on souhaite accéder au dossier personnel du magasin de la machine locale.

 
Sélectionnez
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);


Maintenant que nous avons accès à notre magasin, il faut l'ouvrir en indiquant la façon de l'ouvrir avec l'énumération OpenFlags.

 
Sélectionnez
store.Open(OpenFlags.ReadOnly);


Une fois ouvert, on va pouvoir accéder à la liste des certificats via la propriété Certificates.

 
Sélectionnez
foreach (X509Certificate2 cert in store.Certificates)
{
    Console.WriteLine(string.Format("Délivré à {0} par {1}", 
	cert.SubjectName.Name, cert.IssuerName.Name));
}


Pour finir, on ferme le magasin de certificats.

 
Sélectionnez
store.Close();


L'exécution de ce code devrait produire un affichage similaire au suivant

image


On retrouve bien les informations de notre certificat, à savoir le destinataire (SubjectName) et l'émetteur (IssuerName) sous la forme d'un DN comme indiqué précédemment.


Maintenant que nous savons comment lister les certificats, il faut pouvoir en sélectionner un via une interface. Le Framework a tout prévu pour ça avec la classe X509Certificate2UI

 
Sélectionnez
// Création d'un magasin de certificat utilisant le compte utilisateur.
X509Store store = new X509Store(StoreLocation.CurrentUser);

// Ouverture du magasin de certificat.
store.Open(OpenFlags.ReadOnly);

// Sélection d'un certificat
X509CertificateCollection certificates = 
    X509Certificate2UI.SelectFromCollection
    (
    store.Certificates, 
    "Liste des certificats", 
    "Veuillez sélectionner un certificat", 
    X509SelectionFlag.SingleSelection
    );

// Fermeture du magasin de certificat.
store.Close();

// Récupération du certificat sélectionné.
X509Certificate2 certificate = null;
if (certificates.Count != 0)
{
    certificate = (X509Certificate2)certificates[0];
}


Félicitation ! Vous avez maintenant sélectionné un certificat. Nous avons donc toutes les billes pour nous attaquer à la signature XML.

3.2. La signature XML

Vous pourrez trouver en annexe des exemples de fichiers XML obtenus après signature suivant les différentes méthodes que nous aborderons.

3.2.1. La classe SignedXml

La classe SignedXml fournit un wrapper afin de faciliter la création de signature XML.

3.2.1.1. Méthodes importantes

Méthode Commentaire
AddObject Ajoute un élément Object à la signature. Cet élément contient des données qui peuvent être signées. On peut ajouter plusieurs éléments Object
AddReference Ajoute un élément Reference à la signature. Cela permet d'indiquer quelles sont les données à signer et comment générer la signature de ces données
CheckSignature Vérifie une signature électronique
ComputeSignature Calcule la signature électronique
GetXml Obtient le XML représentant la signature. A appeler après ComputeSignature
LoadXml Charge une signature électronique afin de pouvoir la vérifier

3.2.1.2. Propriétés importantes

Propriété Commentaire
KeyInfo Cette propriété permet d'obtenir ou de définir des informations de clé (élément KeyInfo du bloc de signature)
SignedInfo Représente l'élément SignedInfo du bloc de signature
SigningKey Cette propriété permet d'obtenir ou de définir la clé utilisée pour la signature

3.2.1.3. Champs statiques

La classe SignedXml fournit plusieurs champs statiques avec les URI des différents algorithmes utilisables. A utiliser si l'on souhaite modifier les algorithmes de signature ou de hachage à utiliser (propriétés SignedInfo.SignatureMethod et Reference.DigestMethod).

3.2.2. Signature enveloppée

Nous utiliserons le XML suivant pour la signature

Fichier article.xml
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<Articles>
    <Article>
        <Titre>Windows Presentation Foundation</Titre>
        <Auteur>Thomas Lebrun</Auteur>
        <URL>http://morpheus.developpez.com/windows-presentation-foundation/</URL>
    </Article>
    <Article>
        <Titre>Les nouveautés du Framework .NET 2.0</Titre>
        <Auteur>Louis Guillaume Morand</Auteur>
        <URL>http://lgmorand.developpez.com/dotnet/framework2/</URL>
    </Article>
    <Article>
        <Titre>La signature électronique</Titre>
        <Auteur>David Grellety</Auteur>
        <URL>http://stormimon.developpez.com/dotnet/signature-electronique/</URL>
    </Article>
</Articles>

3.2.2.1. Signature du document entier

Tout d'abord, il faut commencer par charger notre fichier XML.

 
Sélectionnez
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true; 
xmlDoc.Load("articles.xml");


Maintenant nous allons créer un objet SignedXml portant sur le document XML que l'on veut signer.

 
Sélectionnez
SignedXml signedXml = new SignedXml(xmlDoc);


On renseigne la clé à utiliser pour signer en indiquant la clé privée de notre certificat.

 
Sélectionnez
signedXml.SigningKey = certificate.PrivateKey;


Puis on ajoute ensuite les informations du certificat afin de pouvoir vérifier la signature. Dans notre exemple, les informations du certificat seront sous la forme de balises X509Data (cf. exemple de signature XML).

 
Sélectionnez
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;


On crée un objet Reference qui permet de préciser les informations à signer. Pour signer l'ensemble du document on utilise une chaîne vide comme référence.

 
Sélectionnez
Reference reference = new Reference();
reference.Uri = "";


On va maintenant ajouter une transformation pour indiquer qu'il s'agit d'une signature enveloppée. Cette étape est très importante car cela permet de ne pas tenir compte des éléments Signature déjà présents pour le calcul de l'empreinte.

 
Sélectionnez
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());


On ajoute la référence à l'objet SignedXml.

 
Sélectionnez
signedXml.AddReference(reference);


On calcule la signature et on récupère le XML associé.

 
Sélectionnez
signedXml.ComputeSignature();
XmlElement signature = signedXml.GetXml();


On ajoute à la fin du document XML la signature générée puis on sauvegarde le document ainsi modifié.

 
Sélectionnez
xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(signature, true));
xmlDoc.Save("enveloped.xml");


A ce stade notre document est maintenant signé. Passons à la vérification de la signature.

3.2.2.2. Vérification de la signature

On charge le fichier XML signé.

 
Sélectionnez
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true; 
xmlDoc.Load("enveloped.xml");


On crée un objet SignedXml portant sur le document XML à vérifier.

 
Sélectionnez
SignedXml signedXml = new SignedXml(xmlDoc);


On récupère la signature et on la charge avec l'objet SignedXml

 
Sélectionnez
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature"); 
signedXml.LoadXml((XmlElement)nodeList[0]);


Pour finir on appelle la méthode CheckSignature afin de savoir si la signature est valide ou non.

 
Sélectionnez
bool valid = signedXml.CheckSignature();


La vérification échouera si le document a été modifié.

3.2.2.3. Signature d'éléments du document

Pour signer certains éléments et non plus le document entier ce n'est pas plus compliqué. Il faut par contre légèrement modifier le fichier XML de départ en ajoutant dans notre exemple un attribut Id sur les éléments Article.

Fichier article_modif.xml
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<Articles>
    <Article Id="ART1">
        <Titre>Windows Presentation Foundation</Titre>
        <Auteur>Thomas Lebrun</Auteur>
        <URL>http://morpheus.developpez.com/windows-presentation-foundation/</URL>
    </Article>
    <Article Id="ART2">
        <Titre>Les nouveautés du Framework .NET 2.0</Titre>
        <Auteur>Louis Guillaume Morand</Auteur>
        <URL>http://lgmorand.developpez.com/dotnet/framework2/</URL>
    </Article>
    <Article Id="ART3">
        <Titre>La signature électronique</Titre>
        <Auteur>David Grellety</Auteur>
        <URL>http://stormimon.developpez.com/dotnet/signature-electronique/</URL>
    </Article>
</Articles>


Ensuite les seules choses à changer dans le code précédent sont la gestion des références sur les éléments à signer et l'ajout du XML de la signature. Pour signer uniquement l'article ART1.

 
Sélectionnez
Reference reference = new Reference();
reference.Uri = "#ART1";
 
Sélectionnez
XmlNode article = xmlDoc.SelectSingleNode("//Article[@Id='ART1']");
article.AppendChild(xmlDoc.ImportNode(signature, true));
xmlDoc.Save("enveloped.xml");

3.2.2.4. Vérification de la signature

Le code utilisé pour vérifier la signature du document complet est toujours valable dans ce cas de figure.

3.2.3. Signature enveloppante

Pour effectuer une signature enveloppante, il faut ajouter les données à signer dans un élément Object qui sera inclus à la signature. Cet élément doit posséder une référence.


Nous allons maintenant signer un fichier PDF au lieu d'un fichier XML. Pour les besoins de la signature, les données de ce fichier doivent néanmoins être placées dans un élément XML.

 
Sélectionnez
string xmlDsigUrl = SignedXml.XmlDsigNamespaceUrl;

// Création de l'élément XML contenant les données à signer
XmlDocument xmlDoc = new XmlDocument();
XmlElement element = xmlDoc.CreateElement("Content", xmlDsigUrl);
element.InnerText = Convert.ToBase64String(File.ReadAllBytes("test.pdf"));
xmlDoc.AppendChild(element);

// Instanciation de l'objet SignedXml
SignedXml signedXml = new SignedXml();

// Définition de la clé de signature
signedXml.SigningKey = certificate.PrivateKey;

// Ajout des informations du certificat utilisé pour signer
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;

// Ajout des données à signer 
DataObject dataObject = new DataObject();
dataObject.Data = xmlDoc.ChildNodes;
dataObject.Id = "CONTENT";
signedXml.AddObject(dataObject);

// Création et ajout de la référence sur les données à signer
Reference reference = new Reference();
reference.Uri = "#CONTENT";
signedXml.AddReference(reference);

// Génération et sauvegarde de la signature
signedXml.ComputeSignature();
XmlElement signature = signedXml.GetXml();

using (XmlTextWriter writer = new XmlTextWriter("enveloping.xml", Encoding.UTF8))
{
    signature.WriteTo(writer);
}

3.2.3.1. Vérification de la signature

A la différence de la signature enveloppée, il n'est pas nécessaire de créer notre objet SignedXml en indiquant le document XML à valider. Charger l'élément Signature sera donc suffisant

 
Sélectionnez
// Chargement du XML contenant la signature
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("enveloping.xml");

// Instanciation de l'objet SignedXml
SignedXml signedXml = new SignedXml();
// Chargement de la signature
signedXml.LoadXml(xmlDoc.DocumentElement);

// Vérification
bool valid = signedXml.CheckSignature();

3.2.4. Signature détachée

Pour la signature détachée, le XML de la signature sera enregistré dans un fichier indépendant des données à signer. Pour le reste on retrouve toujours le même principe.


Voici comment réaliser une signature détachée du fichier test.pdf

 
Sélectionnez
// Instanciation de l'objet SignedXml
SignedXml signedXml = new SignedXml();

// Définition de la clé de signature
signedXml.SigningKey = certificate.PrivateKey;

// Ajout des informations du certificat utilisé pour signer
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;

// Création et ajout de la référence sur le fichier à signer
Reference reference = new Reference();
reference.Uri = "test.pdf";
signedXml.AddReference(reference);

// Génération et sauvegarde de la signature
signedXml.ComputeSignature();
XmlElement signature = signedXml.GetXml();

using (XmlTextWriter writer = new XmlTextWriter("detached.xml", Encoding.UTF8))
{
    signature.WriteTo(writer);
}

3.2.4.1. Vérification de la signature

La vérification est triviale car le code est le même que pour la signature enveloppante.

 
Sélectionnez
// Chargement du XML contenant la signature
XmlDocument detached = new XmlDocument();
detached.Load("detached.xml");

// Instanciation de l'objet SignedXml
SignedXml signedXml = new SignedXml();

// Chargement de la signature
signedXml.LoadXml(detached.DocumentElement);

// Vérification
bool valid = signedXml.CheckSignature();

3.2.5. Utilisation de la canonicalisation

Nous avons parlé de la canonicalisation un peu plus tôt, voyons en pratique ce qu'il en est dans le cadre d'une signature détachée. Nous allons modifier le code pour signer le fichier article.xml, la canonicalisation s'appliquant au XML uniquement.

 
Sélectionnez
Reference reference = new Reference();
reference.Uri = "article.xml";


Après signature, modifiez le fichier article.xml afin d'avoir quelque chose comme ceci

Fichier article.xml après modification
Sélectionnez
<?xml 
version="1.0" 
encoding="utf-8"?>
<Articles >
    <Article >
        <Titre>Windows Presentation Foundation</Titre>
        <Auteur>Thomas Lebrun</Auteur>
        <URL>http://morpheus.developpez.com/windows-presentation-foundation/</URL>
    </Article >
    <Article >
        <Titre>Les nouveautés du Framework .NET 2.0</Titre>
        <Auteur>Louis Guillaume Morand</Auteur>
        <URL>http://lgmorand.developpez.com/dotnet/framework2/</URL>
    </Article >
    <Article >
        <Titre>La signature électronique</Titre>
        <Auteur>David Grellety</Auteur>
        <URL>http://stormimon.developpez.com/dotnet/signature-electronique/</URL>
    </Article >
</Articles >


La vérification de la signature va échouer car le document a été modifié. Pourtant du point de vue logique ce document XML n'est pas différent de celui utilisé lors de la signature.


Nous allons maintenant ajouter une transformation utilisant la canonicalisation avec la classe XmlDsigExcC14NTransform.

 
Sélectionnez
Reference reference = new Reference();
reference.Uri = "article.xml";
reference.AddTransform(new XmlDsigExcC14NTransform());


Vous pouvez à présent modifier le fichier XML (dans les limites de ce qui est géré par la canonicalisation bien évidemment), la signature restera valide.

Il existe plusieurs classes pour la canonicalisation en fonction des besoins.
XmlDsigC14NTransform
XmlDsigExcC14NTransform
XmlDsigC14NWithCommentsTransform
XmlDsigExcC14NWithCommentsTransform

3.2.6. Ajouter des informations à la signature

Il est possible d'ajouter des informations à la signature si nécessaire. Il suffit pour cela d'utiliser la méthode AddObject comme nous l'avons vu dans la signature enveloppante.

 
Sélectionnez
string xmlDsigUrl = SignedXml.XmlDsigNamespaceUrl;

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("articles.xml");

SignedXml signedXml = new SignedXml(xmlDoc);
signedXml.SigningKey = certificate.PrivateKey;

KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;

XmlElement elem = xmlDoc.CreateElement("SigningTime", xmlDsigUrl);
elem.InnerText = DateTime.Now.ToString();

DataObject dataObject = new DataObject("Info", null, null, elem);
signedXml.AddObject(dataObject);

Reference reference = new Reference();
reference.Uri = "";
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference);

reference = new Reference();
reference.Uri = "#Info";
signedXml.AddReference(reference);

signedXml.ComputeSignature();
XmlElement signature = signedXml.GetXml();

xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(signature, true));
xmlDoc.Save("envelopedInfo.xml");


Vous remarquerez l'ajout d'une seconde référence dans notre exemple de code afin de signer également les informations annexes. Cette seconde référence est bien évidemment optionnelle.


On retrouve cette utilisation dans un autre standard du W3C, XAdES (XML Advanced Electronic Signature). Le but de ce standard est de garantir la validité des signatures électroniques dans le temps.

3.2.7. Adaptation pour utiliser XAdES

Dans le cadre de XAdES il est nécessaire de pouvoir signer des éléments se trouvant dans l'élément Object. Malheureusement, la classe SignedXml ne permet pas de référencer les éléments enfants d'un élément Object. Lors du calcul de la signature une exception sera déclenchée.


Mais heureusement tout n'est pas perdu ! Pour pouvoir effectuer des signatures au format XAdES il faut créer une classe dérivant de SignedXml et redéfinir la méthode SignedXml.GetIdElement afin de combler le manque.


La méthode GetIdElement renvoie un objet XmlElement possédant un attribut Id avec une valeur donnée. Il suffit donc de parcourir les éléments Object et tester leurs enfants si jamais la méthode de base n'a rien trouvé.

 
Sélectionnez
public sealed class XadesXml : SignedXml
{
    public XadesXml(XmlDocument doc)
        : base(doc)
    {
    }

    public override XmlElement GetIdElement(XmlDocument document, string idValue)
    {
        XmlElement elem = base.GetIdElement(document, idValue);

        if (elem == null)
        {
            XmlNode node1;
            foreach (DataObject data in base.Signature.ObjectList)
            {
                foreach (XmlNode node in data.Data)
                {
                    node1 = node.SelectSingleNode("//SignedProperties[@Id='" + idValue + "']");
                    if (node1 != null)
                    {
                        return (XmlElement)node1;
                    }
                }
            }
        }

        return elem;
    }
}


Avec cette classe vous pourrez maintenant gérer le format XAdES sans soucis.


Voici un exemple d'utilisation de la classe dans le cadre d'une signature XAdES. A noter que l'exemple est volontairement simpliste, XAdES imposant plus d'informations.

 
Sélectionnez
string xadesUrl = "http://uri.etsi.org/01903/v1.1.1#";

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("articles.xml");

SignedXml signedXml = new XadesXml(xmlDoc);
signedXml.SigningKey = certificate.PrivateKey;
// Ajout d'un identifiant au bloc de signature
signedXml.Signature.Id = "SIG";

KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;

// Création du bloc d'informations pour XAdES
XmlElement elem = xmlDoc.CreateElement("xad", "QualifyingProperties", xadesUrl);
// Création d'un lien vers le bloc de signature associé
elem.Attributes.Append(xmlDoc.CreateAttribute("Target"));
elem.Attributes["Target"].Value = "#SIG";

// Création de l'élément SignedProperties contenant les données XAdES à signer
XmlElement signedProperties = xmlDoc.CreateElement("xad", "SignedProperties", xadesUrl);
signedProperties.Attributes.Append(xmlDoc.CreateAttribute("Id"));
signedProperties.Attributes["Id"].Value = "SIG_SP";
elem.AppendChild(signedProperties);

XmlElement signedSignatureProperties = xmlDoc.CreateElement("xad", "SignedSignatureProperties", xadesUrl);
signedProperties.AppendChild(signedSignatureProperties);

XmlElement signingTime = xmlDoc.CreateElement("xad", "SigningTime", xadesUrl);
signingTime.InnerText = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
signedSignatureProperties.AppendChild(signingTime);

// Création de l'élément UnsignedProperties contenant les données XAdES non signées
XmlElement unsignedProperties = xmlDoc.CreateElement("xad", "UnsignedProperties", xadesUrl);
elem.AppendChild(unsignedProperties);

XmlElement unsignedSignatureProperties = xmlDoc.CreateElement("xad", "UnsignedSignatureProperties", xadesUrl);
unsignedProperties.AppendChild(unsignedSignatureProperties);

XmlElement machineName = xmlDoc.CreateElement("MachineName");
machineName.InnerText = Environment.MachineName;
unsignedSignatureProperties.AppendChild(machineName);

// Ajout du bloc XAdES à la signature
DataObject dataObject = new DataObject(null, null, null, elem);
signedXml.AddObject(dataObject);

// Ajout d'une référence sur le document à signer
Reference reference = new Reference();
reference.Uri = "";
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference);

// Ajout d'une référence sur les données XAdES à signer (SignedProperties)
reference = new Reference();
reference.Uri = "#SIG_SP";
reference.AddTransform(new XmlDsigExcC14NTransform());
signedXml.AddReference(reference);

signedXml.ComputeSignature();
XmlElement signature = signedXml.GetXml();

xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(signature, true));
xmlDoc.Save("enveloped.xml");

3.2.8. Cosignature

La cosignature consiste à signer le document (ou l'élément) par plusieurs personnes. Les signatures se trouveront toutes au même niveau.

Cela se fait très simplement en effectuant une signature enveloppée du document ou de l'élément déjà signé. Cette signature viendra s'ajouter à la précédente. On obtient ainsi une cosignature.

3.2.9. Contre-signature

La contre-signature consiste à signer une signature existante. Cela revient à réaliser une signature XML de l'élément XML représentant la signature à contre-signer et d'inclure le bloc XML de la signature ainsi générée dans la signature à contre-signer.

Afin de pouvoir contre-signer une signature, il est nécessaire d'ajouter un ID sur la valeur de la signature (noeud SignatureValue du bloc XML associé à la signature) qui servira de référence pour la contre-signature. Il faut également ajouter un noeud qui nous servira à stocker la contre-signature.

Pour l'exemple nous repartirons du code utilisé pour la signature enveloppée d'un document entier.

 
Sélectionnez
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true; 
xmlDoc.Load("articles.xml");

SignedXml signedXml = new SignedXml(xmlDoc);
signedXml.SigningKey = certificate.PrivateKey; 

KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;

// Ajout d'un noeud CounterSignature pour le stockage de la contre-signature
XmlElement counterSignature = xmlDoc.CreateElement("CounterSignature", "http://www.w3.org/2000/09/xmldsig#");
signedXml.AddObject(new DataObject(null, null, null, counterSignature));

Reference reference = new Reference();
reference.Uri = "";
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference); 

signedXml.ComputeSignature();

XmlElement signature = signedXml.GetXml();

// Ajout d'un ID pour avoir une référence lors de la contre-signature
XmlNamespaceManager mgr = new XmlNamespaceManager(xmlDoc.NameTable);
mgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
XmlNode signatureValue = signature.SelectSingleNode("//ds:SignatureValue", mgr);
signatureValue.Attributes.Append(xmlDoc.CreateAttribute("ID"));
signatureValue.Attributes["ID"].Value = "SIGV";

xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(signature, true));
xmlDoc.Save("enveloped.xml");

Pour contre-signer, il suffit ensuite de réaliser une signature enveloppée avec comme référence l'ID de la signature à contre-signer.

 
Sélectionnez
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true; 
xmlDoc.Load("enveloped.xml");

SignedXml signedXml = new SignedXml(xmlDoc);
signedXml.SigningKey = certificate.PrivateKey; 

KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;

Reference reference = new Reference();
reference.Uri = "#SIGV";
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference); 

signedXml.ComputeSignature();
XmlElement signature = signedXml.GetXml();

XmlNamespaceManager mgr = new XmlNamespaceManager(xmlDoc.NameTable);
mgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
XmlNode counterSignature = xmlDoc.SelectSingleNode("//ds:CounterSignature", mgr);
counterSignature.AppendChild(xmlDoc.ImportNode(signature, true));

xmlDoc.Save("enveloped.xml");

Le code pour la vérification des signatures est identique à celui utilisée pour la vérification d'une signature enveloppée.

A noter qu'il est bien évidemment possible de contre-signer une contre-signature.

La contre-signature est prévue par XAdES avec un élément CounterSignature. Voici un schéma, tiré de la note du W3C sur XAdES, montrant l'utilisation de cet élément.

image

4. Conclusion

J'espère que ce tutoriel vous aura permis de comprendre comment fonctionne la signature électronique et qu'il aidera ceux qui seront amenés à mettre en place la signature XML au sein de leurs applications.


Bien qu'il aborde la plupart des cas de signature XML ce tutoriel n'est évidemment pas exhaustif. Si vos questions ne trouvent pas leurs réponses dans ce tutoriel n'hésitez pas à me contacter par MP afin que je mette à jour ce tutoriel, à condition que j'ai la réponse bien évidemment ;-)

5. Liens

6. Annexes

6.1. Exemple de signature enveloppée

 
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<Articles>
   <Article>
      <Titre>Windows Presentation Foundation</Titre>
      <Auteur>Thomas Lebrun</Auteur>
      <URL>http://morpheus.developpez.com/windows-presentation-foundation/</URL>
   </Article>
   <Article>
      <Titre>Les nouveautés du Framework .NET 2.0</Titre>
      <Auteur>Louis Guillaume Morand</Auteur>
      <URL>http://lgmorand.developpez.com/dotnet/framework2/</URL>
   </Article>
   <Article>
      <Titre>La signature électronique</Titre>
      <Auteur>David Grellety</Auteur>
      <URL>http://stormimon.developpez.com/dotnet/signature-electronique/</URL>
   </Article>
   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
         <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
         <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
         <Reference URI="">
            <Transforms>
               <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>jX73wKyyax09gARIJal210XBer4=</DigestValue>
         </Reference>
      </SignedInfo>
      <SignatureValue>FztYk7RaJ91b0nMHXeOrDsnBT4fYO8EzzsyJ0/v6w...</SignatureValue>
      <KeyInfo>
         <X509Data>
             <X509Certificate>MIIB8DCCAV2gAwIBAgIQV6ptLvlaoIdJF9Qu...</X509Certificate>
         </X509Data>
      </KeyInfo>
   </Signature>
</Articles>
 
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<Articles>
   <Article Id="ART1">
      <Titre>Windows Presentation Foundation</Titre>
      <Auteur>Thomas Lebrun</Auteur>
      <URL>http://morpheus.developpez.com/windows-presentation-foundation/</URL>
   </Article>
   <Article Id="ART2">
      <Titre>Les nouveautés du Framework .NET 2.0</Titre>
      <Auteur>Louis Guillaume Morand</Auteur>
      <URL>http://lgmorand.developpez.com/dotnet/framework2/</URL>
      <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
         <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
            <Reference URI="#ART2">
               <Transforms>
                  <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
               </Transforms>
               <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
               <DigestValue>8QPhVF66XmYpcg5tLbOksRE90oE=</DigestValue>
            </Reference>
         </SignedInfo>
         <SignatureValue>pffXUSmnQKNs6M89hwnUPZsjM89hSqg7HnuNwOl6p...</SignatureValue>
         <KeyInfo>
            <X509Data>
                <X509Certificate>MIIB8DCCAV2gAwIBAgIQV6ptLvlaoIdJF9Qu...</X509Certificate>
            </X509Data>
         </KeyInfo>
      </Signature>
   </Article>
   <Article Id="ART3">
      <Titre>La signature électronique</Titre>
      <Auteur>David Grellety</Auteur>
      <URL>http://stormimon.developpez.com/dotnet/signature-electronique/</URL>
   </Article>
</Articles>

6.2. Exemple de signature enveloppante

 
Sélectionnez
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
   <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="#CONTENT">
         <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
         <DigestValue>Bw9yS8blHQiKxqbfgoYCNTLvldU=</DigestValue>
      </Reference>
   </SignedInfo>
   <SignatureValue>bqev0pbHyesEUsswr4kK5ySuT7j8Hcm1HbU+nM/4y...</SignatureValue>
   <KeyInfo>
      <X509Data>
          <X509Certificate>MIIB8DCCAV2gAwIBAgIQV6ptLvlaoIdJF9Qu...</X509Certificate>
      </X509Data>
   </KeyInfo>
   <Object Id="CONTENT">
      <Content>JVBERi0xLjQNJYKCDTggMCBvYmo...</Content>
   </Object>
</Signature>

6.3. Exemple de signature détachée

 
Sélectionnez
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
   <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="articles.xml">
         <Transforms>
            <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
         </Transforms>
         <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
         <DigestValue>jX73wKyyax09gARIJal210XBer4=</DigestValue>
      </Reference>
   </SignedInfo>
   <SignatureValue>fe4LC2Msuh8cDKt1EyKMIQHJk5JzUMkXs89Mg42kI...</SignatureValue>
   <KeyInfo>
      <X509Data>
          <X509Certificate>MIIB8DCCAV2gAwIBAgIQV6ptLvlaoIdJF9Qu...</X509Certificate>
      </X509Data>
   </KeyInfo>
</Signature>

6.4. Exemple de signature enveloppée avec ajout d'informations

 
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<Articles>
   <Article>
      <Titre>Windows Presentation Foundation</Titre>
      <Auteur>Thomas Lebrun</Auteur>
      <URL>http://morpheus.developpez.com/windows-presentation-foundation/</URL>
   </Article>
   <Article>
      <Titre>Les nouveautés du Framework .NET 2.0</Titre>
      <Auteur>Louis Guillaume Morand</Auteur>
      <URL>http://lgmorand.developpez.com/dotnet/framework2/</URL>
   </Article>
   <Article>
      <Titre>La signature électronique</Titre>
      <Auteur>David Grellety</Auteur>
      <URL>http://stormimon.developpez.com/dotnet/signature-electronique/</URL>
   </Article>
   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
         <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
         <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
         <Reference URI="">
            <Transforms>
               <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>jX73wKyyax09gARIJal210XBer4=</DigestValue>
         </Reference>
         <Reference URI="#Info">
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>bvyX9pyL/Z9uweV8phcO40GkLbY=</DigestValue>
         </Reference>
      </SignedInfo>
      <SignatureValue>crVtZy7aaSoM+reyRoqNQoPF5snDVEg7fWj4VNr7J...</SignatureValue>
      <KeyInfo>
         <X509Data>
             <X509Certificate>MIIB8DCCAV2gAwIBAgIQV6ptLvlaoIdJF9Qu...</X509Certificate>
         </X509Data>
      </KeyInfo>
      <Object Id="Info">
         <SigningTime>07/02/2009 18:07:37</SigningTime>
      </Object>
   </Signature>
</Articles>

6.5. Exemple de signature XAdES

 
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<Articles>
   <Article>
      <Titre>Windows Presentation Foundation</Titre>
      <Auteur>Thomas Lebrun</Auteur>
      <URL>http://morpheus.developpez.com/windows-presentation-foundation/</URL>
   </Article>
   <Article>
      <Titre>Les nouveautés du Framework .NET 2.0</Titre>
      <Auteur>Louis Guillaume Morand</Auteur>
      <URL>http://lgmorand.developpez.com/dotnet/framework2/</URL>
   </Article>
   <Article>
      <Titre>La signature électronique</Titre>
      <Auteur>David Grellety</Auteur>
      <URL>http://stormimon.developpez.com/dotnet/signature-electronique/</URL>
   </Article>
   <Signature Id="SIG" xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
         <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
         <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
         <Reference URI="">
            <Transforms>
               <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>jX73wKyyax09gARIJal210XBer4=</DigestValue>
         </Reference>
         <Reference URI="#SIG_SP">
            <Transforms>
               <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>xJbbfZySt5arzMmQWnTNoNfqLpg=</DigestValue>
         </Reference>
      </SignedInfo>
      <SignatureValue>iUkiNbIf8miyeby3YSWhtsQ4yEzK323+AfL2AGCx...</SignatureValue>
      <KeyInfo>
         <X509Data>
             <X509Certificate>MIIB8DCCAV2gAwIBAgIQV6ptLvlaoIdJF9Qu...</X509Certificate>
         </X509Data>
      </KeyInfo>
      <Object>
         <xad:QualifyingProperties Target="#SIG" xmlns:xad="http://uri.etsi.org/01903/v1.1.1#">
            <xad:SignedProperties Id="SIG_SP">
               <xad:SignedSignatureProperties>
                  <xad:SigningTime>2009-02-07T19:05:23Z</xad:SigningTime>
               </xad:SignedSignatureProperties>
            </xad:SignedProperties>
            <xad:UnsignedProperties>
               <xad:UnsignedSignatureProperties>
                  <MachineName xmlns="">FIRESTORM</MachineName>
               </xad:UnsignedSignatureProperties>
            </xad:UnsignedProperties>
         </xad:QualifyingProperties>
      </Object>
   </Signature>
</Articles>

Remerciements

Je tiens ici à remercier l'équipe .NET de Developpez.com pour ses relectures attentives et ses suggestions, et en particulier pvialatte et tomlev.