NDW

アプリ開発やトラブルシューティング等のノウハウ、キャンプや登山の紹介や体験談など。

.NET Core(C#) 1. システムエンジニアリング 実装技術

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

投稿日:2022年8月23日 更新日:

概要

AES-256の概要

暗号化・復号化サンプル

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

  • 暗号化・復号化のメイン処理のサンプルです。
    • 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()だけで全てのブロックを暗号化・復号化できるようです。







-.NET Core(C#), 1. システムエンジニアリング, 実装技術

関連記事

保守運用

運用 と 保守 の 違い

若い頃は 運用 と 保守 の違いを調べても良くわからなかった… この辺を使い分けられる人をほとんど見たことない… ある事項が運用なのか保守なのかの話をすると認識が合わない&#8 …

mavenマルチモジュールプロジェクトの構成例

システム開発でよく使用するmavenマルチモジュールプロジェクトの構成サンプルを説明します。 構成方針 複数のサブシステムをもつシステム開発を想定しています。システム名はzzz、サブシステムはf10, …

ルートCA、サーバ・クライアント証明書の作成方法

概要 検証・開発環境での使用を前提とした、ルートCA証明書の作成、その証明書で署名したサーバ証明書とクライアント証明書を作成する手順を説明します。 可能な限りシンプルで簡単な条件・手順になっています。 …

JavaEE7のJSF, Facelets, JSPの関係

JavaEEを使ったアプリ開発の際に、いつも気になるが後回しにしていたこと… HTML5への対応方法の調査等、今後の理解促進のために、調べてみた。 FaceletsとJSFとの関係は? J …

マスタデータ生成ツール

開発や結合試験、本番環境等で使用するマスタデータをExcelで管理することがあります。 そのようなExcelファイルからDBに登録するためのインサート文を作成するために、いつもツールに悩むので作成して …