【C#】共通鍵暗号(AES)で暗号化された文字列を復号する方法

目次

概要

暗号化された文字列(Base64形式)を、共通の鍵(Key)と初期化ベクトル(IV)を使用して元の平文に戻す実装です。 復号処理は暗号化の逆の手順となり、CryptoStream を「読み取りモード(Read)」で使用し、暗号化されたバイトデータをストリーム経由で平文に変換してから StreamReader で文字列として取り出します。

仕様(入出力)

  • 入力: 暗号化された文字列(Base64)、復号用のKey(バイト配列)、IV(バイト配列)。
  • 出力: 復号された平文の文字列。
  • 前提: .NET標準ライブラリ(System.Security.Cryptography)を使用。アルゴリズムはAES。

基本の使い方

復号の流れは以下の3ステップです。

  1. MemoryStream: 暗号化されたバイト配列を読み込ませる(データソース)。
  2. CryptoStream: Read モードを指定し、復号変換(Decryptor)を通すフィルターとして設置する。
  3. StreamReader: フィルターを通った結果(平文)を文字列として読み取る。
// 1. Base64をバイト配列に戻す
byte[] cipherBytes = Convert.FromBase64String(encryptedString);

// 2. ストリームを構築して読み取る
using var ms = new MemoryStream(cipherBytes);
using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read);
using var sr = new StreamReader(cs);

// 3. 文字列取得
string plainText = sr.ReadToEnd();

コード全文

入力されたコードの誤字やクラス名の不整合を修正し、単体で動作するように整形した実装です。 KeyとIVは暗号化時と完全に一致している必要があります。

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

class Program
{
    static void Main()
    {
        // 動作確認のため、正規の長さのKeyとIVを生成して使用します
        // (本来は暗号化時に使用したものを設定します)
        using var aes = Aes.Create();
        aes.KeySize = 256;
        aes.GenerateKey();
        aes.GenerateIV();

        byte[] myKey = aes.Key;
        byte[] myIV = aes.IV;

        // クラスの初期化
        var encryptor = new StringEncrypter(myKey, myIV);

        // テスト用に一度暗号化します("Hello World" を暗号化)
        // ※解説のため暗号化メソッドの実装も簡易的に含めていますが、主眼はDecryptです
        string original = "Hello World";
        string encrypted = encryptor.Encrypt(original);
        
        Console.WriteLine($"暗号文: {encrypted}");

        // --- 復号処理の実行 ---
        string decrypted = encryptor.Decrypt(encrypted);
        Console.WriteLine($"復号文: {decrypted}");
    }
}

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

    // コンストラクタ
    public StringEncrypter(byte[] key = null, byte[] iv = null)
    {
        using var aes = Aes.Create();
        // 引数がnullなら自動生成されたものを使用、指定があればそれを使用
        Key = key ?? aes.Key;
        IV = iv ?? aes.IV;
    }

    // 復号メソッド
    public string Decrypt(string encryptedText)
    {
        // 文字列が空なら即リターン
        if (string.IsNullOrEmpty(encryptedText)) return string.Empty;

        // 1. Base64文字列をバイト配列に変換
        var cipherBytes = Convert.FromBase64String(encryptedText);

        // 2. AESオブジェクトの準備
        using var aes = Aes.Create();
        aes.Key = Key;
        aes.IV = IV;

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

        // 4. ストリームチェーンの構築
        // MemoryStream(暗号データ) -> CryptoStream(復号) -> StreamReader(文字列化)
        using var memStream = new MemoryStream(cipherBytes);
        using var cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read);
        using var reader = new StreamReader(cryptoStream);

        // 5. 復号されたデータを最後まで読み取る
        return reader.ReadToEnd();
    }

    // (参考) 暗号化メソッド
    public string Encrypt(string plainText)
    {
        if (string.IsNullOrEmpty(plainText)) return string.Empty;
        using var aes = Aes.Create();
        using var encryptor = aes.CreateEncryptor(Key, IV);
        using var ms = new MemoryStream();
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        using (var sw = new StreamWriter(cs))
        {
            sw.Write(plainText);
        }
        return Convert.ToBase64String(ms.ToArray());
    }
}

カスタムポイント

  • ストリームの読み書きモード: 暗号化時は CryptoStreamMode.Write を使いますが、復号時はあるデータソースから読み出して変換するため CryptoStreamMode.Read を使用するのが一般的です。
  • エンコーディング: StreamReader はデフォルトでUTF-8を使用します。もしShift-JISなどで暗号化されたデータを復元する場合は、new StreamReader(cryptoStream, Encoding.GetEncoding("Shift_JIS")) のようにエンコーディングを指定する必要があります。

注意点

  1. パディングエラー: キーやIVが間違っている場合や、データが破損している場合、sr.ReadToEnd() の実行時に CryptographicException: Padding is invalid... (パディングが無効です)という例外が発生します。
  2. Base64形式: Decrypt メソッドに渡す文字列は、有効なBase64文字列である必要があります。URL用Base64など、記号が置換されている場合は事前に標準Base64に戻す処理が必要です。

まとめ

共有キー暗号方式における文字列の復号は、暗号化データをバイナリに戻した後、CryptoStream を読み取りモードで通過させることで実現します。この際、暗号化時と完全に同一のKeyとIVを使用することが必須条件であり、これらが不一致の場合は例外が発生し、元のデータを復元することはできません。

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

この記事を書いた人

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

目次