Project Euler 59問目

#はじめに
C#を用いてProjectEulerに挑戦している人の記録です。VisualStudio2017で、1つのフォームアプリの中に問題番号のボタンを作成し、そのボタンをクリックすると結果をラベルに表示するという形式でやっています。

#59問目
各文字はそれぞれ一意のコードに割り当てられている. よく使われる標準としてASCII (American Standard Code for Information Interchange) がある. ASCIIでは, 大文字A = 65, アスタリスク (*) = 42, 小文字k = 107というふうに割り当てられている.

モダンな暗号化の方法として, テキストファイルの各バイトをASCIIに変換し, 秘密鍵から計算された値とXORを取るという手法がある. XOR関数の良い点は, 暗号化に用いたのと同じ暗号化鍵でXORを取ると平文を復号できる点である. 65 XOR 42 = 107であり, 107 XOR 42 = 65である.

破られない暗号化のためには, 鍵は平文と同じ長さのランダムなバイト列でなければならない. ユーザーは暗号文と暗号化鍵を別々の場所に保存する必要がある. また, もし一方が失われると, 暗号文を復号することは不可能になる.

悲しいかな, この手法はほとんどのユーザーにとって非現実的である. そこで, 鍵の変わりにパスワードを用いる手法が用いられる. パスワードが平文より短ければ (よくあることだが), パスワードは鍵として繰り返し用いられる. この手法では, 安全性を保つために十分長いパスワードを用いる必要があるが, 記憶するためにはある程度短くないといけない.

この問題での課題は簡単になっている. 暗号化鍵は3文字の小文字である. cipher1.txtは暗号化されたASCIIのコードを含んでいる. また, 平文はよく用いられる英単語を含んでいる. この暗号文を復号し, 平文のASCIIでの値の和を求めよ.

```CSharp
private void button59_Click(object sender, EventArgs e)
        {
            string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\cipher.txt";
            Encoding utf8 = Encoding.GetEncoding("utf-8");
            string String = "";
            var IntList = new List<int>();
            using (var reader = new StreamReader(path, utf8))
            {
                try
                {
                    String = reader.ReadToEnd();
                }
                catch (Exception ex)
                {

                }
            }
            char[] DelimiterChars = { ',' };
            string[] String2 = String.Split(DelimiterChars);
            foreach (string a in String2)
            {
                IntList.Add(int.Parse(a));
            }
            string[] KeyString = new string[26 * 26 * 26];
            int i = 0;
            for (int KeyFirst = 97; KeyFirst < 123; KeyFirst++)
            {
                for (int KeySecond = 97; KeySecond < 123; KeySecond++)
                {
                    for (int KeyThird = 97; KeyThird < 123; KeyThird++)
                    {
                        KeyString[i] = Convert.ToChar(KeyFirst).ToString() +
                                        Convert.ToChar(KeySecond).ToString() +
                                        Convert.ToChar(KeyThird).ToString();
                        i++;
                    }
                }
            }
            string TruePlainText = "";
            for (int j = 0; j < KeyString.Length; j++)
            {
                string PlainText = "";
                int PlainTextIndex = 0;
                char[] Key = KeyString[j].ToCharArray();
                foreach (int a in IntList)
                {
                    string Plain = "";
                    if (PlainTextIndex % 3 == 0) Plain = Convert.ToChar(a ^ Key[0]).ToString();//value[0];
                    if (PlainTextIndex % 3 == 1) Plain = Convert.ToChar(a ^ Key[1]).ToString();//value[1];
                    if (PlainTextIndex % 3 == 2) Plain = Convert.ToChar(a ^ Key[2]).ToString();//value[2];
                    PlainTextIndex++;
                    PlainText += Plain;
                }
                if (PlainText.Contains(" the "))
                {
                    TruePlainText = PlainText;
                    break;
                }
            }
            int PlainSum = 0;
            for (int a = 0; a < TruePlainText.Length; a++)
            {
                int Plain = Convert.ToChar(TruePlainText[a]);
                PlainSum += Plain;
            }

            label1.Text = "Answer=" + PlainSum;
        }
```
chiper.txtは文字コードが,で区切られて羅列されており、区切られている要素をリストに入れます。
その後、鍵にあたる3文字の小文字列を作ります。次に、作った鍵の文字列でChiper.txtの値と論理和をとって復号します。よく用いられる英単語を含むということなので、theを含むとかんがえ、実際に復号した文が意味の通る英文になっていたため、アスキーコードの和を答えとしました。
#終わりに
暗号の復号を簡単にですができました。意味の分からない暗号文から、意味の分かる平文を導くことができてとても面白かったです。