Dans mon précédent article je vous ai présenté les différentes façons de sauvegarder une information lorsque l’on n’a pas besoin de pouvoir retrouver l’information initiale. Cependant dans certains cas on souhaite protéger une donnée afin que tout le monde ne puisse pas la lire tout en ayant la possibilité de la relire par la suite.
Prenons un exemple pour illustrer :
Je souhaite créer une application se connectant à un web service protégé par une authentification par token. L’utilisateur s’authentifie à l’api et celle-ci lui renvoie un token d’accès à utiliser pour chaque appel à une méthode du web service. Comme l’utilisateur est feignant (et il a bien raison) il souhaite ne pas avoir besoin de resaisir ses informations pour s’authentifier et souhaite donc que l’application intègre la fonctionnalité “ mémoriser mes informations ”. Je dois donc sauvegarder le token d’accès entre 2 lancements de l’application. Bien évidemment je souhaite que seul l’utilisateur puisse lire ces informations.
En gros il nous faut 2 méthodes : Protect (data) et Unprotect (data).
Les algorithmes de chiffrement fournissent ce type de méthodes mais avec un deuxième argument : la clé de chiffrement. Les données sont chiffrées grâce à cette clé puis déchiffrées grâce à la même clé (algorithme de chiffrement symétrique) ou une autre clé (algorithme de chiffrement asymétrique).
Le Framework .NET contient plusieurs algorithmes de chiffrement symétrique : AES, DES, RC2, Rijndael, TripleDES. On peut donc tout simplement utiliser une de ces classes. OK mais maintenant où sauvegarder la clé de chiffrement ? Bien évidement celle-ci doit également être protégée => Le serpent se mord la queue.
La solution la plus simple est en fait de demander à Windows de se débrouiller et de le faire à notre place. Après tout un système aussi utilisé que Windows doit bien fournir une méthode pour gérer un besoin aussi courant (n’est-ce pas Simon ;)). Et bien évidemment c’est le cas, Windows fournit Protected Data API. Cette API est composée de 2 méthodes
public static byte[] Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope)
public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope)
La donnée à protéger correspond à l’argument userData. Dans notre cas nous souhaitons protéger une chaine de caractères. Il faut donc la convertir en byte[] à l’aide par exemple de System. Text. UnicodeEncoding. GetBytes (string) et inversement une fois déprotégé System. Text. UnicodeEncoding. GetString (byte[]).
optionalEntropy est, comme son nom l’indique, optionnel et permet d’ajouter un sel spécifique à l’application. En gros pour déchiffrer l’information il faudra utiliser la même valeur.
Le dernier paramètre, scope, permet de définir qui peut déchiffrer l’information : l’utilisateur courant ou la machine.
Remarque : toutes les applications lancées dans le scope spécifié peuvent déchiffrer les informations. Ce n’est donc pas une méthode inviolable mais de toute façon peut-on faire mieux dans ce cas ?
Et voilà le résultat :
public static string Protect(string str)
{
byte[] entropy = Encoding.ASCII.GetBytes(Assembly.GetExecutingAssembly().FullName);
byte[] data = Encoding.ASCII.GetBytes(str);
string protectedData = Convert.ToBase64String(ProtectedData.Protect(data, entropy, DataProtectionScope.CurrentUser));
return protectedData;
}
public static string Unprotect(string str)
{
byte[] protectedData = Convert.FromBase64String(str);
byte[] entropy = Encoding.ASCII.GetBytes(Assembly.GetExecutingAssembly().FullName);
string data = Encoding.ASCII.GetString(ProtectedData.Unprotect(protectedData, entropy, DataProtectionScope.CurrentUser));
return data;
}
En conclusion, avant de faire quelque chose soi-même (surtout en matière de sécurité), il faut regarder si cela n’existe pas déjà.