Encrypting/decrypting a file line by line?

2020-07-17 05:16发布

问题:

I'm fairly novice to encryption, and I'm trying to get a line-by-line encryptor working; I need to be able to append encrypted lines to a file as I go during an application's run, rather than just one big massive encrypt-everything-and-save. I'm having a beast of a time with it though. Here's my encryptor, shamelessly stolen after several failed attempts at my own:


class Encryption
    {
        private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

        public static byte[] Encrypt(byte[] plain, string password)
        {
            MemoryStream memoryStream;
            CryptoStream cryptoStream;
            Rijndael rijndael = Rijndael.Create();
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
            rijndael.Key = pdb.GetBytes(32);
            rijndael.IV = pdb.GetBytes(16);
            memoryStream = new MemoryStream();
            cryptoStream = new CryptoStream(memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
            cryptoStream.Write(plain, 0, plain.Length);
            cryptoStream.FlushFinalBlock();
            cryptoStream.Close();
            return memoryStream.ToArray();
        }

        public static byte[] Decrypt(byte[] cipher, string password)
        {
            MemoryStream memoryStream;
            CryptoStream cryptoStream;
            Rijndael rijndael = Rijndael.Create();
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
            rijndael.Key = pdb.GetBytes(32);
            rijndael.IV = pdb.GetBytes(16);
            memoryStream = new MemoryStream();
            cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write);
            cryptoStream.Write(cipher, 0, cipher.Length);
            cryptoStream.FlushFinalBlock();
            cryptoStream.Close();
            return memoryStream.ToArray();
        }
    }

And here's a dummy function showing how I'm attempting it:


       private void EncryptFile(string filepath, string outputPath, string password)
        {
            FileInfo fileInfo = new FileInfo(filepath);
            string filename = fileInfo.Name;

            string fullpath = outputPath + "\\" + filename;

            BinaryWriter writer = new BinaryWriter(File.OpenWrite(fullpath), Encoding.ASCII);

            /// Two methods that I've attempted here:
            /// 1.  The desired method: encrypt line by line - I assumed I'd be able to generate
            ///     multiple blocks of data and decrypt them later.  This isn't working

            //string[] lines = File.ReadAllLines(filepath);

            /// 2.  Just read the whole thing and encrypt and write it in one swoop.

            string line = File.ReadAllText(filepath);

            //foreach(string line in lines)
            {
                byte[] bytes = Encoding.ASCII.GetBytes(line);
                byte[] encoded = Encryption.Encrypt(bytes, password);

                writer.Write(encoded);
                writer.Flush();
            }

            writer.Close();
        }



        private void DecryptFile(string filepath, string outputPath, string password)
        {
            FileInfo fileInfo = new FileInfo(filepath);
            string filename = fileInfo.Name;
            string fullpath = outputPath + "\\" + filename;

            StreamWriter writer = new StreamWriter(fullpath, false, Encoding.UTF8);

            byte[] bytes = File.ReadAllBytes(filepath);

            ///  Here is the method that's working at the moment for decrypting; just
            ///  grab all the data and decrypt it on one swoop.

            byte[] decrypted = Encryption.Decrypt(bytes, password);

            string s = Encoding.ASCII.GetString(decrypted);

            writer.Write(s);
            writer.Flush();


            ///  I've tried a number of things here to decrypt line by line,
            ///  none of which work.  This crashes with an issue about the padding
            ///  being invalid.  

            /*
            int index = 0;
            int count = 32;

            while (index 

I'm not entirely sure what I should be doing anymore. I've been wandering around poking at things and reading through examples online, but they all seem to be how to encrypt a whole file or just encrypt a piece of data and do nothing with it other than immediately decrypt it again. How should I handle line-by-line writing?

回答1:

Instead of programming this for you, I'll give you a scheme which you could implement.

If you have per line encryption, I presume you want to be able to decrypt per line as well. Note that a "line" is a rather inconvenient term for a computer. It's just a bunch of characters that end with some kind of line terminator. The characters in themselves are encoded using a specific character-encoding.

Furthermore, I'll make the following assumptions:

  • the encrypted text should be presented in lines, which means that the bytes need to converted from binary to characters (using an encoding);
  • you want a single key to be used, while staying secure;
  • you don't need integrity protection or authentication of the cipher text, only confidentiality.

Now the idea is simple:

  1. create a single key using a password based key derivation function;
  2. open the text file for reading (using the correct character-encoding);
  3. read a line and convert the line to bytes again (e.g. using UTF-8);
  4. create an output stream that creates a byte array underneath;
  5. create a random IV and write it to the byte array (IV is always a single block size);
  6. create a crypto stream and connect it to the same output stream;
  7. encrypt the encoded line;
  8. base 64 encode the encrypted bytes, and make sure it stays in one line;
  9. write the encoded, encrypted line to a new text file.

To do the opposite, reverse the process, although the IV should be retrieved from the bytes after base 64 decoding, and the key should of course be calculated using the same method as used during encryption.