【C#】共通鍵暗号(AES)で暗号化されたファイルを復号する方法

目次

概要

暗号化されたファイルを元の状態に戻す(復号する)には、暗号化時と逆のストリーム処理を行います。 ファイルから読み込んだ暗号データを CryptoStream(読み取りモード)に通すことで平文に戻し、その結果を別のファイルへ書き出します。この処理もストリーム間コピーを利用するため、メモリ消費を抑えながら巨大なファイルを復元できます。

仕様(入出力)

  • 入力: 暗号化されたファイルのストリーム、Key、IV。
  • 出力: 復号されたデータを書き込む先のファイルストリーム。
  • 動作: 暗号化ストリームから読み出したデータをAESで復号し、順次出力ファイルへ書き込む。

基本の使い方

暗号化された入力ファイルストリームを CryptoStream でラップし、復号モード(Read)で読み出します。

// 入力元(暗号化ファイル)
using var fsIn = File.OpenRead("encrypted.dat");

// 復号用フィルタを通すストリーム
// 入力(fsIn) -> 復号(cs) -> 読み出し
using var cs = new CryptoStream(fsIn, decryptor, CryptoStreamMode.Read);

// 出力先(復元ファイル)
using var fsOut = File.Create("restored.txt");

// 復号されたデータを書き込む
cs.CopyTo(fsOut);

コード全文

前回の暗号化コードと対になる、ファイル復号の実装です。 KeyとIVは、必ず暗号化時と同じバイト配列を指定する必要があります。

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

class Program
{
    static void Main()
    {
        // 動作確認のため、事前に正規のKeyとIVを用意したと仮定します
        // (本来は暗号化時に出力されたKey/IVを使用してください)
        // ここではサンプル用に新規生成していますが、これでは既存のファイルは復号できません
        using var aes = Aes.Create();
        aes.GenerateKey();
        aes.GenerateIV();
        byte[] validKey = aes.Key;
        byte[] validIv = aes.IV;

        // 1. 復号クラスの初期化
        var encrypter = new StreamEncrypter(validKey, validIv);

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

        // (テスト用:ファイルがないとエラーになるため、ダミー作成)
        if (!File.Exists(encryptedFile))
        {
            // 本来は暗号化されたファイルが必要
            File.WriteAllBytes(encryptedFile, new byte[16]); 
            Console.WriteLine("※テスト用のダミーファイルを作成しました。正しく復号できません。");
        }

        Console.WriteLine($"復号開始: {encryptedFile} -> {restoredFile}");

        try
        {
            // 2. ストリームを開いて復号を実行
            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("復号が完了しました。");
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine($"暗号化エラー: KeyまたはIVが間違っているか、ファイルが破損しています。\n{ex.Message}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"エラー: {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>
    /// 入力ストリーム(暗号)を復号して出力ストリーム(平文)に書き込みます
    /// </summary>
    public void Decrypt(Stream inStream, Stream outStream)
    {
        using var aes = Aes.Create();
        aes.Key = Key;
        aes.IV = IV;

        // 復号器(Decryptor)を作成
        using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

        // 入力ストリームをCryptoStreamでラップする (Mode.Read)
        // cryptoStreamから「読む」ときに復号処理が行われる
        using var cryptoStream = new CryptoStream(inStream, decryptor, CryptoStreamMode.Read);
        
        // 復号されたデータをすべて出力ストリームにコピーする
        cryptoStream.CopyTo(outStream);
    }
}

カスタムポイント

  • ストリームの向き: 暗号化時は outStream をラップして Write しましたが、復号時は inStream をラップして Read する構成が一般的です(直感的に「暗号データから読み出して元に戻す」流れになるため)。
  • パディング: AESはブロック単位(16バイト)で処理するため、ファイル末尾にはパディング(埋め草)が含まれています。CryptoStream を読み切る(CloseまたはDisposeされる)タイミングで、このパディングが自動的に削除され、元のファイルサイズに戻ります。

注意点

  1. 鍵の不一致: 最も多いエラーは CryptographicException: Padding is invalid and cannot be removed. です。これはKeyまたはIVが暗号化時と異なる場合に発生します。
  2. 途中終了: CopyTo が最後まで完了せずに処理が中断した場合、復元されたファイルは不完全な状態(破損ファイル)となります。using ブロックで確実に完了させる必要があります。

まとめ

ファイルの復号処理には CryptoStreamRead モードを使用します。入力ストリーム(暗号化ファイル)を CryptoStream で包み込み、そこから出てくる復号データを CopyTo メソッドで出力ストリーム(復元ファイル)へ流し込むことで完了します。正常に復号するためには、暗号化時に使用したものと完全に一致するKeyとIVが不可欠であり、これらが異なるとパディングエラーにより処理は失敗します。

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

この記事を書いた人

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

目次