2015-06-16 4 views
2

Я бы хотел добавить данные в уже зашифрованный файл (AES, CBC-Mode, padding PKCS # 7), используя CryptoStream без чтения и записи всего файла.Добавление данных в зашифрованный файл

Пример:

публикации: "Hello world..."

Новых публикации: "Hello world, with appended text"

Конечно, я должен был бы читать отдельные блоки данных и добавление затем к уже текущему блоку. В вышеупомянутом примере я должен прочитать число байт, присутствующих в первом блоке (14 байт) и добавить два байта в первый блок, а затем писать остальную часть приложенного текста

"Hello world, wi" 
"th appended text" 

Одна из проблем, я Я сталкиваюсь с тем, что я не могу прочитать количество байтов в блоке данных. Есть ли способ узнать количество присутствующих байтов (в примере, 14)?

Кроме того, я застрял, так как у CryptoStreamMode есть только члены для чтения и записи, но нет обновления.

Есть ли способ выполнить мою желаемую функциональность с помощью CryptoStream?

ответ

1

Это немного сложный, но не слишком большой. Обратите внимание, что это для режима CBC + PKCS # 7!

Три метод: WriteStringToFile создаст новый файл, AppendStringToFile будет добавлять к уже зашифрованному файлу (работает как WriteStringToFile если файл отсутствует/пустой), ReadStringFromFile будет читать из файла.

public static void WriteStringToFile(string fileName, string plainText, byte[] key, byte[] iv) 
{ 
    using (Rijndael algo = Rijndael.Create()) 
    { 
     algo.Key = key; 
     algo.IV = iv; 
     algo.Mode = CipherMode.CBC; 
     algo.Padding = PaddingMode.PKCS7; 

     // Create the streams used for encryption. 
     using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write)) 
     // Create an encryptor to perform the stream transform. 
     using (ICryptoTransform encryptor = algo.CreateEncryptor()) 
     using (CryptoStream cs = new CryptoStream(file, encryptor, CryptoStreamMode.Write)) 
     using (StreamWriter sw = new StreamWriter(cs)) 
     { 
      // Write all data to the stream. 
      sw.Write(plainText); 
     } 
    } 
} 

public static void AppendStringToFile(string fileName, string plainText, byte[] key, byte[] iv) 
{ 
    using (Rijndael algo = Rijndael.Create()) 
    { 
     algo.Key = key; 
     // The IV is set below 
     algo.Mode = CipherMode.CBC; 
     algo.Padding = PaddingMode.PKCS7; 

     // Create the streams used for encryption. 
     using (FileStream file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite)) 
     { 
      byte[] previous = null; 
      int previousLength = 0; 

      long length = file.Length; 

      // No check is done that the file is correct! 
      if (length != 0) 
      { 
       // The IV length is equal to the block length 
       byte[] block = new byte[iv.Length]; 

       if (length >= iv.Length * 2) 
       { 
        // At least 2 blocks, take the penultimate block 
        // as the IV 
        file.Position = length - iv.Length * 2; 
        file.Read(block, 0, block.Length); 
        algo.IV = block; 
       } 
       else 
       { 
        // A single block present, use the IV given 
        file.Position = length - iv.Length; 
        algo.IV = iv; 
       } 

       // Read the last block 
       file.Read(block, 0, block.Length); 

       // And reposition at the beginning of the last block 
       file.Position = length - iv.Length; 

       // We use a MemoryStream because the CryptoStream 
       // will close the Stream at the end 
       using (var ms = new MemoryStream(block)) 
       // Create a decrytor to perform the stream transform. 
       using (ICryptoTransform decryptor = algo.CreateDecryptor()) 
       using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) 
       { 
        // Read all data from the stream. The decrypted last 
        // block can be long up to block length characters 
        // (so up to iv.Length) (this with AES + CBC) 
        previous = new byte[iv.Length]; 
        previousLength = cs.Read(previous, 0, previous.Length); 
       } 
      } 
      else 
      { 
       // Use the IV given 
       algo.IV = iv; 
      } 

      // Create an encryptor to perform the stream transform. 
      using (ICryptoTransform encryptor = algo.CreateEncryptor()) 
      using (CryptoStream cs = new CryptoStream(file, encryptor, CryptoStreamMode.Write)) 
      using (StreamWriter sw = new StreamWriter(cs)) 
      { 
       // Rewrite the last block, if present. We even skip 
       // the case of block present but empty 
       if (previousLength != 0) 
       { 
        cs.Write(previous, 0, previousLength); 
       } 

       // Write all data to the stream. 
       sw.Write(plainText); 
      } 
     } 
    } 
} 

public static string ReadStringFromFile(string fileName, byte[] key, byte[] iv) 
{ 
    string plainText; 

    using (Rijndael algo = Rijndael.Create()) 
    { 
     algo.Key = key; 
     algo.IV = iv; 
     algo.Mode = CipherMode.CBC; 
     algo.Padding = PaddingMode.PKCS7; 

     // Create the streams used for decryption. 
     using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read)) 
     // Create a decrytor to perform the stream transform. 
     using (ICryptoTransform decryptor = algo.CreateDecryptor()) 
     using (CryptoStream cs = new CryptoStream(file, decryptor, CryptoStreamMode.Read)) 
     using (StreamReader sr = new StreamReader(cs)) 
     { 
      // Read all data from the stream. 
      plainText = sr.ReadToEnd(); 
     } 
    } 

    return plainText; 
} 

Пример использования:

var key = Encoding.UTF8.GetBytes("Simple key"); 
var iv = Encoding.UTF8.GetBytes("Simple IV"); 

Array.Resize(ref key, 128/8); 
Array.Resize(ref iv, 128/8); 

if (File.Exists("test.bin")) 
{ 
    File.Delete("test.bin"); 
} 

for (int i = 0; i < 100; i++) 
{ 
    AppendStringToFile("test.bin", string.Format("{0},", i), key, iv); 
} 

string plainText = ReadStringFromFile("test.bin", key, iv); 

Как с AppendStringToFile работает? Три случая:

  • Пустой файл: в WriteStringToFile
  • Файл с одного блока: ХВ этого блока является IV передается в качестве параметра. Блок расшифровывается, а затем повторно шифруется вместе с переданным plainText
  • Файл с несколькими блоками: IV последнего блока является предпоследним блоком. Таким образом, предпоследний блок читается и используется как IV (IV передается при игнорировании параметра). Последний блок расшифровывается, а затем повторно зашифровывается вместе с переданным plainText. Чтобы повторно использовать последний блок, используемый IV является предпоследним блоком.
+0

Большое спасибо за этот пример, я дам ему попробовать завтра. Один вопрос: будет ли это работать и с двоичными данными? У меня есть только что заменить StreamReader/StreamWriter на BinaryReader/BinaryWriter? Или есть что-то еще? Вопрос в том, могу ли я определить длину двоичных данных? Откуда я знаю, сколько байтов исходит из исходных данных и сколько добавочных байтов было добавлено, поэтому я могу удалить правильное количество байтов заполнения, чтобы добавить данные в правильное положение? –

+0

@rudolf Вам не нужно отбрасывать байты заполнения, они лишаются 'CryptoStream' (или' ICryptoTransform', я не уверен), потому что вы используете «хорошее» дополнение – xanatos

+0

@RudolfZiegaus У меня есть внесли некоторые изменения в метод Append, так что байты последнего блока, которые дешифрованы, не декодируются в поток, а просто повторно зашифрованы. – xanatos

Смежные вопросы