Introduction▲
Clé publique et clé privée ?▲
On parle de clé publique et de clé privée pour les algorithmes de chiffrement asymétrique. Ces clés permettent de crypter et de décrypter un message, elles seules permettent cette action. La clé publique ne doit pas être gardée secrète et peut être diffusée sans que la confidentialité du message ne soit affectée, par contre, la clé privée, comme son nom l'indique doit rester privée, sa divulgation rendrait inefficace le cryptage, car elle permet de décrypter le message. Pour résumer, la clé publique sert à coder un message, alors que la clé privée sert à le décoder. Mais attention, les termes « publique » et « privée » sont subjectifs, en effet, chacune des clés peut remplir ce rôle, c'est juste que l'on attribue un rôle à chacune, rôle qui bien évidemment n'est plus interchangeable par la suite. La longueur des clés est exprimée en bits, elle va généralement de 128 à 1024 bits suivant le type de clé et d'algorithme. Seuls les algorithmes asymétriques utilisent un jeu de deux clés, les algorithmes symétriques eux, n'utilisent qu'une seule clé.
Dans le contexte de la signature d'une assembly, on la signe au préalable avec la clé privée uniquement. La clé publique quant à elle nous servira à contrôler la validité de cette signature.
Qu'est-ce qu'une signature ?▲
Une signature électronique permet principalement de garantir que le contenu d'un document n'a pas été modifié après qu'il a été signé. Appliquée aux assembly .Net, elle garantit l'intégrité du contenu d'origine. Le procédé de signature d'assembly prend donc tout son sens en termes de sécurité et de garantie d'authenticité d'un programme.
I. Qu'est-ce que la signature d'assembly ?▲
La signature d'une assembly .Net comporte deux composantes fondamentales : hashage de son contenu et un nom fort. Voyons sans plus tarder à quoi correspondent ces deux concepts.
I-A. Strong Names ou Nom Fort▲
Un nom fort, ou « Strong Names » en anglais (d'ailleurs au cours de cet article nous utiliserons la version française et anglaise sans distinction) correspond à un ensemble d'éléments qui permettent d'identifier avec certitude une assembly donnée. Un nom fort est donc composé des éléments suivants :
- une version ;
- un nom simple ;
- une clé publique ;
- une clé privée ;
- ainsi qu'une culture et une architecture CPU (x86 / x64) optionnelles.
Une version est simplement une séquence de quatre chiffres agencés de la sorte :
numéro majeur de version - numéro mineur de version - numéro de « build » - numéro de révision, cela peut être par exemple « 2.7.3.6 ».
Un nom simple, il s'agit du nom que le développeur a donné à l'assembly, généralement c'est le nom du fichier sans son extension (.dll, .exe…), il faut donc bien faire attention à ne pas mélanger cela avec un Namespace ou à un nom de classe.
Une clé publique, qui va permettre à l'environnement .Net de vérifier la signature de l'assembly signée avec une clé privée. En effet, une et une seule clé publique correspond à une clé privée. La clé publique sert donc de référent. La clé publique peut être communiquée sans risque. Cette clé publique est toujours intégrée à une assembly signée, et est assez facile à récupérer au sein d'une assembly pour tout développeur .Net aguerri.
Une clé privée, cette clé est celle qui a servi à signer l'assembly, sa valeur doit rester secrète, en effet elle seule garantit l'authenticité et la non-corruption du contenu d'une assembly. Sa gestion est donc sensible, mais nous y reviendrons plus tard.
Une culture, pour simplifier il s'agit de la langue et des réglages régionaux, cela peut être US, FR ou autre…
Une architecture de processeur, information en relation avec la machine de destination prévue pour l'assembly, à ce jour Intel (par exemple x86, x64 ou même Itanium).
Un nom fort est donc un ensemble de procédés et de valeurs qui garantissent l'authenticité et la non-corruption d'une assembly. Un nom fort est donc un composant de la technologie .Net qui peut s'avérer extrêmement intéressant dans la gestion de la sécurité. De plus, la signature d'assembly est indispensable pour la gestion du versionning d'assembly ou encore pour placer une assembly dans le GAC (Global Assembly Cache).
I-B. Hashage de l'assembly▲
En l'état, ce que nous savons de la signature d'une assembly est le fait que l'on utilise un certain nombre de paramètres pour identifier de manière unique une assembly, notamment par le biais d'une clé publique, ou plutôt d'un jeton correspondant à cette clé publique qui est intégré directement dans l'assembly. Cependant, il manque un élément important de la signature, en effet jusqu'ici rien ne prouve qu'une assembly signée n'a pas été modifiée depuis qu'elle a été compilée par le développeur. Pour ce faire, un autre procédé va entrer en jeu, il s'agit du hashage. Voyons cela plus en détail.
Tout d'abord le hashage est un procédé mathématique reposant sur des algorithmes permettant de créer une sorte de condensé unique d'un texte ou du moins d'un ensemble de caractères, condensé à partir duquel, il sera absolument impossible de retrouver le texte original, c'est le propre du hashage d'être unidirectionnel et définitif. Pour résumer, après hashage d'une chaîne de caractères, on obtient un condensé unique et définitif appelé hash, permettant d'identifier de manière unique et certaine un texte, qui dans notre cas sera le code IL (Intermediate Language) d'une assembly ainsi que certains paramètres de son « manifest ».
Ce hash est calculé lors de la compilation de notre code (VB.Net, C# ou autre…) en IL, bien évidemment ce hash n'est calculé que si l'on a indiqué au compilateur que l'on souhaitait signer notre assembly. Après calcul de ce hash, celui-ci est encrypté à l'aide de la clé privée, et inscrit dans notre assembly. Dès lors, je pense que vous pouvez d'ores et déjà entrevoir la suite… La clé publique qui a été inscrite dans le manifeste de notre assembly, c'est-à-dire dans les métadonnées, va servir à décoder le hash qui y est stocké, et sera comparé avec le résultat du hashage obtenu par l'environnement d'exécution de .Net lors du chargement de l'assembly. Si le hash décrypté par la clé publique contenue dans l'assembly est semblable au hash calculé par l'environnement d'exécution, alors l'assembly peut être chargée, dans le cas contraire une exception est levée, et le chargement annulé.
Ainsi grâce à ce procédé de hashage, l'on peut déterminer avec certitude si l'assembly a été modifiée ou non depuis qu'elle a été signée par le développeur. Cette certitude repose bien entendu sur la non-divulgation de la clé privée qui a servi au cryptage du hash obtenu au moment de la signature.
Au cours de cette partie, nous avons pu voir que la signature d'une assembly repose sur un Strong Name qui est un ensemble d'éléments, ainsi que sur le calcul d'un hash de son contenu. La conjugaison des deux va permettre d'identifier avec certitude et de manière unique une assembly, et de garantir que celle-ci n'a pas été modifiée depuis qu'elle a été signée par le développeur à l'aide d'un couple clé publique/clé privée. Même si ce procédé semble être très utile et performant, il possède, comme nous le verrons un peu plus tard, certaines limites et contraintes, il participera sans aucun doute au processus de sécurisation du code d'une application, mais il ne peut pas prétendre incarner la sécurité à lui tout seul.
II. Signer son assembly avec sn.exe▲
Le SDK du Framework .Net intègre un petit outil en ligne de commande indispensable, il permet de générer le couple clé publique/clé privée RSA permettant de signer votre assembly. Cet outil est très pratique et vous l'utiliserez fréquemment, pour cela nous l'étudierons en détail plus loin dans cet article, ici je me bornerai à vous donner les commandes nécessaires à notre objectif.
II-A. Application exemple▲
Nous allons utiliser une application console pour toutes les démonstrations de cet article, donc ce sera une application réduite à sa plus simple expression. Il ne vous reste qu'à ouvrir Notepad ou votre éditeur de texte préféré, et copier le code ci-dessous et enregistrer le fichier sous StrongNamesApplication.vb.
Imports
System
Imports
System.Reflection
<
Assembly
: AssemblyKeyFile
(
"c:\maCle.snk"
)>
Module
StrongNamesApplication
Sub
Main
(
)
Console.WriteLine
(
"Application de test de la signature d'assembly avec des noms forts."
)
End
Sub
End
Module
Comme vous le voyez ci-dessus, l'application console se contente d'afficher un texte lors de son exécution.
II-B. Signature de l'assembly▲
Lancez donc l'invite de commande de Visual Studio (ou du SDK du Framework .Net), dès lors depuis cette console vous avez accès en ligne de commande à l'outil sn.exe (sn, pour « Strong Names »). Voici la ligne de commande à saisir pour générer la paire de clés RSA :
Un fichier *.snk est créé, ce fichier contient deux clés : une publique et une privée, vous devez prendre garde à ce que ce fichier ne soit pas divulgué, tout du moins la clé privée qu'il contient. Cette clé privée permet de signer l'assembly.
Une fois cette étape passée, il faut maintenant signer notre assembly, cela se fait par le biais du compilateur de votre langage préféré (vbc.exe pour VB.Net, et csc.exe pour C#). Voyons sans plus tarder comment procéder en ligne de commande (la signature d'assembly depuis Visual Studio 2005 sera plus expliquée dans la partie suivante de cet article).
Une fois cette commande terminée, un exécutable (StrongNamesApplication.exe) est créé, il s'agit de notre assembly, de notre application. Vous pouvez le lancer en tapant dans l'invite de commande StrongNameApplication.exe, vous voyez alors s'afficher le texte qui doit être affiché par notre application console exemple.
Tout ceci est bien sympathique, mais un peu fastidieux, surtout si vous devez jongler avec plusieurs assembly et que vous les recompilez fréquemment. De plus, pourquoi se compliquer la vie si vous possédez un logiciel tel Visual Studio 2005 qui automatise ces tâches. Voici comment procéder pour signer vos assembly depuis Visual Studio 2005.
II-C. La signature plus en détail▲
Nous allons utiliser l'outil ILDASM fourni avec le SDK du Framework .Net, il permet de parcourir les assembly pour afficher le code qu'elles contiennent ainsi que les métadonnées, il permet également de désassembler des assembly .Net, et d'afficher le code IL de cette dernière. Ensuite nous utiliserons l'outil ILASM qui, lui, permet d'assembler du code IL pour générer un *.exe ou une *.dll.
Vous allez me dire quel intérêt pour notre article d'aller voir au cœur de l'assembly ? Tout simplement voir l'incidence directe de la signature, et aussi pour mettre à l'épreuve le processus de vérification de signature des assembly lors de leur chargement par l'environnement d'exécution.
Première étape, le désassemblage de notre dll, nous allons produire un fichier texte contenant le code MSIL.
Dans l'invite de commande de Visual Studio 2005, tapez ildasm puis [entrée]. La fenêtre du programme s'ouvre, cliquez alors sur le menu « Fichier », puis « Ouvrir », sélectionnez alors l'assembly.
L'assembly est maintenant chargée dans ildasm, l'on peut parcourir le manifest et ses métadonnées, mais aussi tout le code MSIL.
Maintenant, cliquez sur le menu « Fichier », puis « Dump », cochez toutes les options disponibles, laissez utf-8 comme format, et cliquez sur « OK ». Enregistrez le fichier, il aura une extension *.il, pour « Intermediate language ». Un fichier du même nom sera aussi créé, mais il aura, lui, l'extension *.res, c'est un fichier de ressources qui contient des informations relatives à l'assembly que vous avez « dumpée ».
Dans Visual Studio, ouvrez le fichier avec l'extension *.il qui a été créé par ildasm, il contient les métadonnées et tout le code MSIL de votre assembly.
Tout d'abord si vous avez correctement signé votre assembly et sélectionné toutes les options nécessaires lors du dump, vers la ligne 905/ 910 vous devez trouver la clé publique qui a été copiée :
.publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00 // .$..............
00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00 // .$..RSA1........
0B 59 D3 4B 42 CB F4 69 3C AB D0 60 AE 6D C7 0D // .Y.KB..i<..`.m..
1B 62 4E 78 A6 30 21 E4 E3 32 6B 3F 25 52 62 E8 // .bNx.0!..2k?%Rb.
66 D0 F2 E0 19 34 91 9E 47 4B 71 35 A5 2A 24 44 // f....4..GKq5.*$D
06 56 B2 FD C5 D9 1E 8B EF 12 0D 00 7D 5C 01 E0 // .V..........}\..
58 44 A2 92 D5 5F BE 6E 99 80 D0 22 12 5B AD 30 // XD..._.n...".[.0
CE 1F 78 61 A8 8D 8B 36 EE DC A0 A6 1F 26 80 0D // ..xa...6.....&..
F0 63 9C B1 71 54 99 42 C0 6B 3C 86 47 B7 BA 3F // .c..qT.B.k<.G..?
7D 08 5F E5 A5 EE A6 09 CD 63 AA 17 AA 27 04 BD ) // }._......c...'..
(Bien évidemment comme vous avez une clé différente, ce ne sera pas les mêmes valeurs que celles ci-dessus.)
Cette clé, comme je vous l'ai indiqué plus haut, va servir à vérifier la signature de l'assembly et à la valider lors de son chargement par l'environnement d'exécution .Net. En l'état, notre assembly signée doit passer l'étape de la validation avec succès, en effet elle n'a pas été altérée. Cependant pour bien voir que la signature d'assembly n'est pas un gadget, nous allons altérer notre assembly, puis la réassembler, et enfin tenter d'exécuter notre application console pour voir ce qu'il se passe.
Altération de notre assembly :
Sur la ligne après la clé publique, vous devez avoir la ligne suivante
.ver 1:0:0:0
Remplacez 1:0:0:0 par les chiffres que vous voulez (par exemple 2:1:3:7), ils correspondent au numéro de version de l'assembly. Une fois cette modification effectuée, enregistrez le fichier, et fermez-le.
Dans l'invite de commande de Visual Studio 2005 (ou du SDK), tapez ce qui suit :
ilasm /EXE c:\StrongNamesApplication.il
L'assemblage de notre fichier contenant le code MSIL commence, et doit se terminer par :
Operation completed successfully
Un fichier StrongNamesApplication.exe est alors créé, tentez de le lancer depuis la console. Une exception doit alors être levée, car nous avons altéré l'assembly, et le résultat du hash ne correspond plus à celui effectué lors de la signature de l'assembly par le développeur. Voici ce que vous devez voir apparaître :
Puis dans la console, l'exception suivante est levée :
Exception non gérée : System.IO.FileLoadException: Impossible de charger le fichier ou l'assembly
'StrongNamesApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5ce9c151c1a0a833'
ou une de ses dépendances. Échec de la validation de nom fort. (Exception de HRESULT : 0x8013141A)
Nom du fichier : 'StrongNamesApplication, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=5ce9c151c1a0a833' ---> System.Security.
SecurityException: Échec de la validation de nom fort. (Exception de HRESULT : 0x8013141A)
La zone de l'assembly qui a échoué était :
MyComputer
Nous voyons donc que lors de la vérification de la signature, l'environnement .Net a détecté grâce au hashage que le contenu de l'assembly a été modifié, il a donc refusé de lancer notre application et a levé une exception.
III. Intégration dans Visual Studio 2005▲
Signer une assembly avec Visual Studio est extrêmement simple, voici la démarche à suivre.
En premier il faut générer une paire de clés avec l'utilitaire sn.exe (voir la commande un peu plus haut dans cet article), ensuite, il faut faire un clic droit -> propriété dans l'explorateur de solutions comme le montre l'image ci-dessous :
Ensuite, aller dans l'onglet signature, cocher la case « Signer l'assembly », puis sélectionner le menu « Parcourir » et pointer vers le fichier *.snk que vous avez généré précédemment.
Votre assembly est dès lors prête pour la signature, cela va être effectué par le compilateur. Pour cela, cliquez dans le menu « Générer » en haut dans Visual Studio 2005, comme ci-dessous :
Votre assembly est désormais signée, la compilation a réussi, la fenêtre « Sortie » le confirme :
Voilà, pas la peine d'en rajouter, c'est aussi simple que cela.
IV. Présentation de sn.exe▲
SN.exe est un utilitaire qui permet de signer les assembly .Net avec des noms forts. Cet outil en ligne de commande permet également la gestion des clés RSA utilisées pour la signature, ainsi que toutes les opérations apparentées comme par la désactivation de la vérification de signature.
Voici un tableau issu de la documentation Microsoft MSDN relative aux commandes et paramètres de sn.exe qui est fourni dans le SDK du Framework .Net 2.0.
Option | Description |
-c [csp] | Définit le fournisseur de services de chiffrement à utiliser pour la signature de noms forts. Ce paramètre s'applique à l'ensemble de l'ordinateur. Si vous ne spécifiez pas de nom de fournisseur de services de chiffrement, Sn.exe annule le paramètre en cours. |
-d container | Supprime le conteneur de clé spécifié du fournisseur de services de chiffrement de noms forts. |
-D assembly1 assembly2 | Vérifie que deux assembly ne se distinguent que par leur signature. Cette vérification intervient souvent après la nouvelle signature d'un assembly avec une paire de clés différente. |
-e assembly outfile | Extrait la clé publique d'assembly et la stocke dans outfile. |
-h | Affiche la syntaxe et les options de commande de l'outil. |
-i infile container | Installe la paire de clés d'infile dans le conteneur de clés spécifié. Le conteneur de clés réside dans le fournisseur de services de chiffrement de noms forts. |
-k [taille_de_clé] fichier_sortie | Génère une nouvelle clé RSACryptoServiceProvider de la taille spécifiée et l'écrit dans le fichier spécifié. Une clé publique et une clé privée sont écrites dans le fichier. Si vous ne spécifiez pas de taille de clé, une clé de 1 024 bits est générée par défaut si le fournisseur de services de chiffrement avancé Microsoft est installé ; sinon, une clé de 512 bits est générée. Le paramètre taille_de_clé prend en charge des longueurs de clé allant de 384 bits à 16 384 bits dans des incréments de 8 bits si le fournisseur de services de chiffrement avancé Microsoft est installé. Il prend en charge des longueurs de clé allant de 384 bits à 512 bits dans des incréments de 8 bits si le fournisseur de services de chiffrement de base Microsoft est installé. |
-m [y|n] | Spécifie si les conteneurs de clés sont propres à l'ordinateur ou propres à l'utilisateur. Si vous spécifiez « y », les conteneurs de clés sont propres à l'ordinateur. Si vous spécifiez « n », les conteneurs de clés sont propres à l'utilisateur. Si ni « y » ni « n » ne sont spécifiés, cette option affiche le paramètre en cours. |
-o infile [outfile] | Extrait la clé publique d'infile et la stocke dans un fichier .csv. Chaque octet de la clé publique est séparé par une virgule. Ce format s'avère utile pour les références de codage en dur aux clés sous forme de tableaux initialisés dans le code source. Si vous ne spécifiez pas d'outfile, cette option place la sortie dans le presse-papiers. |
-p infile outfile | Extrait la clé publique de la paire de clés figurant dans infile et la stocke dans outfile. Cette clé publique permet de temporiser la signature d'un assembly à l'aide des options /delaysign+ et /keyfile de Assembly Linker (Al.exe). En cas de temporisation de la signature d'un assembly, seule la clé publique est définie au moment de la compilation et un espace est réservé dans le fichier pour la signature qui sera ajoutée par la suite, lorsque la clé privée sera connue. |
-pc container outfile | Extrait la clé publique de la paire de clés figurant dans « container » et la stocke dans outfile. |
-q[uiet] | Spécifie le mode silencieux ; supprime l'affichage des messages de réussite. |
-R[a] assembly fichier_entrée | Signe à nouveau un assembly ayant préalablement fait l'objet d'une signature ou dont la signature a été temporisée avec la paire de clés figurant dans infile. Si vous utilisez -Ra, les hashages sont recalculés pour tous les fichiers dans l'assembly. |
-Rc[a] conteneur_assembly | Signe à nouveau un assembly ayant préalablement fait l'objet d'une signature ou dont la signature a été temporisée avec la paire de clés figurant dans container. Si vous utilisez -Rca, les hashages sont recalculés pour tous les fichiers dans l'assembly. |
-Rh assembly | Recalcule des hashages pour tous les fichiers de l'assembly. |
-t[p] infile | Affiche le jeton de la clé publique stockée dans infile. Le contenu de infile doit être une clé publique générée précédemment à partir d'un fichier de paires de clés à l'aide de -p. N'utilisez pas l'option -t[p] pour extraire directement le jeton d'un fichier de paires de clés. |
-T[p] assembly | Affiche le jeton de la clé publique de l'assembly. L'assembly doit correspondre au nom d'un fichier qui contient un manifeste d'assembly. |
-v assembly | Vérifie le nom fort figurant dans assembly, où assembly correspond au nom d'un fichier comportant un manifeste d'assembly. |
-vf assembly | Vérifie le nom fort figurant dans assembly. À la différence de l'option -v, -vf force la vérification même si celle-ci a été désactivée à l'aide de l'option -Vr. |
-Vl | Répertorie les paramètres en cours pour la vérification des noms forts sur cet ordinateur. |
-Vr assembly [userlist] [infile] | Inscrit assembly pour que la vérification soit ignorée. Vous pouvez également spécifier une liste de noms d'utilisateur avec la virgule comme séparateur. Si vous spécifiez infile, la vérification reste activée, mais la clé publique figurant dans infile est utilisée au cours des opérations de vérification. Assembly peut être spécifié sous la forme *, strongname pour inscrire toutes les assembly avec le nom fort spécifié. Strongname doit être spécifié en tant que chaîne de chiffres hexadécimaux représentant la clé publique sous forme de jetons. Consultez les options -t et -T pour afficher le jeton de la clé publique. |
-Vu assembly | Annule l'inscription d'assembly pour que la vérification soit ignorée. Les mêmes règles d'affectation de noms aux assembly s'appliquent aux options -Vr et -Vu. |
-Vx | Supprime toutes les entrées des vérifications ignorées. |
-? | Affiche la syntaxe et les options de commande de l'outil. |
Je n'entrerai pas plus dans le détail, en effet, au cours de l'article nous abordons les scénarios d'utilisation les plus fréquents.
V. Signature d'assembly dans un processus de développement▲
Jusqu'ici nous avons abordé la signature d'assembly avec des noms forts d'un point de vue relativement théorique. Ici nous allons nous placer dans une optique plus concrète, en voyant comment il est possible d'intégrer cette sécurité au sein d'un processus de développement. Cela va consister principalement à voir quels sont les inconvénients de la signature, et ainsi de quelle manière il est possible de gérer au mieux la sécurité de la clé privée, qui est la clé de voûte de la signature.
V-A. Les inconvénients et contraintes de la signature▲
Tout procédé qui présente des avantages présente également des inconvénients, la signature d'assembly n'échappe pas à la règle. D'ailleurs dans notre cas précis, il est plus judicieux de parler de contrainte que d'inconvénients. En effet, vous allez voir que signer ses assembly peut se révéler assez lourd si l'on ne s'y prend pas correctement, surtout en cas de gros développement.
La contrainte majeure de la signature d'assembly est paradoxalement la sécurité. Cela s'explique très facilement lorsque l'on comprend que la fiabilité de cette signature repose sur le secret de la clé privée, tout repose sur cette clé. Si la clé privée est dévoilée, alors le secret, et donc la signature, tombe ce n'est ni plus ni moins qu'un élément relatif à la cryptographie employant des algorithmes asymétriques. Nous voyons donc que la contrainte majeure est le fait que la sécurité de génération et de stockage de la clé privée doit être totale.
Une autre contrainte découlant de la première est qu'il va falloir utiliser en cas de gros développement notamment des astuces quant à la signature d'assembly en cours de développement, nous le verrons en particulier avec le « Delay Signing » et le « Test Key Signing » qui sont deux procédés permettant de n'avoir à signer définitivement nos assembly qu'une fois le développement terminé, sans pour autant devoir être privé des avantages et des impératifs procurés par la signature au cours du processus de développement (notamment en cas d'utilisation du versionning dans le GAC et du besoin d'avoir des noms forts pour les assembly).
Et enfin, un réel inconvénient, lui, lié au « Delay Signing » que nous allons voir juste après est le fait de devoir désactivé la vérification réalisée par le CLR lors du chargement de l'assembly, en effet cela peut être une brèche relativement importante dans la sécurité du poste de développement, partant bien évidemment du principe qu'une telle opération ne devra jamais être réalisée en environnement de production. Heureusement, nous allons voir que .Net 2.0 nous apporte quelques évolutions bien précieuses dans ce domaine.
V-B. Le delay signing▲
Une solution permettant de protéger beaucoup plus efficacement la clé privée, est le « Delay Signing », ou la signature différée, voyons-la plus en détail sans plus tarder.
V-B-1. Présentation▲
Comme nous l'avons vu précédemment, l'utilisation de la clé privée par un grand nombre de personnes sur de multiples machines est un risque sur le plan de la sécurité. Pour pallier ce problème, il existe une solution pour contourner cela et ne pas être obligé de faire circuler la clé privée : c'est le « delay signing », on pourrait traduire cela par « signature différée ». Cela consiste à utiliser uniquement le jeton de la clé publique pour l'intégrer dans l'assembly, ainsi elle possède un nom fort (ce qui est nécessaire dans nombre de situations de développement), mais par contre elle ne peut pas passer la validation de l'environnement d'exécution, il faut donc désactiver cette vérification jusqu'à ce que la signature avec la clé privée soit effectivement réalisée, signature qui interviendra généralement juste avant la livraison du produit, et sera effectuée par un nombre restreint de personnes pour des raisons évidentes de sécurité.
Voici la démarche à suivre pour utiliser le « delay signing ».
V-B-2. Extraction de la clé publique▲
Tout d'abord, il faut extraire la clé publique du fichier snk, en effet nous voulons ne diffuser que cette dernière, et ainsi garder la clé privée secrète. Pour cela nous allons utiliser l'utilitaire sn.exe :
V-B-3. Signature partielle▲
Maintenant que nous avons extrait notre clé publique, nous allons compiler notre application en la signant partiellement. Pour cela, deux possibilités :
en ligne de commande :
vbc /delaysign+ /keyfile:c:\maClePublique.pk StrongNamesApplication.vb
avec Visual Studio 2005 :
pour signer partiellement l'assembly, c'est très simple, il faut aller dans les propriétés de votre projet (clic droit sur le projet dans l'explorateur de solutions, puis « propriétés », onglet « signature »), cocher « signer l'assembly », puis sélectionner votre clé publique (fichier *.pk) dans la liste déroulante, et cocher « Différer la signature ». Lors de la prochaine génération de votre projet, l'assembly sera signée partiellement.
En l'état, si vous essayez de charger votre assembly, l'environnement d'exécution lèvera une exception, car vous n'avez pas utilisé la clé privée pour la signature et le hash n'a pas été effectué. Il faut donc désactiver la vérification lors du chargement.
V-B-4. Désactivation de la vérification▲
Ici deux possibilités, toutes deux accessibles par l'utilitaire en ligne de commande sn.exe.
- Soit on indique explicitement l'assembly pour laquelle on désactive la vérification. Pour cela, voici la commande :
- Soit on désactive la vérification pour le jeton correspondant à la clé publique, ainsi toutes les assembly partiellement signées grâce à une clé publique donnée pourront être chargées sans problème. Pour cela, voici la commande :
Remarque : vous pouvez facilement récupérer la valeur du jeton de la clé publique d'une assembly, pour cela il suffit d'utiliser la commande « sn -T [assembly] ».
Une fois le développement terminé, il est bien évidemment possible de réactiver la vérification pour l'assembly concernée, ou pour le jeton de la clé publique en question. Pour cela, il faut utiliser la commande suivante :
Vous pouvez également connaître la liste des assembly et/ou des jetons pour lesquels la vérification est désactivée, pour cela il faut utiliser la commande suivante :
sn -Vl
V-B-5. Signature définitive de l'assembly▲
Il n'est pas nécessaire de régénérer votre application, donc vous n'avez pas à passer par la case compilateur ! C'est quand même bien pratique. Voici la commande à utiliser en ligne de commande :
sn -R StrongNamesApplication.exe c:\maCle.snk
Remarque : l'ajout de « a » après le « R » permet de recalculer le hash pour l'assembly.
Il ne nous reste plus qu'à vérifier que la signature définitive avec la clé publique a correctement fonctionné, pour cela nous allons utiliser le commutateur « -v » qui permet de réaliser un test de validité de signature sur une assembly.
Remarque : le commutateur « -vf » permet de forcer la vérification, même si celle-ci a été désactivée, comme nous l'avons montré un peu plus haut avec l'aide du commutateur « -Vr ».
V-C. Le test key signing▲
V-C-1. Présentation▲
Avec le Framework .Net 2.0 est apparue une nouveauté en matière de signature d'assembly, il s'agit du « Test Key Signing » qui vient pour combler les « lacunes » du delay signing, qui, je vous le rappelle, vous obligent à désactiver la vérification de signature pour une assembly ou un jeton de clé publique donné, ce qui est une faille potentielle de la sécurité de votre poste de développement. De plus, désactiver la vérification de signature vous empêche d'avoir une idée exacte des performances de votre application (notamment lors de son chargement par le CLR) dans un environnement de production, en effet, il ne faut pas sous-estimer le temps nécessaire pour les vérifications de la signature. Même si ces temps sont relativement courts, ils peuvent s'avérer déterminants pour des applications ayant des impératifs de performances. Pour pallier cela, Microsoft a intégré une nouveauté dans le Framework 2.0, le Test Key Signing, voyons à quoi il correspond sans plus attendre.
Le « Test Key Signing » permet à la différence du delay signing de signer réellement son assembly, et donc de pouvoir effectuer le contrôle de signature lors du chargement de celle-ci par l'environnement .Net ce qui peut être un avantage certain comme nous l'avons vu précédemment.
Pour « test signer » une assembly l'on procède de la façon suivante :
- signer l'assembly avec le procédé de signature différée (delay signing) en utilisant votre clé publique ;
- créer une paire de clés (publique/privée) de test avec sn.exe ;
- test signer l'assembly avec la paire de clés de test, c'est la clé privée de test qui va être utilisée pour la signature (utiliser sn.exe avec le commutateur -TS) ;
- configurer l'environnement d'exécution pour utiliser la clé publique pour vérifier la validité des assembly signée avec la clé privée de test (utiliser sn.exe avec le commutateur -Vr).
Votre assembly est à ce stade « test signé » avec la clé privée de test, et la validité de la signature est elle vérifiée grâce à votre clé publique habituelle. Clé publique que vous aurez pris le soin d'extraire de la paire clés publique/privée que vous utilisez pour signer vos assembly, en effet, l'objectif de tout cela est la protection de la clé privée.
Une fois le développement terminé, il ne restera plus qu'à signer définitivement votre assembly avec la clé privée que vous avez gardée en sécurité depuis le début. Cela doit être réalisé avec l'utilitaire sn.exe et avec le commutateur -R ou -Rc en fonction de vos besoins.
V-C-2. Démonstration▲
Voici un schéma montrant le processus complet de « Test Signing » jusqu'à la signature finale avec la clé privée que vous utilisez pour signer vos assembly que vous allez livrer.
Ressources▲
Conclusion▲
Comme nous l'avons vu au cours de cet article, la signature d'assembly n'incarne pas à elle seule la sécurité du code de votre application, mais elle y participe. Si la signature complète d'assembly lors de gros développements n'est pas adaptée, notamment en ce qui concerne la protection de la clé privée. Pour pallier ces faiblesses il existe le « Delay Signing », et depuis le Framework .Net 2.0, le « Test Key Signing », qui pour moi semble être la meilleure solution à l'heure actuelle, apportant flexibilité et confidentialité de la clé privée.
J'espère au cours de cet article vous avoir montré aussi simplement que possible tout l'intérêt que vous pouvez trouver dans ce procédé, pas si évident que cela à appréhender, qu'est la signature d'assembly. Enfin, pour terminer, gardez toujours à l'esprit qu'aucune méthode isolée n'est suffisante pour apporter une sécurité efficace, seule l'addition de différents niveaux de sécurisation amène une réponse convenable.