La cryptographie en VB.NETDate de publication : 13/12/2005
Par
J-M Rabilloud (bidou.developpez.com) Cet article traitera des manipulations cryptographiques avec VB.NET. I. Introduction II. Concepts II-A. Chiffrement II-A-1. Algorithme secret II-A-2. Chiffrement à clé II-A-2-a. Chiffrement à clé symétrique II-A-2-b. Chiffrement à clé asymétrique II-A-2-c. Chiffrement à sens unique II-A-2-d. Techniques dérivées II-B. Identification II-B-1. Hachage II-B-2. Limites du hachage II-B-3. Hachage avec une clé II-C. Génération de clé II-C-1. Aléatoire II-C-2. Dérivée II-C-2-a. Mot de passe II-C-3. Composite II-D. Evaluation des risques et des besoins II-D-1. Boucher les failles II-D-2. Comprendre l'attaquant II-D-3. Limitations III. L'espace de nom Security.Cryptography IV. Exemples IV-A. Chiffrement symétrique de fichier IV-B. Chiffrement Asymétrique de clé IV-C. Le hachage IV-C-1. Salage IV-C-2. Contrôle d'intégrité IV-C-3. Signature numérique IV-D. Clé dérivée IV-E. Clé composite V. Conclusion I. Introduction
Dans cet article nous allons nous pencher sur la sécurisation des données privées au sens large du terme.
De tout temps la manipulation des données afin d'en préserver le secret ou l'intégrité fut un enjeu majeur de l'information.
Que ce soit la permutation de Jules César, l'encre invisible ou le sceau royal, il fut et demeure nécessaire de pouvoir identifier ou chiffrer des données.
Le Framework .NET met à notre disposition un certain nombre de classes pour simplifier le travail, encore faut-il avoir les idées claires quant aux concepts mis en œuvre.
Cet article sera écrit en VB.NET 2003.
Bonne lecture.
II. Concepts
En informatique, il est toujours assez complexe de déterminer ce qui relève de quoi.
En l'occurrence, la meilleure façon de sécuriser des données consiste évidemment à ne pas les divulguer.
Certes, ce n'est pas toujours très commode mais cet aspect n'est pas à perdre de vue.
Pourquoi ?
La protection des données repose généralement sur plusieurs couches, la première devant être la sécurisation du poste de travail.
En effet il ne sert à rien de chiffrer 140 fois un fichier si les clés sont facilement accessibles.
Nous verrons cela plus en détail lors de l'étude de la sécurisation des clés, mais si vous n'avez aucune notion d'administration du poste de travail, je vous encourage vivement à vous pencher sur le sujet.
II-A. Chiffrement
Souvent jargonné sous le terme "Cryptage", le chiffrement est le principe le plus connu même si ce n'est pas toujours le mieux compris.
Le principe consiste à transformer les données avec des méthodes plus ou moins complexes afin d'obtenir des données "protégées".
Cette opération doit pouvoir être accomplie en sens inverse, le déchiffrement, pour pouvoir récupérer les données d'origines.
Il existe de très nombreuses techniques car le principe est vieux comme le monde.
II-A-1. Algorithme secret
Bien qu'ils aient presque complètement disparus, je vous en parle car il s'agit d'une tentation fréquente lorsqu'on commence à se pencher sur les joies de la cryptographie.
A la base, même une compression est un encodage et on peut facilement croire qu'un encodage par un algorithme qu'on est le seul à connaître est particulièrement sûr.
Malheureusement, le Reverse Engineering étant ce qu'il est, cette approche est erronée.
Il est toujours assez simple de retrouver les opérations d'un algorithme et dès lors de casser la sécurité.
Ne vous lancez pas dans ce genre d'aventure, il n'y a pas de plus mauvaise sécurité qu'une sécurité illusoire.
II-A-2. Chiffrement à clé
Tous les algorithmes modernes utilisent maintenant des chiffrements à clés.
Il en existe de plusieurs sortes, regroupés en trois grandes familles.
II-A-2-a. Chiffrement à clé symétrique
Dans ce type de chiffrement, la même clé sert au codage et au décodage des données.
Il en existe plusieurs (Rijndael, RC2, DES, Triple DES, etc…) mais tous reposent sur le même principe.
Pour donner un exemple trivial, un chiffrement à clé symétrique peut être la multiplication par deux de chaque code ASCII.
Deux est alors la clé.
II-A-2-b. Chiffrement à clé asymétrique
Beaucoup plus complexe, ce chiffrement utilise une paire de clé publique/privée.
La clé publique sert au chiffrement et la clé privée au déchiffrement.
Le plus connu d'entre eux est l'algorithme RSA (RIVEST, SHAMIR, ADLEMAN).
Les chiffrements à clé symétrique ou asymétrique ne sont pas plus sûrs selon la nature de la clé.
Cette différence est due aux problèmes de communication de clé.
En effet, dans une approche du chiffrement à des fins de communication, je dois à un moment ou un autre fournir la clé de déchiffrement dans le cas d'une clé symétrique.
La clé est alors vulnérable.
Dans le cas d'un chiffrement asymétrique, il suffit de communiquer la clé publique aux personnes souhaitant m'envoyer des données, pour qu'ils puissent chiffrer les données avec cette clé.
Je pourrais les déchiffrer à l'aide de ma clé privée qui, elle, n'est pas transmise.
Gardez à l'esprit que les chiffrements asymétriques sont plus lourds et donc beaucoup plus long, on les réserve généralement pour le traitement de petit volume de données.
II-A-2-c. Chiffrement à sens unique
Cette famille un peu particulière nommée "hachage" ne permet pas le déchiffrement.
Comme on l'utilise à des fins d'identification, nous la verrons un peu plus loin.
II-A-2-d. Techniques dérivées
Dans la longue lutte entre chiffreurs et casseurs, il apparaît bien régulièrement de nouvelles techniques de dérivation pour brouiller les pistes.
On utilise par exemple le salage pour contrer les attaques par dictionnaire, les clés composites ou les clés dérivées pour protéger la clé, etc…
II-B. Identification
Bien que l'identification soit un vaste domaine, elle fait partie intégrante du monde de la cryptographie informatique.
En effet, l'identification permet de pouvoir reconnaître une source de données chiffrées ou non comme sûre sans avoir à la déchiffrer au préalable.
Par ailleurs l'identification doit servir à diminuer les attaques par leurre.
Bien que généralement non suffisant, la base de l'identification repose généralement sur le hachage.
II-B-1. Hachage
Le hachage (de l'anglais hash) est un chiffrement à sens unique, c'est-à-dire qu'il n'est pas théoriquement possible de remonter à la valeur d'origine en connaissant la valeur hachée.
Il existe de nombreux algorithmes (MD5, SHA1, SHA256,…).
Dans tous les cas, le hachage donne une chaîne résultante de longueur connue que l'on appelle taille de hachage.
L'intérêt du hachage est donc évidemment de faire de l'identification ou du contrôle d'intégrité de données.
On peut aussi l'utiliser à des fins de comparaison.
Ainsi, il est beaucoup plus sûr de faire une comparaison de valeurs hachées que de leurs homologues en clair.
II-B-2. Limites du hachage
Quoique très fiable, le hachage a des limites.
La première de ces limites est une utilisation correcte, car il est assez facile de reconnaître une valeur hachée et il peut être assez facile de deviner ce qui a été haché.
Dès lors, le leurre redevient possible.
Le deuxième danger consiste à stocker des valeurs critiques hachées trop facilement accessible.
Ainsi, si vous hachez des mots de passe et que vous laissez les valeurs obtenues accessibles, vous rendez vos mots de passe vulnérables à une attaque par dictionnaire, voire par force brute.
II-B-3. Hachage avec une clé
Pour contourner la possibilité de corruption du hachage, on utilise généralement un hachage avec une clé, c'est-à-dire qu'on ajoute une valeur secrète à la valeur à hacher ce qui rend théoriquement impossible une mystification.
II-C. Génération de clé
Il s'agit là du pivot de la cryptographie, sans de bonnes clés pas de cryptographie valable.
Là encore, il existe plusieurs techniques de génération de clé.
II-C-1. Aléatoire
Les précurseurs de clé sont générés de façon aléatoire.
Il faut s'assurer que la fonction aléatoire le soit bien, ce qui exclu Rand par exemple, et privilégier les appels à des fonctions ad hoc.
L'avantage de la génération aléatoire est qu'elle permet de générer des clés très robustes quand elle est bien utilisée.
II-C-2. Dérivée
Très populaire puisqu'elle permet de ne pas stocker la clé, la génération de clé dérivée est cependant périlleuse car elle n'est fiable que si les précurseurs (généralement des mots de passe) sont bien choisis, ce qui va nous permettre de gloser quelque peu sur les mots de passe.
II-C-2-a. Mot de passe
L'éternel problème du mot de passe repose sur la continuelle lutte entre mémorisation et robustesse.
Un mot de passe fiable fait généralement une vingtaine de caractère utilisant des lettres sensibles à la casse, des chiffres et des caractères de ponctuation, de plus il est de bon ton qu'il ne contienne aucun mot ou suite de caractères susceptible de le rendre vulnérable à une attaque par dictionnaire.
Autant dire tout de suite que pour mémoriser "dAT9{j*c3-{p5KvADyX(rb" il faut un peu d'entraînement.
Cependant une clé dérivée de "médor" présente assez peu de résistance aussi faut il trouver un compromis selon la sensibilité des données protégées et leur durée de vie.
II-C-3. Composite
Il existe un grand nombre de techniques de génération de clé.
On les nomme clés composites car leurs précurseurs viennent de sources différentes.
Les plus utilisées sont des clés dérivées où le précurseur est un mot de passe prétraité avec une autre donnée.
Attention cependant, il existe des générateurs un peu exotiques.
J'ai vu ainsi un programme utiliser des paramètres hardware dans les précurseurs d'une clé de chiffrement de données.
C'est uniquement le jour du changement de poste de travail que l'utilisateur c'est rendu compte qu'il ne pouvait plus déchiffrer ses données (ce qui prouve au moins qu'elles étaient bien sécurisées).
II-D. Evaluation des risques et des besoins
La première étape lorsqu'on se lance dans la sécurisation des données consiste à évaluer ce dont on a besoin, et donc, d'avoir une idée des techniques d'attaques.
II-D-1. Boucher les failles
Avant toute attaque assez volumineuse, les attaquants vont toujours commencer par chercher les failles éventuelles.
Bien qu'elles soient connues, on les retrouve très fréquemment.
Ne stocker jamais un mot de passe ou une clé en clair dans le code.
De manière générale éviter de stocker quoi que ce soit dans le code car il s'agit d'un endroit particulièrement vulnérable.
Lorsque des valeurs sensibles doivent y être, elles doivent être hachées.
Méfiez vous aussi des chaînes de connexions qui peuvent contenir des informations sensibles en clair.
Ne hacher jamais le seul contenu d'un fichier si vous mettez la valeur hachée dans le fichier
Une des plus redoutables fausses bonnes idées consiste à hacher le contenu du fichier, puis à ajouter la valeur obtenue en fin de fichier.
La première chose que va essayer un pirate qui trouve une valeur hachée en fin de fichier, est une combinaison courante (nom de fichier + contenu, contenu seul, etc…) pour savoir à quoi correspond le hachage.
N'écrivez jamais des données déchiffrées sur le disque
Même temporairement.
Votre chiffre sera d'autant plus vulnérable que le pirate pourra accéder à la fois aux représentations claire et chiffrée.
Il y aura de toute façon une faille grave puisqu'il suffirait de faire planter l'application au moment ou le fichier en clair existe.
Par ailleurs des données même temporairement déchiffrées sur un disque peuvent rester accessibles alors que le fichier est détruit.
N'utilisez jamais de méthode de cryptage cassée
Il ne sert à rien d'implémenter une méthode utilisant le chiffre de beaufort par exemple car certains logiciels de décryptage le casseront en quelques minutes.
Partez du principe que l'attaquant sait ce qu'il fait.
Toutes les méthodes de chiffrement vulnérables aux attaques par "mot probable" par exemple doivent être bannies.
N'utilisez pas de clé faible
Lorsqu'une clé est générée aléatoirement, il peut exister un risque de créer une clé faible.
Une clé faible est une clé présentant un élément de symétrie.
Par exemple, si pour une clé 128 bits, les 64 premiers bits ont la même valeur que les 64 derniers, vous avez une clé faible.
II-D-2. Comprendre l'attaquant
Pour vous défendre correctement, vous devez appréhender les techniques de l'attaquant.
La cryptographie est une chaîne dont la sécurité est régie par le maillon le plus faible.
A part quelques boutonneux généralement peu dangereux, les attaquants qui cherchent à casser un chiffre sont des gens expérimentés qui vont toujours chercher le point de vulnérabilité de votre défense.
La lutte peut revêtir deux aspects, la bataille des clés ou l'attaque du chiffre.
Attaquer un chiffre donné par les algorithmes standards de chiffrement, pour peu qu'il soit correctement utilisé, est une entreprise colossale, demandant du temps, des ressources importantes et une solide connaissance des mécanismes de chiffrement.
C'est donc généralement autour de la clé que se joue la sécurité.
Une chose est sûre pour le défenseur, vous devez mettre les clés le plus à l'abri possible.
Une chose est certaine pour l'attaquant, si votre code sait déchiffrer, la clé sera à un moment ou à un autre relativement accessible.
Comme vous l'avez compris, la clé ne doit pas être présente dans le code.
Ou elle doit être dérivée d'une donnée secrète, ou elle doit exister dans une ressource externe.
Si l'application n'est pas censée être installée sur n'importe quel poste, vous pouvez utiliser des fichiers ou le registre (avec des ACL correctes), sinon, seule la dérivation est véritablement fiable.
Dans le pire des cas, l'attaquant peut installer une version fonctionnelle de l'application sur un poste où il possède les droits administrateurs.
II-D-3. Limitations
Utiliser le hachage, les signatures numériques ou des certificats à des fins d'identification.
Une identification réussie ne doit pas vous dispenser d'un contrôle de validité des données entrantes, ceci vous permettra d'éviter qu'une faille se transforme en désastre.
III. L'espace de nom Security.Cryptography
Cet espace de nom contient l'ensemble des classes nécessaires à la gestion de la cryptographie.
On retrouve généralement toujours la même construction hiérarchique, à savoir :
<Famille Algorithmes>
<Algorithmes>
<Fournisseur ou Classe>
Une représentation incomplète peut être exprimée par l'arborescence suivante :
![]()
Dans la suite de cet article, nous allons voir quelques exemples des pratiques les plus courantes de manipulation des classes de cet Espace de nom.
IV. ExemplesIV-A. Chiffrement symétrique de fichier
Cette première application (WinForms) permet de crypter des fichiers avec l'algorithme symétrique Triple DES (Data Encryption Standard).
Celui-ci utilise une clé de 168 bits (ou 192 bits), qui sont en fait trois clés de 56 bits et un vecteur d'initialisation.
Ce vecteur à pour but d'empêcher l'identification de blocs chiffrés similaires pour un texte clair similaire.
Généralement on utilise le service cryptographique le plus proche possible des données à chiffrer afin de diminuer les vulnérabilités de la clé.
Il est nécessaire d'avoir la clé et le vecteur pour déchiffrer, ils seront donc créés une fois puis stockés dans le registre.
Nous allons aussi utiliser des évènements personnalisés pour suivre la progression de l'opération.
Je vais donc créer une bibliothèque de classe (crypto.vb) :
Tout d'abord je dérive une classe pour mon argument d'évènement :
Cet argument renverra le nombre d'octets à convertir et le nombre d'octets convertis.
Maintenant nous allons créer une classe de chiffrement / déchiffrement de fichier selon l'algorithme Triple DES.
Mon entête de classe sera :
Comme nous l'avons vu dans la sécurité d'accès au code, je peux demander la vérification des autorisations au niveau assembly afin d'interdire l'appel à ma bibliothèque en cas d'autorisations insuffisantes.
Le code de la classe sera :
Pour exécuter un chiffrement symétrique Triple DES je crée une instance du fournisseur TripleDESCryptoServiceProvider.
Si les valeurs n'existent pas déjà dans le registre, je crée une clé de 192 bits et un vecteur.
Notez que le générateur de nombre aléatoire utilisé pour générer la clé interdit la création d'une clé faible.
Une fois que mon fournisseur possède une clé et un vecteur, il peut fournir ces mécanismes de chiffrement / déchiffrement.
Pour sécuriser l'opération, on chiffre généralement un flux.
Pour cela, on utilise une instance de CryptoStream qui attend une référence au flux sortant, un appel au service de chiffrement et le mode d'action.
Ensuite l'opération elle-même est très simple.
Ce code chiffre donc le fichier spécifié.
Ce chiffrement est robuste, le seul point faible de l'application étant la capacité d'accéder à la clé de registre pour un attaquant.
Si les ACL sont correctement définies, il est quasiment impossible de briser ce chiffrement, surtout si je régénère une clé et un vecteur périodiquement.
Le déchiffrement prendrait exactement la même forme si ce n'est que je devrais créer CryptoStream avec le code :
Et que je dois passer l'extension du fichier de sorti.
Un code de consommation de ma classe pourrait être :
IV-B. Chiffrement Asymétrique de clé
En l'état je peux donc chiffrer et déchiffrer des fichiers sur mon poste.
Par contre je ne peux pas fournir ces fichiers à l'extérieur sans fournir la clé, qui du coup devient plus vulnérable.
Supposons que nous voulions communiquer ces fichiers à neo.51 par mail.
Je dois trouver un moyen sécurisé de lui transmettre la clé.
Pour cela, nous allons utiliser un cryptage asymétrique.
Il est assez facile de créer des clés asymétriques dans .NET mais il faut se méfier.
Si la clé publique est faite pour être communiquée, la clé privée, elle, ne doit jamais l'être.
Or le fournisseur RSA permet d'exporter assez simplement des informations de clé pouvant contenir la clé privée.
Ces méthodes, parfois appelées « clés mobiles » doivent être utilisées en toute connaissance de cause.
En effet, les clés mobiles sont extrêmement vulnérables si elles sont trop facilement accessibles.
Généralement, on ne les utilise que pour garder les valeurs de clés en cas d'incident sur le poste de travail.
Les clés mobiles ne devraient jamais être utilisées au sein d'une application.
Lorsqu'on crée une paire de clé, on utilise généralement un « magasin » (KeyContainer) qui gère le stockage et la protection des clés sur le poste de travail.
Je vais simuler tout cela au sein d'un même exemple.
Tout d'abord, je vais ajouter un constructeur à la classe CryptFile que nous avons vu précédemment, afin de pouvoir l'utiliser avec un couple clé, vecteur importé.
Ensuite, nous allons créer une classe gérant le chiffrement asymétrique.
Ce constructeur est particulier dans le sens qu'il permet au fournisseur RSACryptoServiceProvider de chiffrer mais pas de déchiffrer.
Reprenons donc, je veux échanger la recette de la garbure avec Neo.51 afin qu'il ne se nourrisse pas uniquement de hamburger ce qui n'est pas bon pour lui.
Cette recette sera cryptée afin que Maïté ne nous tombe pas dessus quand elle verra que je ne mets pas de confit.
Pour que Neo.51 puisse déchiffrer le fichier je dois lui faire parvenir la clé et le vecteur Triple DES.
Il doit donc créer un magasin et en exporter la clé publique.
En effet, j'ai besoin de sa clé publique pour chiffrer les informations.
Si je chiffre avec la mienne il ne pourra pas déchiffrer puisqu'il n'a pas ma clé privée.
On peut simuler cela avec le code suivant :
Il lui suffit alors de m'envoyer la chaîne obtenue dans un mail.
Je vais donc utiliser cette chaîne pour chiffrer mes informations de clé symétrique :
Je possède donc maintenant deux chaînes chiffrées correspondant respectivement à la clé et au vecteur Triple DES.
Je peux les mettre à mon tour dans un mail avec le fichier word précédemment chiffré.
Pour déchiffrer le fichier, Neo.51 devra seulement déchiffrer les informations de clé avec sa clé privé, puis le fichier.
Par exemple :
Cette technique est correctement sécurisée puisqu'à aucun moment la clé privée n'a été communiquée.
Cependant, il ne faut pas perdre de vue quelques considérations annexes.
Une des nombreuses légendes du Web veut que les chiffrements asymétriques soient la panacée universelle, toujours bien plus sûrs que leurs homologues symétriques.
Non seulement il n'en est rien, mais cela peut être fallacieux.
Les clés asymétriques telle que la paire RSA de l'exemple peuvent toujours être attaquées par reconstruction.
Ces attaques sont d'autant plus efficaces que l'on peut récupérer des informations de la clé publique pour conduire l'attaque.
Ainsi dans mon exemple, la clé RSA est plus vulnérable que le fichier crypté car elle n'est pas assez longue.
Rassurez vous quand même, l'attaque d'une clé RSA de 1024 bits ne se fait pas en dix minutes.
Je vous ai montré ici un exemple d'implémentation particulier, mais sachez que le Framework expose des classes RSAOAEPKeyExchangeFormatter et RSAOAEPKeyExchangeDeformatter qui gèrent parfaitement ce type de fonctionnement.
IV-C. Le hachage
Comme nous l'avons vu dans la partie théorique, le hachage est un chiffrement en sens unique utilisé principalement à des fins d'authentification.
Le premier exemple est le simple hachage de mot de passe.
Dans le plus simple des cas, imaginons une boite de dialogue pour entrer dans mon application, avec un mot de passe commun pour tous les utilisateurs :
Notez que je laisse sous forme de chaîne dans le code source la valeur hachée de comparaison.
Cela est sans importance puisqu'on ne peut pas remonter de la valeur hachée vers la valeur claire.
Cependant la sécurité ne repose que sur la robustesse du mot de passe.
Ce genre d'application est assez rare pour des raisons évidentes, on trouve plus souvent des applications multi utilisateur, chacun gérant son mot de passe.
Le stockage de ces mots de passe est généralement une base de données ou un fichier, comme c'est le cas pour l'authentification par formulaire ASP.NET.
Prenons un exemple comme celui-ci.
Les données de connexion se trouvent rangée sous la forme :
Autant le dire clairement, si vous hachez simplement les mots de passe tel que nous venons de le faire et s'il y a de nombreux utilisateurs enregistrés, vous êtes probablement loin de la sécurité.
L'efficacité des attaques par dictionnaire n'est plus à démontrer, pas plus que la faiblesse des mots de passe usuellement choisis.
Une méthode pour résoudre cela consiste à forcer le choix d'un mot de passe robuste.
Par exemple, le code suivant force la saisie d'un mot de passe robuste :
Dans ce type de contrainte, veillez à ne pas forcer le changement de mot de passe trop fréquemment ; la sécurité doit être présente et non pesante.
IV-C-1. Salage
Une autre technique pour se protéger contre les attaques par dictionnaire consiste à utiliser le salage (parfois nommé "hachage avec clé").
Le salage est l'adjonction d'un terme au terme à hacher pour obtenir un hachage résistant.
Selon les cas, le sel (c'est-à-dire le terme ajouté) doit être secret ou non.
Dans le cas de notre fichier, il doit être original puisqu'il ne peut être secret stricto sensu.
Pour la suite de notre exemple, imaginons le fichier App.config suivant :
Un exemple de vérification de mot de passe pourrait être par exemple :
Dans ce cas, j'ai placé mes termes de sel dans le fichier de configuration.
Cependant j'ai un peu complexifié l'opération en insérant le sel dans le mot de passe.
Ne sous estimez pas l'efficacité du salage.
Beaucoup de pirates en herbe se livrent à des attaques par dictionnaire sans même étudier le fonctionnement, le salage rendra le dictionnaire inopérant.
Même si dans mon exemple cela reste très simple, le salage permet d'augmenter fortement la sécurité.
Par exemple je pourrais multiplier les leurres en ajoutant dans le fichier des valeurs de salage que je n'utilise pas ; je pourrais également utiliser une composition de valeurs pour saler, etc…
En l'état, les hachages ne sont pas spécialement vulnérables, si ce n'est que rien n'empêche de mettre une autre valeur hachée à la place d'un hachage de mot de passe existant ce qui permettrait alors de franchir la sécurité (à condition de saler correctement).
IV-C-2. Contrôle d'intégrité
La problématique du contrôle d'intégrité des fichiers est toujours bien plus complexe qu'il y parait à première vue.
D'abord parce que si les données hachées sont susceptibles d'être modifiées, le hachage doit être relativement facile d'accès, ensuite parce qu'un des pièges les plus classique consiste à hacher des données prévisibles ce qui permet à un hacker de retrouver la logique du contrôle et donc de pouvoir modifier le fichier à sa guise.
Ne vous lancez pas non plus dans une surenchère du chiffrement, chiffrer le hachage ne change généralement rien dans une optique d'application installable partout car la clé de chiffrement sera aussi accessible que le hachage.
Continuons avec mon fichier de configuration précédent.
Je veux donc lui donner une identification afin qu'il ne soit pas possible de faire fonctionner l'application avec une version du fichier modifiée par ailleurs.
Le principe est toujours le même.
On récupère un certain nombre de données que l'on mixe ensemble avant de procéder au hachage.
On obtient alors une valeur, appelée parfois "empreinte de sécurité" ou "signature numérique" que l'on compare avec une même valeur précédemment stockée.
Il existe deux écoles pour le stockage de l'empreinte.
Le stockage interne : C'est-à-dire dans le fichier marqué.
Les deux méthodes sont sensiblement équivalentes, elles permettent l'utilisation de leurre (par exemple stocker plusieurs valeurs dans le registre alors qu'une seule est utilisée).
Regardons un exemple de contrôle en stockage interne, valeur que vous voyez dans le fichier de configuration dans la section "IntegriteSection".
J'ai utilisé ici une empreinte assez simple, il demeure évidemment possible de faire beaucoup plus complexe.
Attention toutefois à ce que vous utilisez, car selon les cas vous pouvez vous piéger vous-même.
Par exemple, j'ai une fois intégré dans l'empreinte la date de dernière modification du fichier.
Comme je faisais un stockage interne, j'enregistrais le fichier par la suite ce qui fait que mon contrôle d'intégrité plantait systématiquement.
IV-C-3. Signature numérique
La signature numérique consiste à identifier des données et leur provenance.
Fondamentalement, cela consiste à hacher des données puis à signer ce hachage avec une clé privé asymétrique.
On peut identifier ces données grâce à la valeur du hachage, et vérifier leur authenticité par contrôle de la signature à l'aide de la clé publique.
Par exemple, nous allons ajouter deux propriétés et deux fonctions à notre classe RSAPaire comme suit :
Les deux propriétés ont peu d'intérêt, penchons nous sur les fonctions.
Dans mon exemple, j'attends un nom de fichier que je vais pouvoir hacher.
C'est ce hachage que je vais signer à l'aide de l'objet RSAPKCS1SignatureFormatter.
La fonction VerifSignature est quand à elle le simple pendant de la fonction VerifySignature de l'objet RSAPKCS1SignatureDeformatter.
Maintenant je vais appeler ma méthode de chiffrement afin de crypter le fichier et de récupérer une signature.
Dans la première partie, je crée un objet RSA en récupérant les clés de mon magasin comme nous l'avons vu précédemment, je transmets alors le nom du fichier chiffré et je récupère les deux valeurs obtenues, c'est-à-dire le hachage et le hachage signé.
La vérification quant à elle a lieu grâce à un objet RSAPaire qui ne contient que la clé publique.
In fine j'obtiens donc un fichier chiffré, une valeur de hachage, une signature numérique.
Le fait que le fichier soit chiffré n'a aucune importance sur l'authentification.
Celui qui réceptionne ces trois éléments et qui connaît ma clé publique peut vérifier que :
Le hachage est bien correspondant à la signature
Donc le fichier est bien authentique.
Cette technique est extrêmement efficace, car tant que la clé privée est bien protégée, l'intégrité et l'authentification ne font aucun doute.
Cette technique d'identification peut être utilisée dans de nombreux scénarios.
IV-D. Clé dérivée
Le principe de la clé dérivée repose donc sur la génération d'une clé à partir d'un précurseur qui lui est censé être secret.
Prenons l'exemple le plus courant où le précurseur est un mot de passe.
Dans mon exemple, l'application utilise des Datasets chiffrés (sous forme XML) et nécessite donc un mot de passe pour pouvoir les lire.
On manipule les clés dérivées dans le Framework à l'aide de la classe PasswordDeriveBytes.
Pour dériver la clé, c'est-à-dire pour fournir une clé en partant d'un mot de passe, cette classe attend :
Un nombre d'itération
Le principe est assez simple, le mot de passe est ajouté au sel, et l'ensemble est haché le nombre de fois précisé selon l'algorithme défini.
Trois remarques cependant.
Il faut que la valeur de hachage soit suffisamment longue pour pouvoir alimenter une clé de la bonne dimension (dans notre cas 192 bits soit 24 caractères).
Par ailleurs il faut un mot de passe robuste afin de se garantir contre les attaques par dictionnaire puisque le sel est fourni par l'application.
On pourrait durcir la protection en allongeant la taille du sel et/ou en demandant un deuxième mot de passe comme valeur de salage.
En l'état mon code serait :
L'avantage évident de cette méthode est que la clé n'est stockée nulle part.
IV-E. Clé composite
Pour finir, nous allons voir une technique chère aux auteurs de shareware, la clé composite.
Ce type de clé utilise des précurseurs de deux sources, généralement l'éditeur et la machine.
Il existe de nombreux schémas possibles, je vais prendre un grand classique comme exemple.
Cependant il faut généralement faire preuve de plus d'imagination pour créer un système résistant.
Dans cette implémentation, le Shareware possède un bouton "enregistrer" qui génère une clé à expédier vers l'éditeur, celui-ci en
retour fournit une clé à donner au logiciel pour activer les fonctionnalités manquantes.
Le programme contient donc une clé publique dont l'éditeur possède la clé privée pour sécuriser le transfert.
La clé publique peut être stockée n'importe où car sa découverte n'a pas grande importance.
Pour extraire des informations de la machine, on utilise l'interop avec WMI, c'est-à-dire les classes de l'espace de nom Management.
Créons une classe qui va nous permettre de manipuler la clé composite.
Dans cet exemple, je récupère l'adresse MAC, le numéro de série du disque C et la référence du processeur.
J'ai implémenté par ailleurs deux fonctions, une qui génère une chaîne cryptée avec les informations et une autre qui vérifie la clé finale, autrement dit qui vérifie que le logiciel est bien enregistré.
La création de la chaîne cryptée pour l'envoi vers l'éditeur n'est pas bien complexe, elle reprend ce que nous avons vu pour le chiffrement asymétrique.
Une fois cette chaîne arrivée, l'éditeur va donc à son tour retourner une chaîne à l'utilisateur.
Que contient elle ?
Elle contient en fait deux choses :
Ces deux valeurs seront stockées dans le registre lors de l'enregistrement.
En regardant maintenant le code de vérification, on comprend mieux comment est composée une clé composite.
Dans cet exemple, la clé TripleDES est composée d'un tableau de 24 bytes définit par :
Le vecteur quand à lui est uniquement composé par des paramètres de la machine.
Dans cet exemple, la solution est assez vulnérable, mais je pourrais très facilement la durcir en :
V. Conclusion
Voila, nous avons vu les grandes familles de gestion des données secrètes.
N'oubliez pas que votre plus grand ennemi demeure dans les chaînes de caractères, et qu'une bonne défense doit être rigoureuse.
Ne vous alarmez pas trop, la plupart des attaquants sont assez mauvais et utilisent des logiciels d'attaque qu'ils n'ont pas écrit.
Ceux-là n'ont aucune chance de percer vos défenses si vous ne commettez pas d'erreur.
|
Copyright © 2005 J-M Rabilloud. 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.
Copyright © 2000-2012 - www.developpez.com