Encrypting/Decrypting large files (.NET)

后端 未结 5 1346
北恋
北恋 2020-11-29 04:35

I have to encrypt, store and then later decrypt large files. What is the best way of doing that? I heard RSA encryption is expensive and was advised to use RSA to encrypt an

5条回答
  •  既然无缘
    2020-11-29 05:07

    RSA

    It's true asymmetric cryptography (RSA, ECC, etc.) is slower than symmetric (AES, ChaCha20, etc). RSA and others are great for securing a random symmetric key (or establishing one). AES and others are great for efficient encryption, used along with integrity checking (HMAC).

    Importantly, mature symmetric ciphers don't have any known theoretical weakness. Unless your attackers has the symmetric key, the encryption cannot be broken. Currently, all mature asymmetric cryptography (RSA, ECC) are based on mathematical properties that are vulnerable to being cracked by a future Quantum Computer (if it ever comes).

    Also, handling of public/private keys becomes a problem. It's simple for a human to remember a password - their brain cannot be hacked. With public/private keys, they need to be stored somewhere. Particularly the private key is sensitive. Computers have TDM components that can create and store public/private keys separate to the CPU. This is very complicated to use.

    So with that in mind, RSA should only be used if and when it's absolutely necessary.

    AES

    Here is a complete version I wrote recently, that returns the wrapping streamer, so you can use it however you need.

    Also, this method generates IV from random generator instead of the password digestor. This is best practice, for example 7z does this - see https://crypto.stackexchange.com/questions/61945/is-it-ok-to-transmit-an-iv-as-a-custom-http-header. The IV is included in the header for the output.

    Usage:

    void Save()
    {
        var encryptedFilePath = Directory.GetCurrentDirectory() + "\\data.bin.aes";
        using(var fileStream = File.Create(encryptedFilePath))
        {
            using (var cryptoStream = Security.FileEncryptor.CreateEncryptor(fileStream, passwordHere))
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(cryptoStream, myObject);
                cryptoStream.Flush();
            }
    
        }
    }
    
    void Load()
    {
        var encryptedFilePath = Directory.GetCurrentDirectory() + "\\data.bin.aes";
    
        using(var fileStream = File.Open(encryptedFilePath, FileMode.Open))
        {
            using (var cryptoStream = Security.FileEncryptor.CreateDecryptor(fileStream, passwordHere))
            {
                var formatter = new BinaryFormatter();
                var myObject = (myObjectType)formatter.Deserialize(cryptoStream);
            }
        }
    }
    

    Utility:

    using System.IO;
    using System.Security.Cryptography;
    using System;
    
    namespace Security
    {
    
        class FileEncryptor
        {
            public static Stream CreateEncryptor(Stream source, string password)
            {
                byte[] SaltBytes = new byte[16];
                RandomNumberGenerator.Fill(SaltBytes); //RandomNumberGenerator is used for .Net Core 3
    
                AesManaged aes = new AesManaged();
                aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
                aes.KeySize = aes.LegalKeySizes[0].MaxSize;
    
                Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, SaltBytes, iterations);
                aes.Key = key.GetBytes(aes.KeySize / 8);
    
                byte[] IVBytes = new byte[aes.BlockSize / 8];
                RandomNumberGenerator.Fill(IVBytes); //RandomNumberGenerator is used for .Net Core 3
                aes.IV = IVBytes;
    
                aes.Mode = CipherMode.CBC;
                ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV);
    
                //Store/Send the Salt and IV - this can be shared. It's more important that it's very random, than being private.
                source.WriteByte((byte)SaltBytes.Length);
                source.Write(SaltBytes, 0, SaltBytes.Length);
                source.WriteByte((byte)IVBytes.Length);
                source.Write(IVBytes, 0, IVBytes.Length);
                source.Flush();
    
                var cryptoStream = new CryptoStream(source, transform, CryptoStreamMode.Write);
                return cryptoStream;
            }
    
            public static Stream CreateDecryptor(Stream source, string password)
            {
                var ArrayLength = source.ReadByte();
                if (ArrayLength == -1) throw new Exception("Salt length not found");
                byte[] SaltBytes = new byte[ArrayLength];
                var readBytes = source.Read(SaltBytes, 0, ArrayLength);
                if (readBytes != ArrayLength) throw new Exception("No support for multiple reads");
    
                ArrayLength = source.ReadByte();
                if (ArrayLength == -1) throw new Exception("Salt length not found");
                byte[] IVBytes = new byte[ArrayLength];
                readBytes = source.Read(IVBytes, 0, ArrayLength);
                if (readBytes != ArrayLength) throw new Exception("No support for multiple reads");
    
                AesManaged aes = new AesManaged();
                aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
                aes.KeySize = aes.LegalKeySizes[0].MaxSize;
                aes.IV = IVBytes;
    
                Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, SaltBytes, iterations);
                aes.Key = key.GetBytes(aes.KeySize / 8);
    
                aes.Mode = CipherMode.CBC;
                ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
    
                var cryptoStream = new CryptoStream(source, transform, CryptoStreamMode.Read);
                return cryptoStream;
            }
    
            public const int iterations = 1042; // Recommendation is >= 1000.
        }
    }
    

提交回复
热议问题