web-dev-qa-db-fra.com

"Le rembourrage n'est pas valide et ne peut pas être supprimé" à l'aide d'AesManaged

J'essaie de faire en sorte que le chiffrement/déchiffrement simple fonctionne avec AesManaged, mais je reçois toujours une exception lorsque je tente de fermer le flux de déchiffrement. La chaîne ici est chiffrée et déchiffrée correctement, puis j'obtiens l'exception CryptographicException "Le rembourrage n'était pas valide et ne peut pas être supprimé" après que Console.WriteLine imprime la chaîne correcte.

Des idées?

MemoryStream ms = new MemoryStream();
byte[] rawPlaintext = Encoding.Unicode.GetBytes("This is annoying!");

using (Aes aes = new AesManaged())
{
  aes.Padding = PaddingMode.PKCS7;
  aes.Key = new byte[128/8];
  aes.IV = new byte[128/8];

  using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(),
                                            CryptoStreamMode.Write))
  {
    cs.Write(rawPlaintext, 0, rawPlaintext.Length);
    cs.FlushFinalBlock();
  }

  ms = new MemoryStream(ms.GetBuffer());
  using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(),
                                            CryptoStreamMode.Read))
  {
    byte[] rawData = new byte[rawPlaintext.Length];
    int len = cs.Read(rawData, 0, rawPlaintext.Length);
    string s = Encoding.Unicode.GetString(rawData);
    Console.WriteLine(s);
  }
}
29
TimK

L'astuce consiste à utiliser MemoryStream.ToArray(). J'ai également changé votre code pour qu'il utilise le CryptoStream pour écrire, à la fois pour le cryptage et le décryptage. Et vous n'avez pas besoin d'appeler CryptoStream.FlushFinalBlock() explicitement, car vous l'avez dans une instruction using(), et ce vidage se produira sur Dispose(). Ce qui suit fonctionne pour moi.

byte[] rawPlaintext = System.Text.Encoding.Unicode.GetBytes("This is all clear now!");

using (Aes aes = new AesManaged())
{
    aes.Padding = PaddingMode.PKCS7;
    aes.KeySize = 128;          // in bits
    aes.Key = new byte[128/8];  // 16 bytes for 128 bit encryption
    aes.IV = new byte[128/8];   // AES needs a 16-byte IV
    // Should set Key and IV here.  Good approach: derive them from 
    // a password via Cryptography.Rfc2898DeriveBytes 
    byte[] cipherText= null;
    byte[] plainText= null;

    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(rawPlaintext, 0, rawPlaintext.Length);
        }

        cipherText= ms.ToArray();
    }


    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
        {
            cs.Write(cipherText, 0, cipherText.Length);
        }

        plainText = ms.ToArray();
    }
    string s = System.Text.Encoding.Unicode.GetString(plainText);
    Console.WriteLine(s);
}

En outre, je suppose que vous savez que vous souhaiterez définir explicitement le Mode de l'instance AesManaged et utiliser System.Security.Cryptography.Rfc2898DeriveBytes pour dériver la clé et l'IV à partir d'un mot de passe et sel.

voir également:
- AesManaged

50
Cheeso

Cette exception peut être provoquée par une incompatibilité de l'un des nombreux paramètres de chiffrement.

J'ai utilisé l'interface Security.Cryptography.Debug pour tracer tous les paramètres utilisés dans les méthodes de cryptage/décryptage.

Enfin, j'ai découvert que mon problème était que je définissais la propriété KeySize après avoir défini la Key, ce qui faisait que la classe régénérait une clé aléatoire et n'utilisait pas la clé que j'avais initialement configurée.

23
athina.bikaki

byte [] rawData = new byte [rawPlaintext.Length];

Vous devez lire la longueur du tampon, qui comprend probablement le rembourrage nécessaire (IIRC, depuis quelques années).

1
leppie

Personne n'a répondu qu'en fait MemoryStream.GetBuffer renvoie le tampon alloué, pas les vraies données dans ce tampon. Dans ce cas, il renvoie un tampon de 256 octets, alors qu'il ne contient que 32 octets de données chiffrées.

1

Pour ce que ça vaut, je vais documenter ce à quoi j'ai été confronté. J'essayais de lire le flux de mémoire du chiffreur avant la fermeture de CryptoStream. Je sais que c'était naïf et j'ai perdu une journée à le déboguer.

    public static byte[] Encrypt(byte[] buffer, byte[] sessionKey, out byte[] iv)
    {
        byte[] encrypted;
        iv = null;
        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
        {
            aesAlg.Key = sessionKey;
            iv = aesAlg.IV;
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(sessionKey, iv);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    csEncrypt.Write(buffer, 0, buffer.Length);

                    //This was not closing the cryptostream and only worked if I called FlushFinalBlock()
                    //encrypted = msEncrypt.ToArray(); 
                }

                encrypted = msEncrypt.ToArray();

                return encrypted;
            }
        }
    }

Le déplacement du flux de mémoire du chiffreur lu après la fermeture du flux de cypto a résolu le problème. Comme l'a mentionné Cheeso. Vous n'avez pas besoin d'appeler la FlushFinalBlock() si vous utilisez le bloc using.

0
Jack7