C # AES: Le chiffrement d’un fichier provoque l’erreur «La longueur des données à chiffrer n’est pas valide.

J’ai un fichier PDF.
Lorsque je souhaite le chiffrer à l’aide de codes sous la Length of the data to encrypt is invalid. erreur est survenue:

  ssortingng inputFile = @"C:\sample.pdf"; ssortingng outputFile = @"C:\sample_enc.pdf"; try { using (RijndaelManaged aes = new RijndaelManaged()) { byte[] key = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; byte[] iv = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; aes.Key = key; aes.IV = iv; aes.Mode = CipherMode.CFB; aes.Padding = PaddingMode.None; aes.KeySize = 128; aes.BlockSize = 128; using (FileStream fsCrypt = new FileStream(outputFile, FileMode.Create)) { using (ICryptoTransform encryptor = aes.CreateEncryptor(key,iv)) { using (CryptoStream cs = new CryptoStream(fsCrypt, encryptor, CryptoStreamMode.Write)) { using (FileStream fsIn = new FileStream(inputFile, FileMode.Open)) { int data; while ((data = fsIn.ReadByte()) != -1) { cs.WriteByte((byte)data); } } } } } } } catch (Exception ex) { // Length of the data to encrypt is invalid. Console.WriteLine(ex.Message); } 

Avec CipherMode.CBC et PaddingMode.PKCS7 , je n’ai aucune erreur.
Mais à cause de mon client, je dois chiffrer le fichier en utilisant AES / CFB with No Padding .

Des idées que se passe-t-il ici?

    Un chiffrement de bloc attend une entrée dont la longueur est un multiple de la taille du bloc. Avec AES, la longueur de l’entrée doit être un multiple de 16.

    Vous devez appliquer une sorte de remplissage au texte en clair pour que cette exigence soit satisfaite. Le rembourrage PKCS # 7 est le meilleur choix.

    Cependant, à bien y penser, le mode CFB transforme un chiffrement par bloc en chiffrement par stream. Les chiffrements de stream n’ont pas besoin de rembourrage. L’implémentation .NET semble être cassée à cet égard.

    Une solution (non idéale) que j’ai utilisée dans cette situation est de placer la longueur brute du texte en clair dans les x premiers octets de données à chiffrer. La longueur est ensuite cryptée avec le rest des données. Lors du déchiffrement à l’aide du stream, il vous suffit de lire les x premiers octets et de les convertir à l’aide de la classe BitConverter. Cela vous indique alors combien d’octets décryptés suivants font partie de votre message. Toute donnée au-delà peut être ignorée en tant que remplissage.

    Lorsque vous utilisez PaddingMode.None, vous pouvez envelopper ICrytoTransform et gérer vous-même le bloc final:

     new CryptoStream(fsCrypt, new NoPaddingTransformWrapper(encryptor), CryptoStreamMode.Write) 

    Ce qui suit est une classe wrapper elle-même:

     public class NoPaddingTransformWrapper : ICryptoTransform { private ICryptoTransform m_Transform; public NoPaddingTransformWrapper(ICryptoTransform symmesortingcAlgoTransform) { if (symmesortingcAlgoTransform == null) throw new ArgumentNullException("symmesortingcAlgoTransform"); m_Transform = symmesortingcAlgoTransform; } #region simple wrap public bool CanReuseTransform { get { return m_Transform.CanReuseTransform; } } public bool CanTransformMultipleBlocks { get { return m_Transform.CanTransformMultipleBlocks; } } public int InputBlockSize { get { return m_Transform.InputBlockSize; } } public int OutputBlockSize { get { return m_Transform.OutputBlockSize; } } public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { return m_Transform.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } #endregion public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { if (inputCount % m_Transform.InputBlockSize == 0) return m_Transform.TransformFinalBlock(inputBuffer, inputOffset, inputCount); else { byte[] lastBlocks = new byte[inputCount / m_Transform.InputBlockSize + m_Transform.InputBlockSize]; Buffer.BlockCopy(inputBuffer,inputOffset, lastBlocks, 0, inputCount); byte[] result = m_Transform.TransformFinalBlock(lastBlocks, 0, lastBlocks.Length); Debug.Assert(inputCount < result.Length); Array.Resize(ref result, inputCount); return result; } } public void Dispose() { m_Transform.Dispose(); } } 

    OU vous pouvez encapsuler un SymmesortingcAlgorithm afin qu'il effectue le wrapping approprié dans CreateEncryptor / CreateDecryptor en fonction du mode de remplissage:

     public class NoPadProblemSymmesortingcAlgorithm : SymmesortingcAlgorithm { private SymmesortingcAlgorithm m_Algo; public NoPadProblemSymmesortingcAlgorithm(SymmesortingcAlgorithm algo) { if (algo == null) throw new ArgumentNullException(); m_Algo = algo; } public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) { if (m_Algo.Padding == PaddingMode.None) return new NoPaddingTransformWrapper(m_Algo.CreateDecryptor(rgbKey, rgbIV)); else return m_Algo.CreateDecryptor(rgbKey, rgbIV); } public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) { if (m_Algo.Padding == PaddingMode.None) return new NoPaddingTransformWrapper(m_Algo.CreateEncryptor(rgbKey, rgbIV)); else return m_Algo.CreateEncryptor(rgbKey, rgbIV); } #region simple wrap public override void GenerateIV() { m_Algo.GenerateIV(); } public override void GenerateKey() { m_Algo.GenerateIV(); } public override int BlockSize { get { return m_Algo.BlockSize; } set { m_Algo.BlockSize = value; } } public override int FeedbackSize { get { return m_Algo.FeedbackSize; } set { m_Algo.FeedbackSize = value; } } public override byte[] IV { get { return m_Algo.IV; } set { m_Algo.IV = value; } } public override byte[] Key { get { return m_Algo.Key; } set { m_Algo.Key = value; } } public override int KeySize { get { return m_Algo.KeySize; } set { m_Algo.KeySize = value; } } public override KeySizes[] LegalBlockSizes { get { return m_Algo.LegalBlockSizes; } } public override KeySizes[] LegalKeySizes { get { return m_Algo.LegalKeySizes; } } public override CipherMode Mode { get { return m_Algo.Mode; } set { m_Algo.Mode = value; } } public override PaddingMode Padding { get { return m_Algo.Padding; } set { m_Algo.Padding = m_Algo.Padding; } } protected override void Dispose(bool disposing) { if (disposing) m_Algo.Dispose(); base.Dispose(disposing); } #endregion }