C#: AES-256を使った暗号化・復号化サンプル

概要

AES-256の概要

暗号化・復号化サンプル

  • ここでは「ストリームを使ったサンプル」(CryptoStreamクラスを使用)と、「バイト列を使ったサンプル」(TransformFinalBlockを使用)のサンプルを紹介します。基本的には、少ないメモリ使用量で大きなファイルを暗号化・復号化できるストリームを使った方法をお薦めします。
  • RFC2828の考えに沿って、暗号化処理の入力となる平文をcleartext、暗号化処理の出力となる暗号文をciphertextと表記します。平文・暗号文はバイナリデータを意味しており、文字列(文章)の他に画像や音声などを暗号化する場合にも使用されます。
  • 完全なソースコードはこちらで公開しています。
    GitHub

    Contribute to nextdoorwith/example-dotnet6 development by cr…

ストリームを使ったサンプル

  • 暗号化・復号化のメイン処理のサンプルです。
    • AES関連の操作は、AesクラスやAesクラスから取得したEncryptor/Decryptor(ICryptoTransform)等のオブジェクトで実現できます。
    • キーやIVは独自に生成も可能ですが、AesのGenerateKey(), GenerateIV()で簡単に生成できます。
    using var aes = Aes.Create();
    aes.GenerateKey();
    aes.GenerateIV();
    var key = aes.Key;
    var iv = aes.IV;
    
    var text = "テストデータ";
    var cleartext = Encoding.UTF8.GetBytes(text);
    
    using var output = new MemoryStream();
    AesExample.Encrypt(output, cleartext, key, iv);
    var ciphertext = output.ToArray();
    
    using var input = new MemoryStream(ciphertext);
    var decrypted = AesExample.Decrypt(input, key, iv);
  • 前述のメイン処理から呼び出している暗号化・復号化処理メソッドです。
    このサンプルでは、平文はバイト列(byte[]型)、暗号文はストリーム(Stream型)を想定しています。暗号化の主要処理はCryptoStreamクラスで行っています。

    public static void Encrypt(Stream output, byte[] cleartext, byte[] key, byte[] iv)
    {
        using var aes = CreateInstance();
        aes.Key = key;
        aes.IV = iv;
        using var encryptor = aes.CreateEncryptor();
        using var cryptoStream =
            new CryptoStream(output, encryptor, CryptoStreamMode.Write);
        cryptoStream.Write(cleartext, 0, cleartext.Length);
        cryptoStream.Flush();
    }
    
    public static byte[] Decrypt(Stream input, byte[] key, byte[] iv)
    {
        using var aes = CreateInstance();
        aes.Key = key;
        aes.IV = iv;
        using var decryptor = aes.CreateDecryptor();
        using var cryptoStream =
            new CryptoStream(input, decryptor, CryptoStreamMode.Read);
        using var output = new MemoryStream();
        cryptoStream.CopyTo(output);
        return output.ToArray();
    }
    
    private static Aes CreateInstance()
    {
        var aes = Aes.Create();
        aes.BlockSize = 128; // 既定値(AESの有効なブロックサイズは128bit固定)
        aes.KeySize = 256; // 既定値は256(128, 192, 256bitを使用可)
        aes.Mode = CipherMode.CBC; // 既定値
        aes.Padding = PaddingMode.PKCS7; // 既定値
        return aes;
    }
    • 暗号化・復号化にCryptoStreamを使用します。ストリームに対して書込みや読み込みを行うことで自動的に暗号化・復号化が行われます。
    • FileStream, NetworkStream等の他のストリームクラスと併せて使用することができます。
    • CSVファイル等の文字列を前提としたデータの暗号化・復号化を行う場合、CryptStreamをStreamReader/StreamWriter等で開くことで、ReadLine(), ReadToEnd(), WriteLine()等の柔軟なテキスト操作が可能になります。
    • 複数のストリームをネストして開いており、処理終了時にこれらのストリームを解放する必要があります。マイクロソフト等のサンプルでは、usingステートメントをネストして使用していますが、ここではusing宣言を使用してフラットな構造にしています。(using宣言の仕様では、"The using locals will then be disposed in the reverse order in which they are declared."とあり、宣言の逆順で解放される旨が記載されています。)
    • なお、ストリーム(リソース)の有効範囲を狭めて積極的に開放したい場合は、従来のusingステートメントの使用をお薦めします。

バイト列を使ったサンプル

  • 暗号化・復号化のメイン処理のサンプルです。
    • キーや初期ベクトルの生成方法は、前述の「ストリームを使ったサンプル」と同様です。
    using var aes = Aes.Create();
    aes.GenerateKey();
    aes.GenerateIV();
    var key = aes.Key;
    var iv = aes.IV;
    
    var text = "テストデータ";
    var cleartext = Encoding.UTF8.GetBytes(text);
    
    var ciphertext = AesExample.Encrypt(cleartext, key, iv);
    var decrypted = AesExample.Decrypt(ciphertext, key, iv);
  • 前述のメイン処理から呼び出している暗号化・復号化処理メソッドです。
    このサンプルでは、平文も暗号文もバイト列(byte[]型)を想定しています。暗号化の主要処理はEncryptor/Decryptor(ICryptoTransform)のTransformFinalBlock()メソッドで行っています。

    public static byte[] Encrypt(byte[] cleartext, byte[] key, byte[] iv)
    {
        using var aes = CreateInstance();
        aes.Key = key;
        aes.IV = iv;
        using var encryptor = aes.CreateEncryptor();
        return encryptor.TransformFinalBlock(cleartext, 0, cleartext.Length);
    }
    
    public static byte[] Decrypt(byte[] ciphertext, byte[] key, byte[] iv)
    {
        using var aes = CreateInstance();
        aes.Key = key;
        aes.IV = iv;
        using var decryptor = aes.CreateDecryptor();
        return decryptor.TransformFinalBlock(ciphertext, 0, ciphertext.Length);
    }
    
    private static Aes CreateInstance()
    {
        var aes = Aes.Create();
        aes.BlockSize = 128; // 既定値(AESの有効なブロックサイズは128bit固定)
        aes.KeySize = 256; // 既定値は256(128, 192, 256bitを使用可)
        aes.Mode = CipherMode.CBC; // 既定値
        aes.Padding = PaddingMode.PKCS7; // 既定値
        return aes;
    }
    • AESは平文・暗号文を所定の長さのデータ(ブロック)に分けて、暗号化・復号化します。ICryptoTransformには、TransformBlock()TransformFinalBlock()メソッドが用意されており、途中ブロックはTransformBlock()、最後のブロックはTransformFinalBlock()を使用するように思えます。しかしながら、TransformFinalBlock()だけで全てのブロックを暗号化・復号化できるようです。