はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】AES(Advanced Encryption Standard)暗号化をしてみる

はじめに

今回はAES(Advanced Encryption Standard)暗号を利用してみるという記事になります。

Advanced Encryption Standard (AES) は、アメリカが2001年に標準暗号として定めた共通鍵暗号アルゴリズムである。アメリカ国立標準技術研究所(NIST)が公募し、Rijndael(ラインダール)がAESとして採用された[4]。

AESはSPN構造のブロック暗号である。ブロック長は128ビットであり、鍵長には128ビット・192ビット・256ビットの3種類が選択できる(鍵長が大きいほうが暗号強度が高い)。これに対し、AESの元となった Rijndael では、ブロック長と鍵長が可変であり、128ビットから256ビットまでの32ビットの倍数が選べる。NISTが公募した際のスペックに従い、米国標準となったAESではブロック長は128ビットに固定され、鍵長も3種類に限られた[5]。

Advanced Encryption Standard - Wikipedia

また安全性に関しても高いようで、Wikipediaを見てたら面白い記述を見つけました。

この暗号はまだどんな攻撃にも屈していないが、何人かの研究者がこの数学的な構造を利用した攻撃方法が存在するかもしれないと指摘している[8][9]。

Advanced Encryption Standard - Wikipedia


そんなAESですが、実はC#では簡単に利用することができるので紹介していこうと思います。

概要

AESの利用に関して公式ドキュメントにも記述があります。
Aes クラス (System.Security.Cryptography) | Microsoft Docs

また公式ドキュメントでは1種類のサンプルしかありませんが、AESにはいくつかの設定項目があります。

ユーザーが設定する場合があるだろうプロパティは以下の通り。

プロパティ 説明
Mode 対称アルゴリズムの操作モードを取得または設定。デフォルトはCBC。他にECBOFBCFBCTSがある。
KeySize 鍵のビット数。128ビット・192ビット・256ビットの3種類。
Key 鍵。
IV 初期ベクトル。(おそらく)ECB以外で利用する。BlockSizeと同じビット数(基本128bit)にする。

Modeに関してECBが最も単純な暗号利用モードですが、メッセージの機密性の保持には向かない欠点があるみたいです。ただIVを設定する必要がない箇所は楽ですね。
暗号利用モード - Wikipedia

使い方

ECB (AES-128)

public static byte[] Encrypy(string text, byte[] key)
{
    if (text == null || text.Length <= 0) throw new ArgumentException(nameof(text));
    
    // AES-128の場合は、「128bit / 8 = 16byte」の鍵を用意する
    if (key.Length != 16) throw new ArgumentException(nameof(key));
            
    byte[] encrypted;
            
    using (Aes aesAlg = Aes.Create())
    {
        // 鍵の長さを128bitにする
        aesAlg.KeySize = 128;
        aesAlg.Key = key;
                
        // ModeをECBにする
        aesAlg.Mode = CipherMode.ECB;

        // Encryptorを生成
        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                
        // 暗号化
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                {
                    swEncrypt.Write(text);
                }
                encrypted = msEncrypt.ToArray();
            }
        }
    }
            
    return encrypted;
}

public static string Decrypy(byte[] cipherText, byte[] key)
{
    if (cipherText == null || cipherText.Length <= 0) throw new ArgumentException(nameof(cipherText));

    // AES-128の場合は、「128bit / 8 = 16byte」の鍵を用意する
    if (key.Length != 16) throw new ArgumentException(nameof(key));
            
    string plaintext = null;
            
    using (Aes aesAlg = Aes.Create())
    {
        aesAlg.KeySize = 128;
        aesAlg.Mode = CipherMode.ECB;
        aesAlg.Key = key;

        ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                {
                    plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
    }

    return plaintext;
}

CBC (AES-256)

public static byte[] Encrypy(string text, byte[] iv, byte[] key)
{
    if (text == null || text.Length <= 0) throw new ArgumentException(nameof(text));
    
    // CBCの場合は、「128bit / 8 = 16byte」の初期ベクトルを用意する
    if (iv.Length != 16) throw new ArgumentException(nameof(iv));

    // AES-256の場合は、「256bit / 8 = 32byte」の鍵を用意する
    if (key.Length != 32) throw new ArgumentException(nameof(key));

    byte[] encrypted;

    using (Aes aesAlg = Aes.Create())
    {
        aesAlg.KeySize = 256;
        aesAlg.Mode = CipherMode.ECB;
        aesAlg.Key = key;
        aesAlg.IV = iv;

        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                {
                    swEncrypt.Write(text);
                }

                encrypted = msEncrypt.ToArray();
            }
        }
    }

    return encrypted;
}

public static string Decrypy(byte[] cipherText, byte[] iv, byte[] key)
{
    if (cipherText == null || cipherText.Length <= 0) throw new ArgumentException(nameof(cipherText));

    // CBCの場合は、「128bit / 8 = 16byte」の初期ベクトルを用意する
    if (iv.Length != 16) throw new ArgumentException(nameof(iv));
            
    // AES-256の場合は、「256bit / 8 = 32byte」の鍵を用意する
    if (key.Length != 32) throw new ArgumentException(nameof(key));
            
    string plaintext = null;
            
    using (Aes aesAlg = Aes.Create())
    {
        aesAlg.KeySize = 128;
        aesAlg.Mode = CipherMode.ECB;
        aesAlg.Key = key;
        aesAlg.IV = iv;

        ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                {
                    plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
    }

    return plaintext;
}