简体   繁体   中英

Is there any faster alternative to FileStream.WriteByte() method or any way to speed up the FileStream.WriteByte() in C#?

I'm a beginner of C# programming. Recently I created a simple file encrypting program and a decrypting program. First I took a file into a FileStream and altered the each byte of the file according to a password. Here's what my code

Code of the Encrypter

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;

namespace Encrypter
{
    class Program
    {
        static void Main(string[] args)
        {         
            //Actual code starts from here
            string file_path = @args[0];

            string[] splited_filepath = file_path.Split('\\');
            string file_name = splited_filepath[splited_filepath.GetLength(0) - 1];

            FileStream file;
            if (File.Exists(file_path))
            {
                file = new FileStream(file_path, FileMode.Open);
                if (!file.CanWrite)
                {
                    Console.WriteLine("The file exists, but not writable!");
                    return;
                    //goto exit;
                }
            }
            else
            {
                Console.WriteLine("The file does not exist!\n{0}", file_path);
                return;
                //goto exit;
            }

            Console.Write("Enter the password : ");
            string password = Console.ReadLine();

            if(password == "")
            {
                Console.WriteLine("Password Cannot be empty!");
                return;
            }

            try
            {
                while(file.Position < file.Length)
                { 
                    int b_input = file.ReadByte();
                    file.Position--;
                    file.WriteByte(encrypt_byte(b_input,password,file.Position));
                    file.Position++;
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
                //goto exit;
            }

            file.Close();
            string encrypted_file_name = base64_enc(file_name) + compute_hash(password, new MD5CryptoServiceProvider());
            string encrypted_file_path = create_encrypted_file_path(encrypted_file_name, splited_filepath);

            try
            {
                File.Move(file_path, encrypted_file_path);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            //exit:;
        }

        static string create_encrypted_file_path(string enc_filename, string[] splited_fp)
        {
            string output = "";
            for(int a = 0; a < splited_fp.GetLength(0) - 1; a++)
            {
                output += (splited_fp[a] + "\\");
            }
            output += enc_filename;
            return output;
        }

        //Encrypting method starts here
        static byte encrypt_byte(int input, string pass, long pos)
        {
            char[] pass_char = pass.ToArray();
            int char_pos = Convert.ToInt32(pos % pass_char.GetLength(0));
            byte output = Convert.ToByte(
                (input + Convert.ToInt32(pass_char[char_pos])) % 256
                );
            return output;
        }

        static string compute_hash(string input,HashAlgorithm algorithm)
        {
            return BitConverter.ToString(algorithm.ComputeHash(Encoding.UTF8.GetBytes(input)));
        }

        //base64 encoding method
        static string base64_enc(string input)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(input));
        }
    }
}

Code of the decrypter

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;

namespace Decrypter
{
    class Program
    {
        static void Main(string[] args)
        {
            //Actual code starts from here
            string file_path = args[0];

            string[] splited_filepath = file_path.Split('\\');
            string file_name = splited_filepath[splited_filepath.GetLength(0) - 1];

            string encrypted_filename = file_name.Substring(0, file_name.Length - 47);
            string hashed_password = file_name.Substring(file_name.Length - 47);

            FileStream file;
            if (File.Exists(file_path))
            {
                file = new FileStream(file_path, FileMode.Open);
                if (!file.CanWrite)
                {
                    Console.WriteLine("The file exists, but not writable!");
                    return;
                    //goto exit;
                }
            }
            else
            {
                Console.WriteLine("The file does not exist!\n{0}", file_path);
                Console.ReadLine();
                return;
                //goto exit;
            }

            Console.Write("Enter the password : ");
            string password = Console.ReadLine();
            if(password == "")
            {
                Console.WriteLine("Password cannot be empty!");
                return;
            }

            //Varify the password
            if(compute_hash(password,new MD5CryptoServiceProvider()) != hashed_password)
            {
                Console.WriteLine(compute_hash(password, new MD5CryptoServiceProvider()));
                Console.WriteLine(hashed_password);
                Console.WriteLine("Invalid password!");
                return;
            }

            try
            {
                while(file.Position < file.Length)
                {                
                    int b_input = file.ReadByte();
                    file.Position--;
                    file.WriteByte(decrypt_byte(b_input, password, file.Position));
                    file.Position++;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
                return;
                //goto exit;
            }
            file.Close();
            string decrypted_filename = base64_dec(encrypted_filename);
            string decrypted_filepath = create_decrypted_file_path(decrypted_filename, splited_filepath);

            try
            {
                File.Move(file_path, decrypted_filepath);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }

            //exit:;
        }

        //Decrypting method starts here
        static byte decrypt_byte(int input, string pass, long pos)
        {
            char[] pass_char = pass.ToArray();
            int char_pos = Convert.ToInt32(pos % pass_char.GetLength(0));
            byte output = Convert.ToByte(
                get_output_byte(input - Convert.ToInt32(pass_char[char_pos]))
                );
            return output;
        }

        //a simple method to get the numaric value of the output byte
        static int get_output_byte(int number)
        {
            if (number < 0) return 256 + number;
            else return number;
        }

        static string compute_hash(string input, HashAlgorithm algorithm)
        {
            return BitConverter.ToString(algorithm.ComputeHash(Encoding.UTF8.GetBytes(input)));
        }

        //base64 decoding method
        static string base64_dec(string input)
        {
            return Encoding.UTF8.GetString(Convert.FromBase64String(input));
        }

        static string create_decrypted_file_path(string enc_filename, string[] splited_fp)
        {
            string output = "";
            for (int a = 0; a < splited_fp.GetLength(0) - 1; a++)
            {
                output += (splited_fp[a] + "\\");
            }
            output += enc_filename;
            return output;
        }
    }
}

The problem is both of these programs are considerably slow and it takes hours to encrypt or decrypt large files. Any suggestions to speed up the process?

This is about the worst possible way I can think of to use a FileStream in a loop:

int b_input = file.ReadByte();
file.Position--;
file.WriteByte(encrypt_byte(b_input,password,file.Position));
file.Position++;

Okay, the first line isn't bad. The second line causes the stream to flush its write buffer and invalidate its read buffer. Then we write a new byte and adjust the position again, causing another flush and invalidation (and causes us to skip every other byte to boot, because the WriteByte already updated the file position).

Simple solution is to use File.ReadAllBytes and just operate in memory. However, as you yourself observed, this doesn't work well for large files.

Instead, you should work with a reasonably sized buffer (say var buffer = new byte[4096]; ) and then use file.Read to read the file in chunks 1 . It's better if you can also use a separate stream pointing at a second file for the write side so that you still benefit from buffers on both sides.

You have to maintain your own count of the position within the file rather than relying on the file's Position property, and your loop should terminate when Read returns 0 (but make sure you're always paying attention to that value and only process that much of the buffer).

Finally, close both streams, delete the old file and move/rename the new file to replace it.


Note that this sort of "encryption" is suitable for toy usage but shouldn't be seriously used for anything. You're already aware of the System.Security.Cryptography namespace. For any serious encryption work, you should be looking for existing classes in there, not rolling your own.


1 Consider also using Async variants. Most of the time will still be spent doing I/O . Blocking a thread whilst that's happening doesn't add much value to the system.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM