[C#] How to Decrypt Files Using Symmetric Key Encryption (AES)

目次

Overview

To restore an encrypted file to its original state (decryption), you perform the reverse stream process of encryption. By passing the encrypted data read from a file through a CryptoStream in read mode, the data is converted back into plaintext and written to another file. This process also uses stream copying, which allows for restoring large files while keeping memory consumption low.

Specifications (Input/Output)

  • Input: The encrypted file stream, the Key, and the IV.
  • Output: The destination file stream where the decrypted data is written.
  • Behavior: Reads data from the encrypted stream, decrypts it using AES, and sequentially writes it to the output file.

Basic Usage

Wrap the encrypted input file stream with a CryptoStream and read it in decryption mode (Read).

// Source (encrypted file)
using var fsIn = File.OpenRead("encrypted.dat");

// Stream that passes through the decryption filter
// Input(fsIn) -> Decryption(cs) -> Read
using var cs = new CryptoStream(fsIn, decryptor, CryptoStreamMode.Read);

// Destination (restored file)
using var fsOut = File.Create("restored.txt");

// Write the decrypted data
cs.CopyTo(fsOut);

Full Code Example

The following is an implementation for file decryption that pairs with the encryption logic. The Key and IV must be exactly the same byte arrays used during encryption.

using System;
using System.IO;
using System.Security.Cryptography;

class Program
{
    static void Main()
    {
        // For demonstration, assume a valid Key and IV are prepared.
        // In a real scenario, use the Key/IV output during encryption.
        using var aes = Aes.Create();
        aes.GenerateKey();
        aes.GenerateIV();
        byte[] validKey = aes.Key;
        byte[] validIv = aes.IV;

        // 1. Initialize the decryption class
        var encrypter = new StreamEncrypter(validKey, validIv);

        string encryptedFile = "crypted.dat";
        string restoredFile = "original.txt";

        // (Test: Create a dummy file if it does not exist)
        if (!File.Exists(encryptedFile))
        {
            File.WriteAllBytes(encryptedFile, new byte[16]); 
            Console.WriteLine("*Created a dummy file for testing. It cannot be decrypted correctly.");
        }

        Console.WriteLine($"Starting decryption: {encryptedFile} -> {restoredFile}");

        try
        {
            // 2. Open streams and execute decryption
            using (var inStream = File.Open(encryptedFile, FileMode.Open, FileAccess.Read))
            using (var outStream = File.Open(restoredFile, FileMode.Create, FileAccess.Write))
            {
                encrypter.Decrypt(inStream, outStream);
            }

            Console.WriteLine("Decryption completed.");
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine($"Encryption Error: The Key or IV is incorrect, or the file is corrupted.\n{ex.Message}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

public class StreamEncrypter
{
    public byte[] Key { get; private set; }
    public byte[] IV { get; private set; }

    public StreamEncrypter(byte[] key = null, byte[] iv = null)
    {
        using var aes = Aes.Create();
        Key = key ?? aes.Key;
        IV = iv ?? aes.IV;
    }

    /// <summary>
    /// Decrypts the input stream (encrypted) and writes it to the output stream (plaintext).
    /// </summary>
    public void Decrypt(Stream inStream, Stream outStream)
    {
        using var aes = Aes.Create();
        aes.Key = Key;
        aes.IV = IV;

        // Create the decryptor
        using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

        // Wrap the input stream with CryptoStream (Mode.Read)
        // Decryption occurs when "reading" from cryptoStream
        using var cryptoStream = new CryptoStream(inStream, decryptor, CryptoStreamMode.Read);
        
        // Copy all decrypted data to the output stream
        cryptoStream.CopyTo(outStream);
    }
}

Customization Points

  • Stream Direction: While encryption usually involves wrapping the outStream and using Write, decryption typically wraps the inStream and uses Read. This follows the intuitive flow of “reading from encrypted data to restore it.”
  • Padding: Since AES processes data in blocks (16 bytes), the end of the file contains “padding.” When the CryptoStream is fully read (at the time it is closed or disposed), this padding is automatically removed, returning the file to its original size.

Important Notes

  • Key Mismatch: The most common error is CryptographicException: Padding is invalid and cannot be removed. This happens when the Key or IV differs from those used during encryption.
  • Incomplete Termination: If CopyTo is interrupted before finishing, the restored file will be incomplete or corrupted. Ensure you use a using block to complete the process reliably.

Conclusion

File decryption is achieved by using the Read mode of CryptoStream. By wrapping the input stream (encrypted file) with CryptoStream and pouring the resulting decrypted data into the output stream (restored file) via the CopyTo method, the process is completed. Identical Key and IV values are essential for successful decryption; otherwise, a padding error will cause the process to fail.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次