40 votes

CryptographicException : Le rembourrage n'est pas valide et ne peut être supprimé

J'avais besoin d'un simple cryptage de chaîne de caractères, j'ai donc écrit le code suivant (en m'inspirant largement de aquí ):

    // create and initialize a crypto algorithm
    private static SymmetricAlgorithm getAlgorithm(string password) {
        SymmetricAlgorithm algorithm = Rijndael.Create();
        Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
            password, new byte[] {
            0x53,0x6f,0x64,0x69,0x75,0x6d,0x20,             // salty goodness
            0x43,0x68,0x6c,0x6f,0x72,0x69,0x64,0x65
        }
        );
        algorithm.Padding = PaddingMode.ISO10126;
        algorithm.Key = rdb.GetBytes(32);
        algorithm.IV = rdb.GetBytes(16);
        return algorithm;
    }

    /* 
     * encryptString
     * provides simple encryption of a string, with a given password
     */
    public static string encryptString(string clearText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(clearBytes, 0, clearBytes.Length);
        cs.Close();
        return Convert.ToBase64String(ms.ToArray());
    }

    /*
     * decryptString
     * provides simple decryption of a string, with a given password
     */
    public static string decryptString(string cipherText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write);
        cs.Write(cipherBytes, 0, cipherBytes.Length);
        cs.Close();            
        return System.Text.Encoding.Unicode.GetString(ms.ToArray());
    }

Le code semble fonctionner correctement, sauf que lors du décryptage de données avec une clé incorrecte, j'obtiens une CryptographicException - "Padding is invalid and cannot be removed" - sur la ligne cs.Close() dans decryptString.

exemple de code :

    string password1 = "password";
    string password2 = "letmein";
    string startClearText = "The quick brown fox jumps over the lazy dog";
    string cipherText = encryptString(startClearText, password1);
    string endClearText = decryptString(cipherText, password2);     // exception thrown

Ma question est la suivante : faut-il s'attendre à cela ? J'aurais pensé que le décryptage avec un mot de passe erroné donnerait lieu à une sortie absurde, plutôt qu'à une exception.

27voto

Jorge Córdoba Points 18919

Bien qu'on ait déjà répondu à cette question, je pense qu'il serait bon de l'expliquer. pourquoi il faut s'y attendre.

Un schéma de remplissage est généralement appliqué parce que la plupart des filtres cryptographiques ne sont pas sémantiquement sûrs et pour empêcher certaines formes de cryptoattaques. Par exemple, en général, dans RSA, le OAEP On utilise un schéma de remplissage qui empêche certains types d'attaques (telles que l'attaque par choix du texte en clair ou l'attaque par choix du texte en clair). aveuglant ).

Un système de remplissage ajoute des données (généralement) aléatoires au message m avant que celui-ci ne soit envoyé. Dans la méthode OAEP, par exemple, deux Oracles sont utilisés (il s'agit d'une explication simpliste) :

  1. Compte tenu de la taille du module, il faut remplacer k1 bits par 0 et k0 bits par un nombre aléatoire.
  2. Ensuite, en appliquant une certaine transformation au message, vous obtenez le message rembourré qui est crypté et envoyé.

Vous disposez ainsi d'une randomisation pour les messages et d'un moyen de tester si le message est un déchet ou non. Comme le schéma de remplissage est réversible, lorsque vous décryptez le message, alors que vous ne pouvez rien dire sur l'intégrité du message lui-même, vous pouvez, en fait, faire une certaine affirmation sur le remplissage et ainsi savoir si le message a été correctement décrypté ou si vous faites quelque chose de mal (c'est-à-dire que quelqu'un a altéré le message ou que vous utilisez la mauvaise clé).

17voto

Yaniv Points 21

J'ai rencontré une exception similaire "Padding is invalid and cannot be removed", mais dans mon cas, la clé IV et le padding étaient corrects.

Il s'est avéré que la purge du flux cryptographique est tout ce qui manquait.

Comme ça :

            MemoryStream msr3 = new MemoryStream();
            CryptoStream encStream = new CryptoStream(msr3, RijndaelAlg.CreateEncryptor(), CryptoStreamMode.Write);
            encStream.Write(bar2, 0, bar2.Length);
            // unless we flush the stream we would get "Padding is invalid and cannot be removed." exception when decoding
            encStream.FlushFinalBlock();
            byte[] bar3 = msr3.ToArray();

6voto

jbtule Points 11159

Si vous voulez que votre utilisation soit correcte, vous devez ajouter authentification à votre texte chiffré afin que vous puissiez vérifier qu'il s'agit du bon mot de passe ou que le texte chiffré n'a pas été modifié. Le remplissage que vous utilisez ISO10126 ne lèvera une exception que si le dernier octet n'est pas décrypté comme l'une des 16 valeurs valides pour le remplissage (0x01-0x10). Vous avez donc 1/16 de chance qu'il ne lève pas l'exception avec un mauvais mot de passe, alors que si vous l'authentifiez, vous avez un moyen déterministe de savoir si votre décryptage est valide.

La mise en œuvre de la cryptographie est faussement facile, mais il est plutôt facile de faire des erreurs. Par exemple, vous utilisez un sel fixe pour la dérivation de votre clé et de votre IV, ce qui signifie que chaque texte chiffré avec le même mot de passe réutilisera son IV avec cette clé, ce qui brise la sécurité sémantique avec le mode CBC, l'IV doit être à la fois imprévisible et unique pour une clé donnée.

Pour cette raison, j'ai un extrait de code, que j'essaie de garder révisé et à jour (commentaires, problèmes bienvenus) :

Exemples modernes de chiffrement symétrique authentifié d'une chaîne de caractères C#.

Si vous utilisez son AESThenHMAC.AesSimpleDecryptWithPassword(ciphertext, password) lorsque le mauvais mot de passe est utilisé, null est retourné, si le texte chiffré ou l'iv a été modifié après le cryptage. null est retournée, vous ne récupérerez jamais de données inutiles, ou une exception de remplissage.

3voto

Ch00k Points 5901

Oui, il faut s'y attendre, ou du moins, c'est exactement ce qui se passe lorsque nos routines de cryptage reçoivent des données non décryptables.

1voto

R D Points 248

Il peut y avoir des octets non lus dans le CryptoStream. La fermeture avant la lecture complète du flux provoquait l'erreur dans mon programme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X