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'origine.
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 disparu, je vous en parle, car il s'agit d'une tentation fréquente lorsqu'on commence à se pencher sur les joies de la cryptographie. À 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ées 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és 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 longs, 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 accessibles. 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 exclut 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ères 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 susceptibles 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. Évaluation 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 stockez jamais un mot de passe ou une clé en clair dans le code.
De manière générale, évitez 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 hachez 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. À 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.
- Le chiffrement est une opération lourde, veillez à ne pas l'utiliser pour protéger des données non sensibles.
- La mise en place d'une cryptographie nécessitant des ressources externes à l'application demande d'utiliser une stratégie de déploiement spécifique. Celle-ci doit alors tenir compte des règles d'administration définies par le client.
- Dans le cadre des applications censées chiffrer / déchiffrer, on utilise généralement des clés dérivées puisqu'il n'est pas nécessaire de les stocker. Cependant si votre application utilise une gestion d'accès spécifique (à chaque utilisateur un mot de passe) les données chiffrées par un utilisateur ne pourront être lues par un autre ce qui n'est pas forcément bienvenu. Les applications à mot de passe commun contournent cela, mais au prix d'une plus grande vulnérabilité puisque le mot de passe doit être communiqué.
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. Exemples▲
IV-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 a 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 :
Public
Class
ChiffreEventArgs
Inherits
System.EventArgs
Private
m_Realize, m_Total As
Long
Public
Sub
New
(
ByVal
ValRealise As
Long
, ByVal
ValTotal As
Long
)
Me
.m_Realize
=
ValRealise
Me
.m_Total
=
ValTotal
End
Sub
Public
ReadOnly
Property
Total
(
) As
Long
Get
Return
Me
.m_Total
End
Get
End
Property
Public
ReadOnly
Property
Realise
(
) As
Long
Get
Return
Me
.m_Realize
End
Get
End
Property
End
Class
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 :
Imports
Microsoft.Win32
Imports
System.IO
Imports
System.Security
Imports
System.Security.Permissions
Imports
System.Security.Cryptography
<
Assembly
: RegistryPermission
(
SecurityAction.RequestMinimum
, All:=
"HKEY_CURRENT_USER\SOFTWARE\Secure"
)>
<
Assembly
: FileIOPermission
(
SecurityAction.RequestMinimum
, Unrestricted:=
True
)>
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 :
Public
Class
CryptFile
'Déclaration des délégués pour le suivi de l'opération asynchrone
Delegate
Sub
StartChiffreEventHandler
(
ByVal
sender As
Object
, ByVal
e As
ChiffreEventArgs)
Delegate
Sub
EndChiffreEventHandler
(
ByVal
sender As
Object
, ByVal
e As
ChiffreEventArgs)
Delegate
Sub
ProgressChiffreEventHandler
(
ByVal
sender As
Object
, ByVal
e As
ChiffreEventArgs)
'Déclaration des évènements pour le suivi de l'opération asynchrone
Public
Event
StartChiffreEvent As
StartChiffreEventHandler
Public
Event
EndChiffreEvent As
EndChiffreEventHandler
Public
Event
ProgressChiffreEvent As
ProgressChiffreEventHandler
Private
RegApp As
RegistryKey
Private
STDES As
TripleDESCryptoServiceProvider
Public
Sub
New
(
)
'vérification du registre
RegApp =
Registry.CurrentUser.OpenSubKey
(
"Software\\Secure\\chPers"
, True
)
STDES =
New
TripleDESCryptoServiceProvider
If
RegApp Is
Nothing
Then
'les clés de registre n'existent pas
With
STDES 'je génère une clé de chiffrement et un vecteur
.GenerateIV
(
)
.GenerateKey
(
)
End
With
'je les mets dans le registre
RegApp =
Registry.CurrentUser.CreateSubKey
(
"Software\\Secure\\chPers"
)
RegApp.SetValue
(
"cle"
, Convert.ToBase64String
(
STDES.Key
))
RegApp.SetValue
(
"vecteur"
, Convert.ToBase64String
(
STDES.IV
))
Else
'les clés de registre existent
With
STDES 'je récupère les valeurs du registre
.Key
=
Convert.FromBase64String
(
CType
(
RegApp.GetValue
(
"cle"
), String
))
.IV
=
Convert.FromBase64String
(
CType
(
RegApp.GetValue
(
"vecteur"
), String
))
End
With
End
If
End
Sub
Public
Sub
chiffrement
(
ByVal
Fichier As
String
)
If
File.Exists
(
Fichier) Then
Dim
FichierSource As
New
FileStream
(
Fichier, FileMode.Open
, FileAccess.Read
)
Dim
FichierCible As
New
FileStream
(
Path.ChangeExtension
(
Fichier, "cry"
), _
FileMode.Create
, FileAccess.Write
)
Dim
ChiffreFlux As
New
CryptoStream
(
FichierCible, STDES.CreateEncryptor
, CryptoStreamMode.Write
)
FichierCible.SetLength
(
0
)
Dim
bin
(
100
) As
Byte
Dim
LongTotale As
Long
=
FichierSource.Length
RaiseEvent
StartChiffreEvent
(
Me
, New
ChiffreEventArgs
(
0
, LongTotale))
Dim
LongEffectue As
Long
=
0
Dim
BlocLen As
Integer
'traitement
While
LongEffectue <
LongTotale
BlocLen =
FichierSource.Read
(
bin, 0
, 100
)
ChiffreFlux.Write
(
bin, 0
, BlocLen)
LongEffectue +=
BlocLen
RaiseEvent
ProgressChiffreEvent
(
Me
, New
ChiffreEventArgs
(
LongEffectue, LongTotale))
End
While
RaiseEvent
EndChiffreEvent
(
Me
, New
ChiffreEventArgs
(
LongEffectue, LongTotale))
ChiffreFlux.Close
(
)
FichierSource.Close
(
)
FichierCible.Close
(
)
Else
Throw
New
FileNotFoundException
(
"Le fichier n'a pas été trouvé"
, Fichier)
End
If
End
Sub
Public
Sub
Dechiffrement
(
ByVal
Fichier As
String
, ByVal
ExtensionCible As
String
)
If
File.Exists
(
Fichier) Then
Dim
FichierSource As
New
FileStream
(
Fichier, FileMode.Open
, FileAccess.Read
)
Dim
FichierCible As
New
FileStream
(
Path.ChangeExtension
(
Fichier, ExtensionCible), _
FileMode.Create
, FileAccess.Write
)
Dim
ChiffreFlux As
New
CryptoStream
(
FichierCible, STDES.CreateDecryptor
, CryptoStreamMode.Write
)
FichierCible.SetLength
(
0
)
Dim
bin
(
100
) As
Byte
Dim
LongTotale As
Long
=
FichierSource.Length
RaiseEvent
StartChiffreEvent
(
Me
, New
ChiffreEventArgs
(
0
, LongTotale))
Dim
LongEffectue As
Long
=
0
Dim
BlocLen As
Integer
'traitement
While
LongEffectue <
LongTotale
BlocLen =
FichierSource.Read
(
bin, 0
, 100
)
ChiffreFlux.Write
(
bin, 0
, BlocLen)
LongEffectue +=
BlocLen
RaiseEvent
ProgressChiffreEvent
(
Me
, New
ChiffreEventArgs
(
LongEffectue, LongTotale))
End
While
RaiseEvent
EndChiffreEvent
(
Me
, New
ChiffreEventArgs
(
LongEffectue, LongTotale))
ChiffreFlux.Close
(
)
FichierSource.Close
(
)
FichierCible.Close
(
)
Else
Throw
New
FileNotFoundException
(
"Le fichier n'a pas été trouvé"
, Fichier)
End
If
End
Sub
End
Class
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 nombres aléatoires 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 :
Dim
ChiffreFlux As
New
CryptoStream
(
FichierClair, STDES.CreateDecryptor
, CryptoStreamMode.Write
)
Et que je dois passer l'extension du fichier de sortie.
Un code de consommation de ma classe pourrait être :
Private
Sub
cmdCryptFile_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) _
Handles
cmdCryptFile.Click
Dim
MonFichier As
String
With
Me
.OpenFileDialog1
.CheckFileExists
=
True
.DefaultExt
=
"doc"
.Filter
=
"Documents Word (*.doc)|*.doc|Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
.FilterIndex
=
1
.Multiselect
=
False
.Title
=
"Selection du fichier à chiffrer"
If
.ShowDialog
(
Me
) =
DialogResult.OK
Then
MonFichier =
.FileName
End
If
End
With
Try
Dim
objChiffre As
New
Crypto.CryptFile
AddHandler
objChiffre.StartChiffreEvent
, AddressOf
Operation_StartChiffreEvent
AddHandler
objChiffre.ProgressChiffreEvent
, AddressOf
Operation_ProgressChiffreEvent
AddHandler
objChiffre.EndChiffreEvent
, AddressOf
Operation_EndChiffreEvent
objChiffre.chiffrement
(
MonFichier)
Catch
ex As
Exception
MsgBox
(
"Erreur lors de l'opération"
, MsgBoxStyle.Critical
And
MsgBoxStyle.OKOnly
, "ERREUR"
)
End
Try
End
Sub
Private
Sub
Operation_StartChiffreEvent
(
ByVal
sender As
Object
, ByVal
e As
Crypto.ChiffreEventArgs
)
With
Me
.lblChiffrement
.Text
=
"Opération en cours..."
.Visible
=
True
End
With
Me
.pgbChiffrement.Visible
=
True
End
Sub
Private
Sub
Operation_ProgressChiffreEvent
(
ByVal
sender As
Object
, ByVal
e As
Crypto.ChiffreEventArgs
)
Me
.pgbChiffrement.Value
=
CInt
(
e.Realise
/
e.Total
*
100
)
End
Sub
Private
Sub
Operation_EndChiffreEvent
(
ByVal
sender As
Object
, ByVal
e As
Crypto.ChiffreEventArgs
)
Me
.lblChiffrement.Visible
=
False
Me
.pgbChiffrement.Visible
=
False
End
Sub
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és, 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é.
Public
Sub
New
(
ByVal
Cle
(
) As
Byte
, ByVal
Vecteur
(
) As
Byte
)
'création d'un service avec des valeurs externes
STDES =
New
TripleDESCryptoServiceProvider
With
STDES
.Key
=
Cle
.IV
=
Vecteur
End
With
End
Sub
Ensuite, nous allons créer une classe gérant le chiffrement asymétrique.
Public
Class
RSAPaire
Private
m_Magasin As
String
Private
m_RSA As
RSACryptoServiceProvider
Public
Sub
New
(
ByVal
NomMagasin As
String
, ByVal
CreerMagasin As
Boolean
)
m_Magasin =
NomMagasin
If
CreerMagasin Then
CreateMagasin
(
) Else
RecupMagasin
(
)
End
Sub
Public
Sub
New
(
ByVal
ImportString As
String
)
m_RSA =
New
System.Security.Cryptography.RSACryptoServiceProvider
m_RSA.FromXmlString
(
ImportString)
End
Sub
Private
Sub
CreateMagasin
(
)
Dim
cspParam As
CspParameters =
New
CspParameters
cspParam.KeyContainerName
=
m_Magasin
m_RSA =
New
System.Security.Cryptography.RSACryptoServiceProvider
(
cspParam)
m_RSA.PersistKeyInCsp
=
True
End
Sub
Private
Sub
RecupMagasin
(
)
Dim
cspParam As
CspParameters =
New
CspParameters
cspParam.KeyContainerName
=
m_Magasin
m_RSA =
New
System.Security.Cryptography.RSACryptoServiceProvider
(
cspParam)
End
Sub
Public
Function
ExportPublicKey
(
) As
String
Return
m_RSA.ToXmlString
(
False
)
End
Function
Public
Function
CryptData
(
ByVal
DonneeClaire
(
) As
Byte
) As
Byte
(
)
Return
m_RSA.Encrypt
(
DonneeClaire, False
)
End
Function
Public
Function
DecryptData
(
ByVal
DonneeChiffree
(
) As
Byte
) As
Byte
(
)
Return
m_RSA.Decrypt
(
DonneeChiffree, False
)
End
Function
End
Class
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 :
Private
Sub
cmdExportPubKey_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) _
Handles
cmdExportPubKey.Click
Dim
objRsa As
New
Crypto.RSAPaire
(
"neo"
, True
)
Me
.txtPublicKey.Text
=
objRsa.ExportPublicKey
End
Sub
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 :
Private
Sub
cmdRSACrypt_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) _
Handles
cmdRSACrypt.Click
Dim
objRsa As
New
Crypto.RSAPaire
(
Me
.txtPublicKey.Text
)
Dim
RegApp As
RegistryKey =
Registry.CurrentUser.OpenSubKey
(
"Software\\Secure\\chPers"
, True
)
Dim
CryptCleSym
(
) As
Byte
Dim
CryptVecSym
(
) As
Byte
CryptCleSym =
objRsa.CryptData
(
Convert.FromBase64String
(
CType
(
RegApp.GetValue
(
"cle"
), String
)))
CryptVecSym =
objRsa.CryptData
(
Convert.FromBase64String
(
CType
(
RegApp.GetValue
(
"vecteur"
), String
)))
Me
.txtCleSymCry.Text
=
Convert.ToBase64String
(
CryptCleSym)
Me
.txtVecSymCry.Text
=
Convert.ToBase64String
(
CryptVecSym)
End
Sub
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ée, puis le fichier. Par exemple :
Private
Sub
cmdDecryptKeyAndFile_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) _
Handles
cmdDecryptKeyAndFile.Click
Dim
objRsa As
New
Crypto.RSAPaire
(
"neo"
, False
)
Dim
DecryptCleSym
(
) As
Byte
Dim
DecryptVecSym
(
) As
Byte
DecryptCleSym =
objRsa.DecryptData
(
Convert.FromBase64String
(
Me
.txtCleSymCry.Text
))
DecryptVecSym =
objRsa.DecryptData
(
Convert.FromBase64String
(
Me
.txtVecSymCry.Text
))
Dim
MonFichier As
String
With
Me
.OpenFileDialog1
.CheckFileExists
=
True
.DefaultExt
=
"cry"
.Filter
=
"Fichiers chiffrés (*.cry)|*.cry"
.FilterIndex
=
1
.Multiselect
=
False
.Title
=
"Selection du fichier à déchiffrer"
If
.ShowDialog
(
Me
) =
DialogResult.OK
Then
MonFichier =
.FileName
End
If
End
With
Try
Dim
objChiffre As
New
Crypto.CryptFile
(
DecryptCleSym, DecryptVecSym)
AddHandler
objChiffre.StartChiffreEvent
, AddressOf
Operation_StartChiffreEvent
AddHandler
objChiffre.ProgressChiffreEvent
, AddressOf
Operation_ProgressChiffreEvent
AddHandler
objChiffre.EndChiffreEvent
, AddressOf
Operation_EndChiffreEvent
objChiffre.Dechiffrement
(
MonFichier, "doc"
)
Catch
ex As
Exception
MsgBox
(
"Erreur lors de l'opération"
, MsgBoxStyle.Critical
And
MsgBoxStyle.OKOnly
, "ERREUR"
)
End
Try
End
Sub
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, 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 telles 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 :
Private
Sub
cmdOK_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) Handles
cmdOK.Click
Dim
SHA As
New
SHA256Managed
Dim
UniEnc As
New
System.Text.UnicodeEncoding
Dim
bitPass
(
) As
Byte
=
UniEnc.GetBytes
(
Me
.txtPassword.Text
)
If
Convert.ToBase64String
(
SHA.ComputeHash
(
bitPass)) =
"ew9LkdvpuYaurqPCDV3rWGZ1vTaD3GfN6b441cA0TNQ="
Then
Me
.DialogResult
=
DialogResult.OK
Else
Me
.DialogResult
=
DialogResult.Cancel
End
If
End
Sub
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-utilisateurs, 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ées sous la forme :
…
<
credentials>
<
User Name=
"Jean-Marc"
Password=
"ew9LkdvpuYaurqPCDV3rWGZ1vTaD3GfN6b441cA0TNQ="
/>
<
User Name=
"Morpheus"
Password=
"ew9LkdvpuYaurqPCDV3rWGZ1vTaD3GfN6b441cA0TNQ="
/>
<
User Name=
"Neo.51"
Password=
"ew9LkdvpuYaurqPCDV3rWGZ1vTaD3GfN6b441cA0TNQ="
/>
…
</
credentials >
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 :
<
Flags
(
)>
Private
Enum
Conforme
HasLower =
1
HasUpper =
2
HasNumber =
4
HasOther =
8
End
Enum
Private
Sub
txtNewPass_Validating
(
ByVal
sender As
Object
, ByVal
e As
System.ComponentModel.CancelEventArgs
) _
Handles
txtNewPass.Validating
If
Me
.txtNewPass.Text.Length
<
12
Then
e.Cancel
=
True
Me
.ErrorProvider1.SetError
(
Me
.txtNewPass
, "Le mot de passe doit contenir au moins 12 caractères"
)
End
If
Dim
Verif As
New
Conforme
Dim
tabChar
(
) As
Char
=
Me
.txtNewPass.Text.ToCharArray
For
cmpt As
Integer
=
tabChar.GetLowerBound
(
0
) To
tabChar.GetUpperBound
(
0
)
Select
Case
True
Case
Char
.IsPunctuation
(
tabChar
(
cmpt)) OrElse
Char
.IsSymbol
(
tabChar
(
cmpt))
Verif =
Verif Or
Conforme.HasOther
Case
Char
.IsLower
(
tabChar
(
cmpt))
Verif =
Verif Or
Conforme.HasLower
Case
Char
.IsUpper
(
tabChar
(
cmpt))
Verif =
Verif Or
Conforme.HasUpper
Case
Char
.IsNumber
(
tabChar
(
cmpt))
Verif =
Verif Or
Conforme.HasNumber
End
Select
Next
If
Verif <
15
Then
e.Cancel
=
True
Me
.ErrorProvider1.SetError
(
Me
.txtNewPass
, _
"Le mot de passe doit contenir au moins une majuscule, une minuscule un chiffre et un symbole"
)
End
If
End
Sub
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 :
<
?xml version=
"1.0"
encoding=
"utf-8"
?>
<
configuration>
<
configSections>
<
sectionGroup name=
"SecurityGroup"
>
<
section name=
"AccessSection"
type
=
"System.Configuration.NameValueSectionHandler"
/>
<
section name=
"IntegriteSection"
type
=
"System.Configuration.SingleTagSectionHandler"
/>
<
section name=
"SaltSection"
type
=
"System.Configuration.SingleTagSectionHandler"
/>
</
sectionGroup>
</
configSections>
<
SecurityGroup>
<
AccessSection>
<
add key=
"bidou"
value=
"pua2jaEGDfiz/1Co3uDWSigjl/jzwMVtU9LJzYED+O4="
/>
<
add key=
"neo.51"
value=
"1pxvek/zYndThdRUtZDtWtta9qIZzf1ELgsLb6qE+k8="
/>
<
add key=
"morpheus"
value=
"lHUgdYfqWnThea9P3uBkaE8q7TuQ1MC1g4rJH/fHRLQ="
/>
</
AccessSection>
<
IntegriteSection Setting1 =
"V+HG0hhMdCIctvSFD5kqfE1vCZX6FjprtHuoxoTEppo="
/>
<
SaltSection Setting1 =
"VV0+*àBD"
Setting2 =
"c)4Dé}yB"
Setting3 =
"w5jnpFgP"
/>
</
SecurityGroup>
</
configuration>
Un exemple de vérification de mot de passe pourrait être par exemple :
Private
Sub
cmdOK_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) Handles
cmdOK.Click
Dim
Salage As
IDictionary
Dim
collSec As
NameValueCollection
Dim
SHA As
New
SHA256Managed
Dim
UniEnc As
New
System.Text.UnicodeEncoding
collSec =
CType
(
Configuration.ConfigurationSettings.GetConfig
(
"SecurityGroup/AccessSection"
), NameValueCollection)
Salage =
CType
(
Configuration.ConfigurationSettings.GetConfig
(
"SecurityGroup/SaltSection"
), IDictionary)
For
Elem As
Integer
=
0
To
collSec.Count
-
1
If
collSec.GetKey
(
Elem).CompareTo
(
Me
.txtUserName.Text
) =
0
Then
Dim
bitPass
(
) As
Byte
=
_
UniEnc.GetBytes
(
Me
.txtPassword.Text.Insert
(
3
, Salage.Item
(
"Setting"
&
(
1
+
Elem).ToString
).ToString
))
If
Convert.ToBase64String
(
SHA.ComputeHash
(
bitPass)).CompareTo
(
collSec.Get
(
Elem)) =
0
Then
Me
.DialogResult
=
DialogResult.OK
Exit
Sub
End
If
End
If
Next
Me
.DialogResult
=
DialogResult.Cancel
End
Sub
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 classiques 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é.
- Le stockage externe : c'est-à-dire dans le registre ou dans un autre fichier.
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 ».
Private
Sub
frmPass_Activated
(
ByVal
sender As
Object
, ByVal
e As
System.EventArgs
) Handles
MyBase
.Activated
Dim
Salage As
IDictionary
Dim
collSec As
NameValueCollection
Dim
SHA As
New
SHA256Managed
Dim
UniEnc As
New
System.Text.UnicodeEncoding
Dim
TabString
(
) As
String
Dim
Integrite As
String
collSec =
CType
(
Configuration.ConfigurationSettings.GetConfig
(
"SecurityGroup/AccessSection"
), NameValueCollection)
Salage =
CType
(
Configuration.ConfigurationSettings.GetConfig
(
"SecurityGroup/SaltSection"
), IDictionary)
ReDim
TabString
(
collSec.Count
*
3
)
For
cmpt As
Integer
=
0
To
collSec.Count
-
1
TabString
(
cmpt *
3
) =
Salage.Item
(
"Setting"
&
(
1
+
cmpt).ToString
).ToString
TabString
(
cmpt *
3
+
1
) =
collSec.GetValues
(
cmpt).GetValue
(
0
).ToString
TabString
(
cmpt *
3
+
2
) =
collSec.GetKey
(
cmpt)
Next
TabString
(
TabString.GetUpperBound
(
0
)) =
System.Reflection.Assembly.GetExecutingAssembly.ImageRuntimeVersion
Integrite =
CType
(
Configuration.ConfigurationSettings.GetConfig
(
"SecurityGroup/IntegriteSection"
), _
IDictionary).Item
(
"Setting1"
).ToString
Dim
bitPass
(
) As
Byte
=
UniEnc.GetBytes
(
String
.Join
(
Me
.Height.ToString
, TabString))
If
Convert.ToBase64String
(
SHA.ComputeHash
(
bitPass)).CompareTo
(
Integrite) <>
0
Then
MsgBox
(
"Échec du contrôle d'intégrité"
, MsgBoxStyle.Critical
And
MsgBoxStyle.OKOnly
)
Me
.DialogResult
=
DialogResult.Cancel
End
If
End
Sub
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ée 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 :
Public
ReadOnly
Property
ValeurHachage
(
) As
Byte
(
)
Get
Return
Me
.m_ValHash
End
Get
End
Property
Public
ReadOnly
Property
Signature
(
) As
Byte
(
)
Get
Return
Me
.m_ValSign
End
Get
End
Property
Public
Sub
CreeSignature
(
ByVal
Fichier As
String
)
Dim
RSASignature As
New
RSAPKCS1SignatureFormatter
(
m_RSA)
Dim
Flux As
New
FileStream
(
Fichier, FileMode.Open
, FileAccess.Read
)
Dim
SHA As
New
SHA1CryptoServiceProvider
Me
.m_ValHash
=
SHA.ComputeHash
(
Flux)
With
RSASignature
.SetHashAlgorithm
(
"SHA1"
)
Me
.m_ValSign
=
.CreateSignature
(
Me
.m_ValHash
)
End
With
End
Sub
Public
Function
VerifSignature
(
ByVal
ValeurHachee As
String
, ByVal
LaSignature As
String
) As
Boolean
Dim
RSASignature As
New
RSAPKCS1SignatureDeformatter
(
m_RSA)
RSASignature.SetHashAlgorithm
(
"SHA1"
)
Return
RSASignature.VerifySignature
(
Convert.FromBase64String
(
ValeurHachee), _
Convert.FromBase64String
(
LaSignature))
End
Function
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 quant à 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.
'creation de la signature
Dim
objRsa1 As
New
Crypto.RSAPaire
(
"neo"
, True
)
objRsa1.CreeSignature
(
FichierCrypte)
Me
.txtHashFile.Text
=
Convert.ToBase64String
(
objRsa1.ValeurHachage
)
Me
.txtNumSign.Text
=
Convert.ToBase64String
(
objRsa1.Signature
)
'vérification de la signature
Dim
PubKey As
String
=
objRsa1.ExportPublicKey
Dim
objrsa2 As
New
Crypto.RSAPaire
(
PubKey)
If
objrsa2.VerifSignature
(
Me
.txtHashFile.Text
, Me
.txtNumSign.Text
) Then
MsgBox
(
"Signature valide"
)
Else
MsgBox
(
"Signature non valide"
)
End
If
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 ;
- le hachage est bien correspondant au fichier.
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érations ;
- un algorithme de hachage ;
- une valeur de salage ;
- le mot de passe.
Un nombre d'itérations
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 :
Private
Sub
cmdAccCryptData_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) _
Handles
cmdAccCryptData.Click
Dim
CryptXML As
New
FileStream
(
Application.StartupPath
&
"\biblio.cry"
, FileMode.Open
)
Dim
pdb As
New
Security.Cryptography.PasswordDeriveBytes
(
Me
.txtPass.Text
, _
New
Byte
(
7
) {21
, 10
, 16
, 21
, 32
, 2
, 78
, 9
})
With
pdb
.HashName
=
"SHA256"
.IterationCount
=
722
End
With
Dim
Prov3DES As
New
TripleDESCryptoServiceProvider
With
Prov3DES
.Key
=
pdb.GetBytes
(
24
)
.IV
=
pdb.GetBytes
(
8
)
End
With
Dim
CryptFlux As
New
CryptoStream
(
CryptXML, Prov3DES.CreateDecryptor
, CryptoStreamMode.Read
)
Dim
MonDs As
New
DataSet
MonDs.ReadXml
(
CryptFlux, XmlReadMode.ReadSchema
)
Me
.DataGrid1.DataSource
=
MonDs
End
Sub
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é à envoyer à l'éditeur, celui-ci renvoyant 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.
Public
Class
ExtractInfo
Private
m_info1, m_info2, m_info3 As
String
Public
Sub
New
(
)
m_info1 =
GetMacAddress
(
)
m_info2 =
GetDiskSerial
(
)
m_info3 =
GetProcessorRef
(
)
End
Sub
Private
Function
GetMacAddress
(
) As
String
Dim
NetClass As
New
System.Management.ManagementClass
(
"Win32_NetworkAdapterConfiguration"
)
Dim
Nets As
ManagementObjectCollection =
NetClass.GetInstances
Dim
Net As
ManagementObject
For
Each
Net In
Nets
GetMacAddress =
Net
(
"MacAddress"
).ToString
Exit
For
Next
Net
End
Function
Private
Function
GetDiskSerial
(
) As
String
Dim
diskClass As
New
System.Management.ManagementClass
(
"Win32_LogicalDisk"
)
Dim
disks As
System.Management.ManagementObjectCollection
=
diskClass.GetInstances
(
)
Dim
disk As
System.Management.ManagementObject
For
Each
disk In
disks
If
CStr
(
disk
(
"Name"
)) =
"C:"
Then
GetDiskSerial =
disk
(
"VolumeSerialNumber"
).ToString
End
If
Next
disk
End
Function
Private
Function
GetProcessorRef
(
) As
String
Dim
ProcClass As
New
System.Management.ManagementClass
(
"Win32_Processor"
)
Dim
Procs As
ManagementObjectCollection =
ProcClass.GetInstances
Dim
Proc As
ManagementObject
For
Each
Proc In
Procs
GetProcessorRef =
Proc
(
"ProcessorId"
).ToString
Exit
For
Next
Proc
End
Function
Public
Function
EmissionCle
(
ByVal
Pbk As
String
) As
Byte
(
)
Dim
mRSA As
New
RSACryptoServiceProvider
mRSA.FromXmlString
(
Pbk)
Dim
UEnc As
New
System.Text.UnicodeEncoding
Dim
strTest As
String
=
Me
.m_info1
&
"|"
&
Me
.m_info2
&
"|"
&
Me
.m_info3
Dim
TabByte
(
) As
Byte
=
UEnc.GetBytes
(
strTest)
Return
mRSA.Encrypt
(
TabByte, False
)
End
Function
Public
Function
VerifyKey
(
ByVal
Test As
String
) As
Boolean
Dim
cmpt As
Integer
Dim
TabByte
(
23
), rndByte
(
) As
Byte
, Verif
(
) As
Byte
Dim
dEnc As
New
System.Text.ASCIIEncoding
dEnc.GetBytes
(
m_info1.Substring
(
4
, 2
)).CopyTo
(
TabByte, 0
)
ReDim
Verif
(
1
)
dEnc.GetBytes
(
m_info1.Substring
(
6
, 2
)).CopyTo
(
Verif, 0
)
dEnc.GetBytes
(
m_info2.Substring
(
0
, 5
)).CopyTo
(
TabByte, 2
)
cmpt =
1
+
m_info2.Length
-
6
ReDim
Preserve
Verif
(
cmpt)
dEnc.GetBytes
(
m_info2.Substring
(
6
)).CopyTo
(
Verif, 2
)
dEnc.GetBytes
(
m_info3.Substring
(
0
, 5
)).CopyTo
(
TabByte, 8
)
ReDim
Preserve
Verif
(
cmpt +
m_info3.Length
-
6
)
dEnc.GetBytes
(
m_info3.Substring
(
6
)).CopyTo
(
Verif, cmpt +
1
)
rndByte =
Convert.FromBase64String
(
_
Registry.CurrentUser.OpenSubKey
(
"Software\\Secure\\Share"
).GetValue
(
"part"
).ToString
)
rndByte.CopyTo
(
TabByte, 14
)
dEnc.GetBytes
(
m_info1.Substring
(
8
, 2
)).CopyTo
(
TabByte, 22
)
Dim
m3DES As
New
TripleDESCryptoServiceProvider
m3DES.Key
=
TabByte
m3DES.IV
=
New
Byte
(
7
) {Verif
(
0
), Verif
(
1
), Verif
(
2
), Verif
(
3
), Verif
(
4
), Verif
(
5
), Verif
(
6
), Verif
(
7
)}
Dim
ms As
New
IO.MemoryStream
Dim
CryptFlux As
New
CryptoStream
(
ms, m3DES.CreateEncryptor
, CryptoStreamMode.Write
)
CryptFlux.Write
(
System.Text.UnicodeEncoding.Unicode.GetBytes
(
Test), 0
, Test.Length
)
Dim
PassByte As
Byte
(
) =
ms.ToArray
If
String
.Compare
(
Registry.CurrentUser.OpenSubKey
(
"Software\\Secure\\Share"
) _
.GetValue
(
"ver"
).ToString
, Convert.ToBase64String
(
PassByte)) <>
0
Then
Return
False
Else
Return
True
End
If
End
Function
End
Class
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 :
- le mot de vérification codé ;
- le complément de la clé Triple DES.
Le mot de vérification codé
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éfini par :
- 2 octets donnés par l'adresse MAC ;
- 10 octets donnés par l'éditeur ;
- 5 octets donnés par la référence processeur ;
- 5 octets donnés par le numéro de série du disque C ;
- 2 octets donnés par l'adresse MAC.
Le vecteur quant à 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.
V. Conclusion▲
Voilà, 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 écrits. Ceux-là n'ont aucune chance de percer vos défenses si vous ne commettez pas d'erreur.