La signature électronique avec .Net
Date de publication : 27/02/2009 , Date de mise à jour : 09/05/2010
Par
David Grellety (Page d'accueil)
Ce tutoriel a pour but d'expliquer le principe de la signature électronique et comment mettre en place la signature XML à l'aide des fonctionnalités du .NET Framework.
Commentez cet article :
1 commentaire ·
1. Introduction
2. La signature électronique
2.1. Pourquoi signer électroniquement ?
2.2. Comment ça marche ?
2.2.1. Algorithmes de hachage
2.2.2. Algorithmes de chiffrement
2.2.3. Schéma de principe
2.3. Clés privées, clés publiques ? Késako ?!
2.4. Les certificats
2.4.1. Installer un certificat
2.4.2. Accéder aux certificats sous Windows
2.4.2.1. Internet Explorer
2.4.2.2. Console MMC
2.4.3. Les informations d'un certificat
2.5. La signature XML
3. Mise en pratique
3.1. Les certificats
3.2. La signature XML
3.2.1. La classe SignedXml
3.2.1.1. Méthodes importantes
3.2.1.2. Propriétés importantes
3.2.1.3. Champs statiques
3.2.2. Signature enveloppée
3.2.2.1. Signature du document entier
3.2.2.2. Vérification de la signature
3.2.2.3. Signature d'éléments du document
3.2.2.4. Vérification de la signature
3.2.3. Signature enveloppante
3.2.3.1. Vérification de la signature
3.2.4. Signature détachée
3.2.4.1. Vérification de la signature
3.2.5. Utilisation de la canonicalisation
3.2.6. Ajouter des informations à la signature
3.2.7. Adaptation pour utiliser XAdES
3.2.8. Cosignature
3.2.9. Contre-signature
4. Conclusion
5. Liens
6. Annexes
6.1. Exemple de signature enveloppée
6.2. Exemple de signature enveloppante
6.3. Exemple de signature détachée
6.4. Exemple de signature enveloppée avec ajout d'informations
6.5. Exemple de signature XAdES
Remerciements
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.

Copyright Lex Persona - Reproduction autorisée par l'auteur
- Calcul de l'empreinte des données à signer.
- Chiffrement de l'empreinte à l'aide de la clé privée. On obtient alors la signature.
- Déchiffrement de la signature avec la clé publique. Cela permet de retrouver l'empreinte associée aux données signées.
- 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.
Vous aurez ainsi accès à la liste des certificats, par dossier.
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.
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.
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.
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 |
<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.
X509Store store = new X509Store(StoreLocation.CurrentUser);
|
Pour accéder au dossier personnel du magasin de l'utilisateur courant on utilisera.
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.
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.
store.Open(OpenFlags.ReadOnly);
|
Une fois ouvert, on va pouvoir accéder à la liste des certificats via la propriété Certificates.
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.
L'exécution de ce code devrait produire un affichage similaire au suivant
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
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509CertificateCollection certificates =
X509Certificate2UI.SelectFromCollection
(
store.Certificates,
"Liste des certificats",
"Veuillez sélectionner un certificat",
X509SelectionFlag.SingleSelection
);
store.Close();
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 |
<?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.
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.
SignedXml signedXml = new SignedXml(xmlDoc);
|
On renseigne la clé à utiliser pour signer en indiquant la clé privée de notre certificat.
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).
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.
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.
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
|
On ajoute la référence à l'objet SignedXml.
signedXml.AddReference(reference);
|
On calcule la signature et on récupère le XML associé.
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é.
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é.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("enveloped.xml");
|
On crée un objet SignedXml portant sur le document XML à vérifier.
SignedXml signedXml = new SignedXml(xmlDoc);
|
On récupère la signature et on la charge avec l'objet SignedXml
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.
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 |
<?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.
Reference reference = new Reference();
reference.Uri = "#ART1";
|
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.
string xmlDsigUrl = SignedXml.XmlDsigNamespaceUrl;
XmlDocument xmlDoc = new XmlDocument();
XmlElement element = xmlDoc.CreateElement("Content", xmlDsigUrl);
element.InnerText = Convert.ToBase64String(File.ReadAllBytes("test.pdf"));
xmlDoc.AppendChild(element);
SignedXml signedXml = new SignedXml();
signedXml.SigningKey = certificate.PrivateKey;
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;
DataObject dataObject = new DataObject();
dataObject.Data = xmlDoc.ChildNodes;
dataObject.Id = "CONTENT";
signedXml.AddObject(dataObject);
Reference reference = new Reference();
reference.Uri = "#CONTENT";
signedXml.AddReference(reference);
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
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("enveloping.xml");
SignedXml signedXml = new SignedXml();
signedXml.LoadXml(xmlDoc.DocumentElement);
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
SignedXml signedXml = new SignedXml();
signedXml.SigningKey = certificate.PrivateKey;
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;
Reference reference = new Reference();
reference.Uri = "test.pdf";
signedXml.AddReference(reference);
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.
XmlDocument detached = new XmlDocument();
detached.Load("detached.xml");
SignedXml signedXml = new SignedXml();
signedXml.LoadXml(detached.DocumentElement);
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.
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 |
<?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.
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.
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.
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é.
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.
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;
signedXml.Signature.Id = "SIG";
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;
XmlElement elem = xmlDoc.CreateElement("xad", "QualifyingProperties", xadesUrl);
elem.Attributes.Append(xmlDoc.CreateAttribute("Target"));
elem.Attributes["Target"].Value = "#SIG";
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);
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);
DataObject dataObject = new DataObject(null, null, null, elem);
signedXml.AddObject(dataObject);
Reference reference = new Reference();
reference.Uri = "";
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference);
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.
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 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();
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.
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.
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
<?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>
|
<?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
<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
<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
<?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
<?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.


Les sources présentées sur cette page sont libres de droits
et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation
constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ©
2009 David Grellety. Aucune reproduction,
même partielle, ne peut être faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 €
de dommages et intérêts.
Cette page est déposée.