【C#】AES-256暗号化・復号化を実装する方法


この記事はプロモーションを含みます。

C#でAES-256を利用して文字列を暗号化・復号化する方法を紹介します。
ランダムなIV(初期化ベクトル)を生成し、暗号文の先頭へ付与する実装例です。
サンプルコードを交えながら、暗号化から復号化までの一連の流れを解説します。

C#でAES-256暗号化・復号化を実装する

AES(Advanced Encryption Standard)は、現在広く利用されている共通鍵暗号方式です。
今回は、AES-256を利用して文字列を暗号化・復号化するサンプルを紹介します。
今回のサンプルでは、以下のような流れで処理を行います。

  • 32バイトのキーを利用してAES-256で暗号化
  • 暗号化時にランダムなIV(初期化ベクトル)を生成
  • IVを暗号データの先頭へ付与
  • Base64文字列として保存・送信
  • 復号時は先頭16バイトのIVを取り出して復号

サンプルコード

以下はサンプルコードです。
サンプルでは暗号化したい文字列や暗号化キーをソースに書いてしまっているので、引数で渡せるようにした方が使いやすくなるかなと思います。

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

namespace AES256
{
    internal class Aes256
    {
        static void Main()
        {
            string str = "暗号化したい文字列";
            string key = "MySecretKey1234567890_1234567890";

            string encStr = Encrypt(str, key);
            Console.WriteLine("暗号化した文字列:" + encStr);

            string decStr = Decrypt(encStr, key);
            Console.WriteLine("復号化した文字列:" + decStr);
        }

        /// <summary>
        /// 復号化(AES-256で暗号化されたBase64文字列を復号する)
        /// </summary>
        /// <param name="cipherTextBase64">暗号化された文字列</param>
        /// <param name="password">暗号化パスワード(32バイト)</param>
        public static string Decrypt(string cipherTextBase64, string password)
        {
            if (string.IsNullOrEmpty(cipherTextBase64))
                throw new ArgumentNullException(nameof(cipherTextBase64));
            if (string.IsNullOrEmpty(password))
                throw new ArgumentNullException(nameof(password));

            // 全体のバイト配列をBase64からデコード
            byte[] allBytes = Convert.FromBase64String(cipherTextBase64);

            const int IvLength = 16;
            if (allBytes.Length < IvLength)
                throw new CryptographicException("暗号データの長さが足りません。");

            // 暗号データからIVを抽出
            byte[] ivBytes = new byte[IvLength];
            Array.Copy(allBytes, 0, ivBytes, 0, IvLength);

            byte[] keyBytes = Encoding.UTF8.GetBytes(password);

            // 復号処理
            using (Aes aes = Aes.Create())
            {
                aes.Key = keyBytes;
                aes.IV = ivBytes;

                using (ICryptoTransform decryptor = aes.CreateDecryptor())
                using (MemoryStream ms = new MemoryStream())
                {
                    
                    using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
                    {
                        // 暗号データのIV以外の部分(16バイト目以降)を取得
                        cs.Write(allBytes, IvLength, allBytes.Length - IvLength);
                        cs.FlushFinalBlock();
                    }
                    // 復号した文字列を返す
                    return Encoding.UTF8.GetString(ms.ToArray());
                }
            }
        }

        /// <summary>
        /// 暗号化(文字列をAES-256で暗号化し、先頭にIVを付与したBase64文字列を返す)
        /// </summary>
        /// <param name="plainText">暗号化する元の文字列</param>
        /// <param name="password">暗号化パスワード(32バイト)</param>
        /// <returns>暗号化されたBase64文字列</returns>
        public static string Encrypt(string plainText, string password)
        {
            if (plainText == null)
                throw new ArgumentNullException(nameof(plainText));
            if (string.IsNullOrEmpty(password))
                throw new ArgumentNullException(nameof(password));

            byte[] keyBytes = Encoding.UTF8.GetBytes(password);

            // 暗号化処理
            using (Aes aes = Aes.Create())
            {
                aes.Key = keyBytes;
                // ランダムなIVの生成
                byte[] ivBytes = aes.IV;

                using (ICryptoTransform encryptor = aes.CreateEncryptor())
                using (MemoryStream ms = new MemoryStream())
                {
                    // 先頭にIV(16バイト)を書き込む
                    ms.Write(ivBytes, 0, ivBytes.Length);

                    // IVに続けて暗号文を書き込む
                    using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                    {
                        byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
                        cs.Write(plainBytes, 0, plainBytes.Length);
                        cs.FlushFinalBlock();
                    }

                    // 暗号データ(IVと暗号文)をBase64に変換して返す
                    return Convert.ToBase64String(ms.ToArray());
                }
            }
        }
    }
}

実行例

実行すると、以下のように表示されます。

暗号化した文字列:3M3U5tjIs3IiupH9TNWcyxgKp+BpFc5gvIl6FVdzlq++G8sL23etcG+0eY/s2Qzl
復号化した文字列:暗号化したい文字列

※暗号化結果は毎回ランダムなIVが生成されるため、実行のたびに異なります。

処理のポイント

・ランダムなIVを利用する
暗号化するたびにIVをランダム生成しているため、同じ文字列を同じキーで暗号化しても、毎回異なる暗号文になります。
byte[] ivBytes = aes.IV;

・IVを暗号文に付与する
復号には暗号化時のIVが必要になるため、サンプルでは暗号文の先頭へ16バイトのIVを書き込んでいます。
ms.Write(ivBytes, 0, ivBytes.Length);

・復号時はIVを取り出す
復号時はBase64をデコードし、先頭16バイトをIVとして取り出してから復号を行います。

まとめ

C#ではSystem.Security.Cryptography名前空間を利用することで、比較的簡単にAES-256による暗号化・復号化を実装できます。

今回紹介した方法では、IVを暗号データに含める一般的な実装となっているため、文字列の保存や通信時の暗号化処理にも応用できるため、ぜひ使ってみてください。

コメント

タイトルとURLをコピーしました