概要
- 次の実行環境を使用しています。
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()で簡単に生成できます。
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()だけで全てのブロックを暗号化・復号化できるようです。
リンク
