C# で AES暗号 (共通鍵暗号) を 利用 する 方法

0 件のコメント

AES暗号 を 利用して暗号化および復号を行うとき、具体的にどのようなコードを実装すればよいかをまとめてみました。 暗号技術の詳細には触れず、実装する場合にどうしたら良いか、という観点でまとめました。

目次

概要

"AES暗号" は 共通鍵暗号 の一種です。 学術的な詳細は分かりませんが… "DES暗号" よりは安全な暗号方法です。

AES暗号の仕様として以下のルールが決まっているので、利用する際は設定を間違えないように気を付けます。

ブロック長
128bit
鍵長
128/192/256bit

実際に実装で利用する共通鍵は「IV (Initialize Vector/初期化ベクタ)」と「Key (鍵)」の2項目になります。 これらを公開鍵暗号等であらかじめ交換しておきます。

共通鍵

  • IV
  • Key

鍵作成

鍵(IV、Key)の生成は何もしなければ自動生成されます。 生成された鍵はプロパティから取得できます。 また、手動で鍵を生成したい場合は Rfc2898DeriveBytes クラス を利用します。

自動生成

using System;
using System.Security.Cryptography;

public void CreateKey1(out string iv, out string key)
{
    var BLOCK_SIZE = 128;   // 128bit 固定
    var KEY_SIZE = 128;     // 128/192/256bit から選択

    // AES暗号サービスを生成
    var csp = new AesCryptoServiceProvider();
    csp.BlockSize = BLOCK_SIZE;
    csp.KeySize = KEY_SIZE;
    csp.Mode = CipherMode.CBC;
    csp.Padding = PaddingMode.PKCS7;

    // IV および 鍵 を自動生成
    csp.GenerateIV();
    csp.GenerateKey();

    // 鍵を出力;
    iv = Convert.ToBase64String(csp.IV);
    key = Convert.ToBase64String(csp.Key);
}

手動生成

using System;
using System.Security.Cryptography;

public void CreateKey2(string ivPassword, out string iv, string keyPassword, out string key)
{
    var BLOCK_SIZE = 128;   // 128bit 固定
    var KEY_SIZE = 128;     // 128/192/256bit から選択

    // IV を生成
    var rfcBlock = new Rfc2898DeriveBytes(ivPassword, BLOCK_SIZE / 8);
    var arrBlock = rfcBlock.GetBytes(BLOCK_SIZE / 8);
    iv = Convert.ToBase64String(arrBlock);

    // Key を生成
    var rfcKey = new Rfc2898DeriveBytes(keyPassword, KEY_SIZE / 8);
    var arrKey = rfcKey.GetBytes(KEY_SIZE / 8);
    key = Convert.ToBase64String(arrKey);
}

暗号化

上述の 鍵生成 で生成した IV および Key を利用して文字列を暗号化します。

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

public string Encrypt(string plainText, string iv, string key)
{
    var cipherText = string.Empty;

    var BLOCK_SIZE = 128;   // 128bit 固定
    var KEY_SIZE = 128;     // 128/192/256bit から選択

    var csp = new AesCryptoServiceProvider();
    csp.BlockSize = BLOCK_SIZE;
    csp.KeySize = KEY_SIZE;
    csp.Mode = CipherMode.CBC;
    csp.Padding = PaddingMode.PKCS7;
    csp.IV = Convert.FromBase64String(iv);
    csp.Key = Convert.FromBase64String(key);

    using (var outms = new MemoryStream())            
    using (var encryptor = csp.CreateEncryptor())
    using (var cs = new CryptoStream(outms, encryptor, CryptoStreamMode.Write))
    using (var writer = new StreamWriter(cs))
    {
        writer.Write(plainText);
        cipherText = Convert.ToBase64String(outms.ToArray());
    }

    return cipherText;
}

復号

上述の 鍵生成 で生成した IV および Key を利用して、 暗号化 で暗号化した文字列を復号します。

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

public string Decrypt(string cipherText, string iv, string key)
{
    var plainText = string.Empty;

    var BLOCK_SIZE = 128;   // 128bit 固定
    var KEY_SIZE = 128;     // 128/192/256bit から選択

    var csp = new AesCryptoServiceProvider();
    csp.BlockSize = BLOCK_SIZE;
    csp.KeySize = KEY_SIZE;
    csp.Mode = CipherMode.CBC;
    csp.Padding = PaddingMode.PKCS7;
    csp.IV = Convert.FromBase64String(iv);
    csp.Key = Convert.FromBase64String(key);

    using (var inms = new MemoryStream(Convert.FromBase64String(cipherText)))
    using (var decryptor = csp.CreateDecryptor())
    using (var cs = new CryptoStream(inms, decryptor, CryptoStreamMode.Read))
    using (var reader = new StreamReader(cs))
    {
        plainText = reader.ReadToEnd();
    }

    return plainText;
}

[おまけ] サンプルコード

鍵生成、暗号化、複合をまとめて利用しやすくしたサンプルコードを以下に掲載します。 基本的にはコピペで動きます。

