Cet article fait suite à l’art du secret, la cryptographie. Si vous n’avez pas de connaissances dans le domaine de la cryptographie, je vous invite à aller le lire.

Nous allons réaliser une application .Net, pour le moment le cœur, de chiffrement de fichiers. Dans un premier temps, nous utiliserons uniquement les primitives fournies par le Framework .NET. Mais avant de coder, il faut que je vous explique ma stratégie.

Un schéma pour poser les bases

Schéma de chiffrement

 

Premier constat évident pour certains, même s’il n’est pas appliqué partout, nous allons chiffrer les données par une clé générée aléatoirement. Le mot de passe fourni par l’utilisateur même après l’utilisation d’une fonction d’étirement est faible. Prenons un exemple simple pour comprendre que la bataille est perdue d’avance. On « mesure » la force, la sécurité de la clé par sa taille et le nombre de valeurs possibles. Comme un mot de passe me direz-vous. Dans notre application, la clé a une taille de 32 octets, soit 256 bits. Les valeurs possibles se calculent donc comme ceci 2^256 (ou 256^32). Je ne m’amuserais pas à essayer d’afficher un tel chiffre. C’est très grand ! Maintenant dans le cas d’un mot de passe de 12 caractères, qui devient un peu la norme basse, un utilisateur n’a pas la possibilité de renseigner toutes les valeurs parmi les 256 possibles pour un caractère. Il est bridé par le clavier, les caractères imprimables. Prenons cette liste de caractères

  • 1234567890
  • abcdefghijklmnopqrstuvwxyz
  • ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • {[[-_\)]}=+
  • ,;:/!*?
  • & »#’|^@

On a 117 caractères ce qui revient à dire que notre mot de passe à une sécurité de 117^12.

CQFD On ne chiffre pas le contenu du  fichier directement avec un mot de passe.

La fonction d’étirement (« dérivation de clé » = Password-Based Key Derivation Function 2 , PBKDF2 ci-dessus) a deux rôles dans notre application.  Le premier : d’obtenir une valeur de la taille d’une clé utilisée par une primitive cryptographique de chiffrement ; le second : de rendre plus coûteux la recherche exhaustive, ou « par dictionnaire », du mot de passe. Ce genre de fonction doit consommer du CPU et de la RAM pour obtenir une valeur au bout de X seconde(s), ce qui dépend grandement de votre configuration et du niveau de sécurité souhaité.  Il faudra donc faire des tests pour établir un bon compromis entre la sécurité et une utilisation courante.

Nous allons utiliser la classe .Net Rfc2898DeriveBytes implémentant le standard RFC2898 comme fonction d’étirement

/// <summary>
/// Permet d’étendre le mot de passe
/// </summary>
/// <param name= »key »>Mot de passe à étendre</param>
/// <param name= »salt »>Données aléatoires</param>
/// <param name= »iteration »>Le nombre d’itérations</param>
/// <param name= »outputSize »>Le nombre d’octets souhaité en sortie</param>
/// <returns></returns>
public byte[] Stretching(byte[] key, byte[] salt, int iteration, int outputSize)
{

using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(key, salt, iteration))

{

return pbkdf2.GetBytes(outputSize);

}

}

Nous allons ensuite utiliser le mot de passe étiré pour protéger la clé de chiffrement (_EncryptedKeyFile ci-dessous) : c’est la notion de KeyWrap. En .Net, il n’y a pas d’implémentation pour cela. Nous allons l’implémenter en utilisant simplement un chiffrement AES. Nous utiliserons un SALT en dur dans l’application. Dans ce cas, ca n’affecte pas la sécurité, car les données chiffrées sont aléatoires.

byte[] key0 = pBKDF2Key.Stretching(mdpKey, KEY_MATERIAL_DATA_SIZE);
Array.Copy(key0, aesKey, KEY_DATA_SIZE);
Array.Copy(key0, KEY_DATA_SIZE, aesIV, 0, IV_DATA_SIZE);
Aes aesEngine = AesCng.Create();
aesEngine.Key = aesKey;
aesEngine.IV = aesIV;
aesEngine.Mode = CipherMode.CBC;
aesEngine.Padding = PaddingMode.None;

using (MemoryStream ms = new MemoryStream())
{

using (CryptoStream cipherStream = new CryptoStream(ms, aesEngine.CreateEncryptor(), CryptoStreamMode.Write))
{

cipherStream.Write(key0);
if (!cipherStream.HasFlushedFinalBlock)

cipherStream.FlushFinalBlock();

}

ms.Flush();
_EncryptedKeyFile = ms.ToArray();

}

Maintenant, la suite est triviale : génération de la partie aléatoire du matériel cryptographique via un CSRNG puis chiffrement avec AES du fichier.

La génération de clé et de IV est incluse dans toutes les classes dérivées de SymmetricAlgorithm , dont AesCng. Pour configurer notre AESEngine, nous utilisons le mode CBC qui est un chiffrement par bloc. .Net contient peu de modes, notamment pas de CTR (Counter Mode). Pour garantir que le dernier bloc du fichier soit complet, on est obligé d’utiliser un padding.

Aes aesEngine = AesCng.Create();
aesEngine.GenerateKey();
aesEngine.GenerateIV();
aesEngine.Mode = CipherMode.CBC;
aesEngine.Padding = PaddingMode.PKCS7;

Nous allons générer les tableaux de byte aléatoires à fournir à l’AESEngine par la suite via la classe RNGCryptoServiceProvider.

Bilan

Vous trouverez le code complet de ce stade dans le répertoire FullNet de ce repository ; Code source du projet

C’est un début, nous avons pu mettre en pratique notre stratégie avec le matériel cryptographique de .NET. On chiffre nos fichiers avec notre propre application un peu sécurisée. La fonction d’étirement de .Net n’est pas la plus récente. Le manque de modes et le manque d’algorithmes disponibles nous restreint à n’utiliser que AES. Même si AES est le standard, toujours sécurisé, je préfère utiliser des algorithmes aussi sécurisés mais moins connus avec des modes plus performants.

Et la suite

Dans l’article suivant, L’art du secret : la cryptographie (3/4) – Own crypto app Improvement , nous allons apporter quelques modifications afin de rendre notre application plus versatile. Nous utiliserons pour cela Bouncy Castle pour C# et SharpHash.

 

Ne ratez plus aucune actualité avec la newsletter mensuelle de SoftFluent

Newsletter SoftFluent