共通鍵暗号化のサンプル:AESを利用

共通鍵暗号化のプログラムサンプル。.Netのライブラリを使用したもの。

プログラムの概要

まず暗号化のためのパスワードを入力させ、テキストを暗号化する。
その後、復号化のパスワードを入力させ、暗号化したデータを復号化する。

暗号化パスワードと復号化パスワードの不一致で、復号化が失敗したら、メッセージを表示する。

ソルトは、暗号化時に作成し文字列で内部で保存しておき、復号化時には、それを使用して復号化のデータを作成する。

ソルトの作成

RNGCryptoServiceProviderを使用し作成した情報をHEX化文字列にしたものを保持するようにしている。ただHEX化文字列をByte[]に変換する処理で、-(ハイフン)を考慮していない実装にしているので、HEX化する際に-(ハイフン)を除去する処理を入れている。

AESキーの作成

Rfc2898DeriveBytesを使用しKeyとIVを作成している。
ただ、KeyとIVの作成方法として、Rfc2898DeriveBytesを利用している意味が実を言うとよくわかっていない。KeyとIVを作成するコードをググって検索した場合、ほぼすべてが同様な実装を使っていたため、これを使用した。

復号化

復号化のパスワード間違い時の動作も検証したかったので、復号化処理には、例外処理を入れてある。
復号化処理で何らかの間違いがあった場合、CryptographicExceptionが発生する。
今回のプログラムでの発生個所は、DecryptStringFromBytesplaintext = srDecrypt.ReadToEnd();

DecryptStringFromBytesの中で利用されるメソッドでCryptographicExceptionが発生するとは、MSDNに記載されていなかったので、プログラムを走らせてエラーが出てから例外処理を入れた。

サンプルコード

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

namespace AESTest1
{
    class Program
    {
        static byte[] EncryptStringToBytes(string plainText, ICryptoTransform encryptor)
        {
            byte[] encrypted;

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

            return encrypted;
        }

        static string DecryptStringFromBytes(byte[] cipherText, ICryptoTransform decryptor)
        {
            string plaintext = null;

            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;

        }
        static void Main(string[] args)
        {
            string plainText = "Here is some data to encrypt!";

            // 共通鍵暗号化
            // パスワードは、上から与える。
            // soltはここで作るが、一度string化したものを再利用する。
            // 想定は、設定画面で、パスワードを設定時、soltを自動生成し、それを覚えておく。
            // 認証画面では覚えておいたsoltと入力パスワードで復号化する。
            using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider()) {

                string soltString;  // soltを文字列化したもの

                // AESのキー情報を作成する
                {
                    // soltの作成
                    Byte[] soltByte1 = new byte[8];
                    using (var rnd = new RNGCryptoServiceProvider()) {
                        rnd.GetBytes(soltByte1);
                    }
                    soltString = BitConverter.ToString(soltByte1).Replace("-", "");
                    Console.WriteLine("Solt\n{0}\n", soltString);

                    // キー作成
                    Console.Write("AES\nEnter crypt password : ");
                    string password = Console.ReadLine();
                    var keyCreator = new Rfc2898DeriveBytes(password, soltByte1);
                    myAes.Key = keyCreator.GetBytes(myAes.KeySize / 8);
                    myAes.IV = keyCreator.GetBytes(myAes.BlockSize / 8);
                }

                // 暗号化を行い、その結果をbase64化したテキストで受け取る
                string entryptedString = Convert.ToBase64String(EncryptStringToBytes(plainText, myAes.CreateEncryptor()));
                Console.WriteLine("Encrypted\n{0}\n", entryptedString);

                // AESのキー情報を作成する
                {
                    // soltは文字列化したものを再利用する
                    var soltByte2 = Enumerable.Range(0, soltString.Length)
                     .Where(x => x % 2 == 0)
                     .Select(x => Convert.ToByte(soltString.Substring(x, 2), 16))
                     .ToArray();
                    // パスワードの再入力
                    Console.Write("Enter encrypt password : ");
                    var password = Console.ReadLine();
                    var keyCreator = new Rfc2898DeriveBytes(password, soltByte2);
                    myAes.Key = keyCreator.GetBytes(myAes.KeySize / 8);
                    myAes.IV = keyCreator.GetBytes(myAes.BlockSize / 8);
                }

                // 復号化
                try {
                    Console.WriteLine("decrypt text: {0}",
                        DecryptStringFromBytes(Convert.FromBase64String(entryptedString), myAes.CreateDecryptor()));
                }
                catch (CryptographicException) {
                    Console.WriteLine("復号化に失敗しました");
                }
            }
        }
    }
}

コメント

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