AesCryptgraphy.cs

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

public class AesCryptgraphy
{
    /// <summary>
    /// AesCryptgraphy クラスのインスタンスを初期化します。
    /// </summary>
    public AesCryptgraphy()
    {
        this.BlockSize = 128;
        this.KeySize = 256;
    }

    /// <summary>
    /// キー長を指定して AesCryptgraphy クラス のインスタンスを初期化します。
    /// </summary>
    /// <param name="keySize"></param>
    public AesCryptgraphy(int keySize)
    {
        this.BlockSize = 128;
        this.KeySize = KeySize;
    }

    /// <summary>
    /// ブロック長を取得または設定します。
    /// ※値は 128bit 固定です。
    /// </summary>
    public int BlockSize { get; set; }

    /// <summary>
    /// キー長を取得または設定します。
    /// ※値は 128 / 192 / 256 bit から選択します。
    /// </summary>
    public int KeySize { get; set; }

    /// <summary>
    /// 共通鍵(IV, キー)を自動生成します。
    /// </summary>
    /// <param name="iv">IV</param>
    /// <param name="key">キー</param>
    public void CreateKey(out string iv, out string key)
    {
        // AES暗号サービスを生成
        var csp = new AesCryptoServiceProvider();
        csp.BlockSize = this.BlockSize;
        csp.KeySize = this.KeySize;
        csp.Mode = CipherMode.CBC;
        csp.Padding = PaddingMode.PKCS7;

        // IV および 鍵 を自動生成
        csp.GenerateIV();
        csp.GenerateKey();

        // 鍵を出力;
        iv = Convert.ToBase64String(csp.IV);
        key = Convert.ToBase64String(csp.Key);
    }

    /// <summary>
    /// 共通鍵を派生させるパスワードを指定して共通鍵(IV, キー)を生成します。
    /// </summary>
    /// <param name="ivPassword">IVを派生させるパスワード</param>
    /// <param name="iv">IV</param>
    /// <param name="keyPassword">キーを派生させるパスワード</param>
    /// <param name="key">キー</param>
    public void CreateKey(string ivPassword, out string iv, string keyPassword, out string key)
    {
        // IV を生成
        var rfcBlock = new Rfc2898DeriveBytes(ivPassword, this.BlockSize / 8);
        var arrBlock = rfcBlock.GetBytes(this.BlockSize / 8);
        iv = Convert.ToBase64String(arrBlock);

        // Key を生成
        var rfcKey = new Rfc2898DeriveBytes(keyPassword, this.KeySize / 8);
        var arrKey = rfcKey.GetBytes(this.BlockSize / 8);
        key = Convert.ToBase64String(arrKey);
    }

    /// <summary>
    /// IV、キーを指定して文字列の暗号化を行います。
    /// </summary>
    /// <param name="plainText">暗号化したい文字列</param>
    /// <param name="iv">IV</param>
    /// <param name="key">キー</param>
    /// <returns>暗号化された文字列</returns>
    public string Encrypt(string plainText, string iv, string key)
    {
        var cipherText = string.Empty;

        var csp = new AesCryptoServiceProvider();
        csp.BlockSize = this.BlockSize;
        csp.KeySize = this.KeySize;
        csp.Mode = CipherMode.CBC;
        csp.Padding = PaddingMode.PKCS7;
        csp.IV = Convert.FromBase64String(iv);
        csp.Key = Convert.FromBase64String(key);

        using (var outms = new MemoryStream())            
        using (var encryptor = csp.CreateEncryptor())
        using (var cs = new CryptoStream(outms, encryptor, CryptoStreamMode.Write))
        {
            using (var writer = new StreamWriter(cs))
            {
                writer.Write(plainText);
            }
            cipherText = Convert.ToBase64String(outms.ToArray());
        }

        return cipherText;
    }

    /// <summary>
    /// IV、キーを指定して暗号化された文字列の複合を行います。
    /// </summary>
    /// <param name="cipherText">暗号化された文字列</param>
    /// <param name="iv">IV</param>
    /// <param name="key">キー</param>
    /// <returns>復号された文字列</returns>
    public string Decrypt(string cipherText, string iv, string key)
    {
        var plainText = string.Empty;

        var csp = new AesCryptoServiceProvider();
        csp.BlockSize = this.BlockSize;
        csp.KeySize = this.KeySize;
        csp.Mode = CipherMode.CBC;
        csp.Padding = PaddingMode.PKCS7;
        csp.IV = Convert.FromBase64String(iv);
        csp.Key = Convert.FromBase64String(key);

        using (var inms = new MemoryStream(Convert.FromBase64String(cipherText)))
        using (var decryptor = csp.CreateDecryptor())
        using (var cs = new CryptoStream(inms, decryptor, CryptoStreamMode.Read))
        using (var reader = new StreamReader(cs))
        {
            plainText = reader.ReadToEnd();
        }

        return plainText;
    }
}

参考記事