[C#]データをAESで暗号化してファイルに保存する

# やりたいこと
テキストとして読まれたくないが、ファイル保存しておきたい文字列があるときに、暗号化してファイル保存して、使うときに復号化するようなことがしたい。

# やり方
AesCngというクラスを使用して、暗号化・復号化を行う。

# サンプルコード

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

namespace ConsoleApp9
{
    class Program
    {
        static void Main(string[] args)
        {
            // "あいうえお"を暗号化
            AesEncrypter.WriteAesEncryptedBytesToFile("あいうえお", @"C:\work\test.bin");

            // 暗号化したデータを読み出し復号化する
            var d = AesEncrypter.ReadAesEncryptedBytesFromFile(@"C:\work\test.bin");

            Console.WriteLine(d);
            Console.ReadLine();
        }
    }

    public class AesEncrypter
    {
        ///IV 半角16文字のランダムな文字列
        private static readonly string AesIV = @"xxxxxxxxxxxxxxxx";

        // キー 半角32文字のランダムな文字列
        // (1文字あたり8bit→8*32=256bit→キーサイズ)
        private static readonly string AesKey = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

        // キーサイズ(bit)
        private static readonly int KeySize = 256;

        // ブロックサイズ
        private static readonly int BlockSize = 128;

        // 暗号化
        public static byte[] Encrypt(string value)
        {
            // AESオブジェクトを取得
            var aes = GetAesCng();

            // 対象の文字列をバイトデータに変換
            var byteValue = Encoding.UTF8.GetBytes(value);

            // バイトデータの長さを取得
            var byteLength = byteValue.Length;

            // 暗号化オブジェクトを取得
            var encryptor = aes.CreateEncryptor();

            // 暗号化
            return encryptor.TransformFinalBlock(byteValue, 0, byteLength);
        }

        // 復号化
        public static string Decrypt(byte[] encryptValue)
        {
            // AESオブジェクトを取得
            var aes = GetAesCng();

            // 復号化オブジェクトを取得
            var decryptor = aes.CreateDecryptor();

            // 復号化
            var decryptValue = decryptor.TransformFinalBlock(encryptValue, 0, encryptValue.Length);

            // 復号化されたバイトデータを文字列に変換
            var stringValue = Encoding.UTF8.GetString(decryptValue);

            return stringValue;
        }

        // AESオブジェクトを取得
        private static AesCng GetAesCng()
        {
            // AESオブジェクトを生成し、パラメータを設定します。
            var aes = new AesCng();
            aes.KeySize = KeySize;
            aes.BlockSize = BlockSize;
            aes.Mode = CipherMode.CBC;
            aes.IV = Encoding.UTF8.GetBytes(AesIV);
            aes.Key = Encoding.UTF8.GetBytes(AesKey);
            aes.Padding = PaddingMode.PKCS7;

            return aes;
        }

        // 文字列を暗号化してファイルに書き出す
        // targetString 暗号化したい文字列
        // keyFilePath 暗号化した文字列を書き込むファイル
        public static void WriteAesEncryptedBytesToFile(string targetString, string keyFilePath)
        {
            byte[] encrypted;

            try
            {
                encrypted = Encrypt(targetString);

                string folderPath = Path.GetDirectoryName(keyFilePath);
                if (!Directory.Exists(folderPath))
                {
                    Directory.CreateDirectory(folderPath);
                }

                using (var fs = new FileStream(keyFilePath, FileMode.Create))
                using (var bw = new BinaryWriter(fs))
                {
                    bw.Write(encrypted);
                }
            }
            catch
            {
                throw;
            }
        }

        // 暗号化されたファイルからキーを読を復号して返す
        // keyFilePath キーファイルパス
        // return ： 復号化された文字列
        public static string ReadAesEncryptedBytesFromFile(string keyFilePath)
        {
            string roundtrip;

            try
            {
                using (var fs = new FileStream(keyFilePath, FileMode.Open))
                using (var br = new BinaryReader(fs))
                {
                    var encrypted = new byte[fs.Length];
                    br.Read(encrypted, 0, (int)fs.Length);

                    roundtrip = Decrypt(encrypted);
                }
            }
            catch
            {
                throw;
            }

            return roundtrip;
        }
    }
}

```
# 注意
暗号化の際、IVやキーが必要になるため、コードの中に「AesIV」「AesKey」として埋め込まれているが、キーなどの値がわかってしまうと、簡単に解読(復号)されてしまう。
.NETのコードは、ILSpyなどのツールを使うと簡単にコードが読めるようにできてしまうため、このコードをこのまま使うようなら、難読化などの対策が必要。

# メモ
下記のクラスでも、同じようにAESの暗号化が行える

- AesCryptoServiceProvider（.NET Framework3.5以降で使用できるクラス。）
- AesManaged（.NET Framework3.5以降で使用できるクラス。これはマネージドコードで全部書かれてるらしい）
- AesCng（.NET Framework4.6.2以降で使用できる、現段階(2019年)で新しいクラス。）

また、上記のAesCngクラスの継承元のAesクラスの、さらに継承元の[SymmetricAlgorithm](https://docs.microsoft.com/ja-jp/dotnet/api/system.security.cryptography.symmetricalgorithm?view=netframework-4.8)というクラスがある。これを継承して、AESだけでなく、DES、RC2、Rijndael、TripleDESなどの暗号化用のクラスが用意されている。

# 参考
AesCng Class
https://docs.microsoft.com/ja-jp/dotnet/api/system.security.cryptography.aescng?view=netframework-4.8

C#で文字列を暗号化・復号化する。
ほぼほぼこちらを参考にさせてもらってます。
https://paveway.hatenablog.com/entry/2019/04/08/csharp_encrypt

Rijndaelクラスを使ったAES暗号化
https://qiita.com/kz-rv04/items/62a56bd4cd149e36ca70