Archives de Tag: crypter

Le chiffrement symétrique avec le Framework .Net

Il peut parfois être utile de savoir chiffrer des données pour des raisons de confidentialité, sécurité, etc…

Il existe différentes méthodes de chiffrements, avec deux types distincts : le chiffrement symétrique et le chiffrement asymétrique. Dans le cas du chiffrage symétrique, c’est une seule et même clé qui est utilisée pour le chiffrement et le déchiffrement. AES est un de ces chiffrements, et probablement le plus connu. Son principal avantage est la sécurité obtenue par des clés allant de 128 à 256 bits donnant un nombre énorme de possibilités.

Cet article s’intéresse à l’utilisation des chiffrements symétriques avec le Framework .Net. Seul AES servira d’exemple, mais tant que la méthode de chiffrement est gérée par le Framework, le fonctionnement sera le même (DES s’utilise par exemple de la même manière qu’AES).

Le Framework .Net nous fournit des implémentations des algorithmes qui nous permettent d’utiliser les méthodes de chiffrements de manière relativement simple. Dans le cas d’AES, on trouve les classes AesCryptoServiceProvider, RijndaelManaged, AesManaged. Trois classes pour faire la même chose ? Pas exactement. AesCryptoServiceProvider et AesManaged héritent d’AES tandis que RijndaelManaged hérite de Rijndael. Ces deux classes, Aes et Rijndael, diffèrent légèrement, et pour bien comprendre, il faut savoir qu’Aes utilise l’algorithme Rijndael, mais avec une certaine configuration fixée, entre autre une taille de bloc de données de 128 bits, tandis que Rijndael permet d’utiliser des blocs de 128 à 256 bits par incrément de 64 (il y d’autres paramètres fixés dans Aes, mais ce n’est pas le sujet 😉 ). Aes n’est donc qu’une cas particulier d’utilisation de Rijndael.

Afin de vraiment être sûr de faire de l’Aes, il faudra utiliser AesCryptoServiceProvider ou AesManaged. Leur utilisation est identique mais il existe quelques différences au niveau du comportement de ces deux classes. Par exemple, changer le Mode d’AesManaged par un mode invalide pour AES (mais valide pour Rijndael) causera une exception au moment de changer le mode, tandis que pour AesCryptoServiceProvider, l’exception sera lancé au moment du chiffrement. Au niveau des performances, de rapides tests sembleraient à l’avantage d’AesManaged. C’est cette classe qui sera utilisé pour l’exemple de cet article.

Passons à la pratique, et créons une instance d’AesManaged. Key et IV sont les deux propriétés importantes dans le chiffrement. La taille de la clé dépend du paramètre KeySize qui n’a que 3 valeurs possibles, à savoir 128, 192 et 256 bits. La clé est un tableau de byte de KeySize / 8 cases. Par défaut, la clé est de 256 bits, donc 32 octets. L’IV est une donnée utilisée uniquement pour le premier bloc de données. Il est particulièrement important si plusieurs données doivent être chiffrées avec la même clé (et dans ce cas, il sera recommandé d’utiliser un IV différent). Cela permet entre autre d’avoir pour une même donnée claire, différentes valeurs une fois chiffrée. L’IV doit donc être distribué avec la donnée chiffrée. Une clé et un IV sont automatiquement générés, et peuvent être régénérés en utilisant les méthodes GenerateKey et GenerateIV.

Et maintenant, comment ça fonctionne ? Les méthodes CreateEncryptor et CreateDecryptor permettent de créer le ICryptoTransform. Cette objet va pouvoir gérer le traitement à effectuer sur les données (dans un cas le chiffrement, et dans l’autre le déchiffrement 😀 ).
Nous avons donc ceci :

var aes = new AesCryptoServiceProvider();
var encrypter = aes.CreateEncryptor();
var decrypter = aes.CreateDecryptor();

En passant la clé et l’IV en paramètre, nous pouvons choisir une clé et un IV différents de ceux de l’instance utilisée d’AesManaged.

