概要
- 次の実行環境を使用しています。
OS Windows 10(64ビット) IDE Microsoft Visual Studio Community 2022(17.1.3) 言語 C#(10.0) + .NET6 - 次のリファレンスを参考にしています。
AES-256の概要
- AES-256(CBC)はCRYPTRECから推奨される安全な暗号化技術です。
- 暗号化に使用するキーや初期ベクトルについて
- 暗号化のキーやIV値を文字列から生成するサンプルを見かけます。サンプルとしては良いのですが、強度が低くなる(予測しやすい)のでお薦めしません。
- より安全な暗号化キーの生成をこちらで紹介しています。
- AESクラスでキーや初期ベクトルを生成するためのメソッドが提供されているので、それらの使用をお薦めします。
- 独自にキーや初期ベクトルを作成したい場合は、暗号用乱数クラスを使用する必要があります。
- ユーザの入力内容に基づいてキーを生成する場合、Argon2idやPBKDF2などの鍵導出関数の使用をお薦めします。
リンク
暗号化・復号化サンプル
- ここでは「ストリームを使ったサンプル」(CryptoStreamクラスを使用)と、「バイト列を使ったサンプル」(TransformFinalBlockを使用)のサンプルを紹介します。基本的には、少ないメモリ使用量で大きなファイルを暗号化・復号化できるストリームを使った方法をお薦めします。
- RFC2828の考えに沿って、暗号化処理の入力となる平文をcleartext、暗号化処理の出力となる暗号文をciphertextと表記します。平文・暗号文はバイナリデータを意味しており、文字列(文章)の他に画像や音声などを暗号化する場合にも使用されます。
- 完全なソースコードはこちらで公開しています。GitHub
Contribute to nextdoorwith/example-dotnet6 development by cr…
ストリームを使ったサンプル
- 暗号化・復号化のメイン処理のサンプルです。
- AES関連の操作は、AesクラスやAesクラスから取得したEncryptor/Decryptor(ICryptoTransform)等のオブジェクトで実現できます。
- キーやIVは独自に生成も可能ですが、AesのGenerateKey(), GenerateIV()で簡単に生成できます。
123456789101112131415using 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クラスで行っています。12345678910111213141516171819202122232425262728293031323334public 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ステートメントの使用をお薦めします。
バイト列を使ったサンプル
- 暗号化・復号化のメイン処理のサンプルです。
- キーや初期ベクトルの生成方法は、前述の「ストリームを使ったサンプル」と同様です。
1234567891011using 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()メソッドで行っています。123456789101112131415161718192021222324252627public 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()だけで全てのブロックを暗号化・復号化できるようです。
リンク