Avec la méthode CreateEncryptor, nous obtenons notre objet gérant le chiffrement. Pour pouvoir l’utiliser, nous allons avoir besoin d’un objet CryptoStream. Cet objet va se charger de prendre un flux de données, de le faire passer à travers notre ICryptoTransform , et de refaire sortir les données transformée dans un autre flux.

Dans notre exemple, nous utiliserons des MemoryStream, mais les manipulations sont identiques avec d’autres types de flux.

Le constructeur du ICryptoTransform prend en paramètre un flux de sortie. Dans un bloc using, nous créons donc le MemoryStream. Nous passons ensuite celui-ci en paramètre du constructeur. Le deuxième paramètre correspond à l’objet ICryptoTransform , ce que nous avons obtenu avec la méthode CreateEncryptor.

Un code parlant mieux que de longues phrases :

using (MemoryStream ms = new MemoryStream())
{
    CryptoStream cs = new CryptoStream(ms, encrypter, CryptoStreamMode.Write);
}

Il nous faut maintenant passer notre donnée à chiffrer. Pour cela, la donnée doit être sous la forme de tableau de byte. Dans le cas d’une chaine, un Encoding.UTF8.GetBytes(theString) suffira. Ce tableau doit ensuite être passé à la méthode Write du CryptoStream.

using (MemoryStream ms = new MemoryStream())
{
    string toCrypt = "Ma donnée à chiffrer";
    var b = Encoding.UTF8.GetBytes(toCrypt);

    CryptoStream cs = new CryptoStream(ms, encrypter, CryptoStreamMode.Write);
    cs.Write(b, 0, b.Length);
}

Enfin, la méthode FlushFinalBlock du CryptoStream doit être appelée pour indiquer qu’il n’y a plus de données à chiffrer. En effet, AES chiffre les données par bloc et donc inévitablement attend un bloc complet avant de le chiffrer. Si la taille de votre donnée n’est pas un multiple de la taille du bloc, celle-ci sera tronquée lorsque vous récupérerez les données du MemoryStream. La méthode FlushFinalBlock nous permet donc de chiffrer le dernier bloc sans attendre les données (et indique ainsi la fin du CryptoStream).Les données peuvent ensuite être récupérés sous la forme Base64 avec un Convert.ToBase64String(ms.ToArray()). L’avantage du Base64 étant qu’il n’y a pas de perte de place étant donné que chaque valeur d’octet correspond à un caractère de la base (et inversement).

Cela nous donne le code final (avec le rajout de la donnée à chiffrer, et la variable pour stocker la chaine chiffrée) :

string toCrypt = "Ma donnée à chiffrer";
var plainData = Encoding.UTF8.GetBytes(toCrypt);
string cryptedData;

var aes = new AesCryptoServiceProvider();

var encrypter = aes.CreateEncryptor();
var decrypter = aes.CreateDecryptor();

using (MemoryStream ms = new MemoryStream())
{
    CryptoStream cs = new CryptoStream(ms, encrypter, CryptoStreamMode.Write);
    cs.Write(b, 0, b.Length);
    cs.FlushFinalBlock();
    cs.Close();

    cryptedData = Convert.ToBase64String(ms.ToArray());

}

Pour le déchiffrement, c’est presque identique. Il faut passer le ICryptoTransform obtenu depuis la méthode CreateDecryptor au constructeur du CryptoStream, récupérer la chaine chiffrée sous la forme de tableau de byte, et la passer à la méthode Write. Pour enfin récupérer les données dans une string.
Cela nous donne :

using (MemoryStream ms = new MemoryStream())
{
    byte[] data = Convert.FromBase64String(cryptedData);
    CryptoStream cs = new CryptoStream(ms, decrypter, CryptoStreamMode.Write);
    cs.Write(data, 0, data.Length);
    cs.FlushFinalBlock();
    cs.Close();

    string newData = Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length);
}

Voilà pour le chiffrement AES. La prochaine étape sera le RSA qui nous permettra en plus de signer des données.

A bientôt sur OneFor4 !

Adrien Olivencia