1. kkent030315

    Posted

    kkent030315
Changes in title
+高校生がC#でSHA256暗号化アルゴリズムを完全独自実装してみた。
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,783 @@
+高校生の僕が**SHA256**暗号化アルゴリズムを外部ライブラリやランタイムに一切頼らず、独自で実装してみました。
+当記事は、マイクロソフト様にケンカを売る記事ではございません。
+
+#前置き
+![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/ac274c17-ff57-c11d-2eb9-00b0dfaa1b2e.png)
+
+この記事をご覧になっている方々は、様々な分野でお仕事をされている方がいらっしゃると思います。
+そういった方々含め、プログラミングを実際にされていない方も「SHA256」という単語は一度でも目に入れた方も多くいらっしゃるのではないでしょうか。
+
+**SHA256**とは、アメリカ国家安全保障局によって設計され、
+2001年にアメリカ国立標準技術研究所によって連邦情報処理標準とされた暗号化アルゴリズムのひとつです。
+SHA256の他にも、「SHA-1」や「SHA-512」といった多くの暗号化アルゴリズムがあります。
+
+そのひとつである「SHA-1」は、2017年にGoogleによって攻撃手段が公開され、
+それまでSHA-1を使用していたシステムは半強制的に、より安全な暗号化アルゴリズムへの移行を余儀なくされた。
+という事件がありましたね。
+
+> 該当記事: [Googleがハッシュ関数「SHA-1」を破ることに成功](https://gigazine.net/news/20170224-google-cwi-break-sha-1/)
+
+そういった背景もある暗号化アルゴリズム達ですが、今回はなかでも多くの場面で使われている
+「SHA-256」をプログラミング言語 C# を使って独自で実装してみたいと思います。
+
+ビットコインを支えている**ブロックチェーン**でも、SHA-256は活躍しています。
+ブロックチェーンに関しましては、下記記事をご参照頂くとわかりやすいと思います。
+
+> 参考記事: [【Python】10分でイキリ高校生がブロックチェーンを初心者に解説してやる!!!](https://qiita.com/sa2shun/items/f4991357ece3303c7167#4blockchain%E3%81%AE%E5%AE%9F%E8%A3%85)
+
+#SHA256の特徴
+![weodnmgvbws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/d2984d08-160c-a760-5cd4-2d61ec393d9d.png)
+
+
+ここまでで、様々な暗号化アルゴリズムがあることがわかりました。
+SHA256の特徴は
+
+- **一方向性**
+ - ▶復号化が困難である。
+
+ここで、”困難”と言ったのは、**総当り攻撃**による突破が可能であるからです。
+論理的には、2^80(2の80乗)という回数総当たりを仕掛ければどれかが当たる、ということです。
+現在では、遥かに少ない2^63(2の63乗)という回数で突破できる手法が見つかっているそうです。
+
+- **衝突困難**
+ - ▶SHA-1で使用された攻撃手法。衝突させることが困難である。
+
+簡単に言うと、**異なる2つの平文から同一のハッシュ値が得られる脆弱性に強い**ということです。
+詳しく知りたい方は、下記の記事を参照下さい。
+
+> 参考記事: [巷で話題のGoogleのSHA-1衝突やってみた](http://73spica.tech/blog/sha1-collision/)
+
+#実装してみる
+ここからは、実際にプログラムを書いてC#で暗号化アルゴリズムを再現してみたいと思います。
+
+##大まかな処理フロー
+かなり大雑把ではありますが、大まかの処理フローは以下の通りです。
+![sha256flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/6922e2c3-94e3-41cd-7fb1-dfb0e0d44059.png)
+
+###1.平文(16進数)を2進数に変換
+`a`であれば16進数`61` 2進数 `1100001` に変換されます。
+
+###2.パディング処理
+パディング(Padding)と呼ばれる空埋め処理を行います。
+1ブロックのサイズは`64byte`ですので、それ以下であれば空の部分を埋める必要があります。
+但し、ただ埋めるだけではダメです。
+例として以下のようなブロックがあったとします。
+`14byte`分足りません。これを埋めます。
+
+```
+//50byte
+0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+```
+
+これを、ただ埋めると何が起こるかは安易に予想できます。
+`X`の部分を0で埋めたとすると、異なるブロックであっても全く同じメッセージブロックが生まれてしまいます。
+
+```
+//46byte
+1111 0101 0111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+0000 0100 0000 0110 0000 0000 0000 0000 XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX
+
+//50byte
+1111 0101 0111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+0000 0100 0000 0110 0000 0000 0000 0000 0000 0000 XXXX XXXX XXXX XXXX XXXX XXXX XXXX
+
+
+
+全く同じブロックが生まれる。
+```
+
+そこで、もとのブロックのブロック長を末端に記録します。
+`B`の部分がブロック長を記録した部分です。
+
+```
+//46byte
+1111 0101 0111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+0000 0100 0000 0110 0000 0000 0000 0000 XXXX XXXX XXXX XXXX XXXX BBBB BBBB BBBB BBBB
+
+//50byte
+1111 0101 0111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+0000 0100 0000 0110 0000 0000 0000 0000 0000 0000 XXXX XXXX XXXX BBBB BBBB BBBB BBBB
+```
+
+ブロック長が64byte丁度だったりと万が一記録する余裕が無かっただった場合は、次のブロックで記録します。
+
+###3.ブロックをメッセージブロックに分割
+
+パディングが済んだ512byteのブロックを64byte長のメッセージブロックへと分割します。
+
+###4.初期ベクトルを使用してハッシュ値を算出
+
+初回ラウンドでは、初期ベクトル(IV)を使用します。
+
+```c#
+/// <summary>
+/// 初期ハッシュ
+/// </summary>
+private uint[] initial_hash =
+{
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+};
+```
+
+###5.合計64ラウンド処理を行う
+
+行う処理は簡単に以下の通りです。
+
+![sha256flo2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/0263bf33-d4e8-3f5f-de93-0c6e586d9fd2.png)
+
+####論理代数
+
+ここからは論理代数の分野です。
+正直なところ、僕もふわっとしか理解できていません。
+一通り調べてはみましたが、情報そのものが正しいものであるか区別する手段がありませんでしたので、
+あくまで参考として記載します。
+詳しい方がいらっしゃいましたら、コメント欄にて解説をお願い致します。
+
+> 参考記事: [論理代数を学ぼう](http://maicommon.ciao.jp/ss/dscrtMath2/alg/)
+参考記事: [論理代数と論理関数 湊 真一](http://www.ieice-hbkb.org/files/01/01gun_08hen_01.pdf)
+
+####Ch
+
+```math
+Ch(E,F,G) = (E \Lambda F) Xor (\rightharpoondown E \Lambda G)
+```
+
+```c#
+private uint Ch(uint x, uint y, uint z)
+{
+ return (x & y) ^ (~x & z);
+}
+```
+
+####Maj (変数多数決関数)
+
+```math
+Maj(A,B,C) = (A \Lambda B) Xor (A \Lambda C) Xor (B \Lambda C)
+```
+
+```c#
+private uint Maj(uint x, uint y, uint z)
+{
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+```
+
+####RotR (Rotation Right)
+
+```c#
+private uint Rot_R(uint x, byte n)
+{
+ return (x >> n) | (x << (32 - n));
+}
+```
+
+####RotL (Rotation Left)
+
+```c#
+/// <summary>
+/// 左ローテート
+/// </summary>
+/// <param name="x"></param>
+/// <param name="n"></param>
+/// <returns></returns>
+private uint Rot_L(uint x, byte n)
+{
+ return (x << n) | (x >> (32 - n));
+}
+```
+
+####Σ0 (Sigma0)
+
+```math
+\sum 0 (A) = (A \ggg 2) Xor (A \ggg 13) Xor (A \ggg 22)
+```
+
+```c#
+private uint Sigma0(uint x)
+{
+ return Rot_R(x, 2) ^ Rot_R(x, 13) ^ Rot_R(x, 22);
+}
+```
+
+####Σ1 (Sigma1)
+
+```math
+\sum 1 (E) = (E \ggg 6) Xor (E \ggg 11) Xor (E \ggg 25)
+```
+
+```c#
+private uint Sigma1(uint x)
+{
+ return Rot_R(x, 6) ^ Rot_R(x, 11) ^ Rot_R(x, 25);
+}
+```
+
+##C#ソースコード
+ここからは、実際のソースコードを紹介します。
+
+###定数K
+
+```c#
+/// <summary>
+/// Kと呼ばれる定数
+/// </summary>
+private readonly uint[] const_k = new uint[64]
+{
+ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
+ 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
+ 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
+ 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
+ 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
+ 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
+ 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
+ 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
+ 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
+ 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
+ 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
+ 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
+ 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
+ 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
+ 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
+ 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
+};
+```
+
+###ComputeHash() 関数 - ハッシュ値を算出
+
+```c#
+/// <summary>
+/// SHA256ハッシュを生成します。
+/// </summary>
+/// <param name="plainText">暗号化したい文字列</param>
+/// <returns>暗号化された文字列</returns>
+public string ComputeHash(string plainText)
+{
+ var p = Padding(plainText);
+ var block_list = Parse(p);
+ var s = new uint[8];
+ Array.Copy(initial_hash, s, initial_hash.Length);
+
+ //ブロック数分ループ
+ foreach (var blocks in block_list)
+ {
+ //ブロックリストの中のブロックにスコープを当てる
+ foreach(var block in block_list)
+ {
+ var pair = MakePair(s);
+ var expanded_block = ExpandBlock(block);
+
+ for (int n = 0; n < 64; ++n)
+ {
+ var CH = Ch(pair["e"], pair["f"], pair["g"]);
+ var MAJ = Maj(pair["a"], pair["b"], pair["c"]);
+ var SIG0 = Sigma0(pair["a"]);
+ var SIG1 = Sigma1(pair["e"]);
+
+ var WJ_KJ = (const_k[n] + expanded_block[n]);
+ var T1_TEMP = (pair["h"] + WJ_KJ + CH);
+ var T1 = (T1_TEMP + SIG1);
+ var T2 = (SIG0 + MAJ);
+
+ pair["h"] = pair["g"];
+ pair["g"] = pair["f"];
+ pair["f"] = pair["e"];
+ pair["e"] = (pair["d"] + T1);
+ pair["d"] = pair["c"];
+ pair["c"] = pair["b"];
+ pair["b"] = pair["a"];
+ pair["a"] = (T1 + T2);
+ }
+
+ s[0] = (pair["a"] + s[0]);
+ s[1] = (pair["b"] + s[1]);
+ s[2] = (pair["c"] + s[2]);
+ s[3] = (pair["d"] + s[3]);
+ s[4] = (pair["e"] + s[4]);
+ s[5] = (pair["f"] + s[5]);
+ s[6] = (pair["g"] + s[6]);
+ s[7] = (pair["h"] + s[7]);
+ }
+ }
+
+ return MakeHash(s);
+}
+```
+
+###ToUInt32Array() 関数 - 2進数に変換
+
+```c#
+/// <summary>
+/// Stringを2進数に変換します。
+/// </summary>
+/// <param name="plain_text">暗号化する文字列</param>
+/// <returns>2進数配列</returns>
+private uint[] ToUInt32Array(string plain_text)
+{
+ //文字列を16進数に変換. 実体はbyte配列
+ var a = Encoding.ASCII.GetBytes(plain_text);
+
+ //パディング結果を格納する配列
+ uint[] result = { };
+
+ foreach (var n in a)
+ {
+ //16進数を2進数に変換
+ var j = int.Parse(Convert.ToString(n, 2));
+
+ //2進数を8桁に揃える 0を先頭に追加
+ result = Extend<uint>(result, 0);
+ //8桁に揃えたものを配列化
+ for (int nb = 0; nb < 7; nb++)
+ {
+ result = Extend<uint>(result, (uint)StrMid(j, nb));
+ }
+ }
+
+ return result;
+}
+```
+
+###CalculateK() 関数 - Kを算出
+
+```c#
+/// <summary>
+/// 与えられた配列から定数Kを算出します。
+/// </summary>
+/// <param name="plain_bits">算出された数</param>
+/// <returns>定数K</returns>
+private uint CalculateK(uint[] plain_bits)
+{
+ uint k = 0;
+ var length = plain_bits.Length;
+
+ while ((length + 1 + k) % 512 != 448)
+ {
+ k += 1;
+ }
+
+ return k;
+}
+```
+
+###Padding() 関数 - パディング
+
+```c#
+/// <summary>
+/// パディングと呼ばれる、空埋め処理を行います。
+/// </summary>
+/// <param name="plain_text">パディングする2進数配列</param>
+/// <returns>パディングされた2進数配列</returns>
+private uint[] Padding(string plain_text)
+{
+ var plain_bits = ToUInt32Array(plain_text);
+ var length = plain_bits.Length;
+ var k = CalculateK(plain_bits);
+
+ //処理する値を保持するバッファ
+ uint[] buf = { };
+
+ buf = Extend<uint>(plain_bits, 1);
+
+ for(int r = 0; r < k; r++)
+ {
+ buf = Extend<uint>(buf, 0);
+ }
+
+ var bytStr = Convert.ToString(length, 2);
+
+ //64桁右寄せゼロ埋め
+ //0000000000000000000000000000000000000000000000000000000000011000
+ bytStr = bytStr.ToString().PadLeft(64, '0');
+
+ //↑で得た64桁の数列を配列に変換
+ //(64)[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 ]
+ uint[] bytStr_toArray = { };
+ for(int x = 0; x <= 63; x++)
+ {
+ var num_str = bytStr.Substring(x, 1);
+ var num = uint.Parse(num_str);
+ bytStr_toArray = Extend<uint>(bytStr_toArray, num);
+ }
+
+ //位取り バッファにAppend
+ foreach(var b in bytStr_toArray)
+ {
+ buf = Extend<uint>(buf, b);
+ }
+
+ //ブロックサイズはパディング後512である
+ //Assert(buf.Length != 512)
+
+ return buf;
+}
+```
+
+###Parse() 関数 - ブロックの分割
+
+```c#
+/// <summary>
+/// パディングされた配列を512バイトのブロック長に分割します。
+/// </summary>
+/// <param name="plain_bits">分割する2進数配列</param>
+/// <returns>分割された2進数ブロック一覧を格納したジャグ配列</returns>
+private uint[][] Parse(uint[] plain_bits)
+{
+ const int BLOCK_SIZE = 512;
+ var length = plain_bits.Length;
+ var num_blocks = length / BLOCK_SIZE;
+
+ //ブロックを保持するジャグ配列
+ var blocks = new uint[num_blocks][];
+
+ for(int n = 0; n < num_blocks; n++)
+ {
+ var block = new uint[BLOCK_SIZE];
+ Array.Copy(plain_bits, block, BLOCK_SIZE);
+ //ブロックリストに追加
+ blocks[n] = block;
+ }
+
+ return blocks;
+}
+```
+
+###ExpandBlock() 関数 - ブロックのプレ処理
+
+```c#
+/// <summary>
+/// ブロックを処理します。
+/// </summary>
+/// <param name="block">64バイトの2進数ブロック配列</param>
+/// <returns>処理された2進数ブロック配列</returns>
+private uint[] ExpandBlock(uint[] block)
+{
+ uint[] result = { };
+
+ for(int x = 0; x < 16; x++)
+ {
+ //例
+ //バイナリ -> ヘックスデミカル -> uint OR バイナリ -> demical
+ //↓チャンクバイナリ 01100001011000100110001110000000 は ヘックスデミカル 0x61626380 である.
+ //CHUNK: [ 32 ] [0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
+ //= 0x61626380 したがって uint は 1633837952
+
+ //コピー先のチャンク配列
+ uint[] chunk_array = new uint[32];
+ //ブロックから、 x*32 から 32先までをコピー
+ Array.Copy(block, x*32, chunk_array, 0, 32);
+ //バイナリに変換
+ var chunk_binary_str = ToBinary(chunk_array);
+
+ var chunk_demical = Convert.ToUInt32(chunk_binary_str, 2);
+
+ result = Extend<uint>(result, chunk_demical);
+
+ for(int y = 16; y < 64; y++)
+ {
+ var T1 = Sub0(result[y - 15]) + result[y - 16];
+ var T2 = T1 + result[y - 7];
+ var T3 = T2 + Sub1(result[y - 2]);
+ }
+
+ result = Extend<uint>(result, T3);
+ }
+
+ return result;
+}
+```
+
+###MakePair() 関数 - ペアを生成
+
+```c#
+/// <summary>
+/// 計算をより楽に、分かりやすくするためにStringとのペアを生成します。
+/// </summary>
+/// <param name="hash">ペアと対になる2進数配列</param>
+/// <returns>ペアを格納したディクショナリ</returns>
+private Dictionary<string, uint> MakePair(uint[] hash)
+{
+ var dictionary = new Dictionary<string, uint>();
+ dictionary.Add("a", hash[0]);
+ dictionary.Add("b", hash[1]);
+ dictionary.Add("c", hash[2]);
+ dictionary.Add("d", hash[3]);
+ dictionary.Add("e", hash[4]);
+ dictionary.Add("f", hash[5]);
+ dictionary.Add("g", hash[6]);
+ dictionary.Add("h", hash[7]);
+
+ return dictionary;
+}
+```
+
+###MakeHash() 関数 - 16進数に変換
+
+```c#
+/// <summary>
+/// 2進数を16進数の文字列に変換します。
+/// </summary>
+/// <param name="s">変換したい2進数配列</param>
+/// <returns>変換された文字列</returns>
+private string MakeHash(uint[] s)
+{
+ string result = "";
+
+ foreach(var v in s)
+ {
+ result += Convert.ToString((int)v, 16);
+ }
+
+ return result;
+}
+```
+
+###ToBinary() 関数 - バイナリに変換
+
+```c#
+/// <summary>
+/// チャンクをバイナリに変換します。
+/// </summary>
+/// <param name="chunk"></param>
+/// <returns>変換されたバイナリ</returns>
+private string ToBinary(uint[] chunk)
+{
+ string result = string.Empty;
+
+ foreach(var n in chunk)
+ {
+ result += n.ToString();
+ }
+
+ return result;
+}
+```
+
+###BinaryToDemical() 関数 - デミカルに変換
+
+`Convert.ToString(value, 16)` を使う方法もあります。
+
+```c#
+/// <summary>
+/// バイナリをデミカル Integer に変換します。
+/// </summary>
+/// <param name="binary_str">バイナリ</param>
+/// <returns>デミカル Integer</returns>
+private int BinaryToDemical(string binary_str)
+{
+ char[] binary_char_array = binary_str.ToCharArray();
+ Array.Reverse(binary_char_array);
+ int result = 0;
+
+ for (int i = 0; i < binary_char_array.Length; i++)
+ {
+ if (binary_char_array[i] == '1')
+ {
+ if (i == 0)
+ {
+ result += 1;
+ }
+ else
+ {
+ result += (int)Math.Pow(2, i);
+ }
+ }
+ }
+
+ return result;
+}
+```
+
+##自作便利関数
+やっていて、色々と不便に感じた部分や簡略化したい部分を関数化したものです。
+
+###Extend() 関数 - 配列を拡張
+C#では型付けが厳しい為、定義後に配列の大きさを柔軟に変更することができません。
+そこで、既存の配列を拡張する関数を作ってみました。
+
+```c#
+/// <summary>
+/// 配列を右辺へ拡張し、値を格納します。
+/// </summary>
+/// <typeparam name="T">アンマネージド型</typeparam>
+/// <param name="source">ソース元である配列</param>
+/// <param name="num">格納する値</param>
+/// <returns>拡張された配列</returns>
+private T[] Extend<T>(IList<T> source, T num)
+{
+ var result = new T[source.Count+1];
+ for (int n = 0; n < source.Count; n++)
+ {
+ result[n] = source[n];
+ }
+ result[source.Count] = num;
+ return result;
+}
+```
+
+使い方:
+
+```c#
+uint[] test = { 1 };
+//before [ 1 ]
+uint[] res = Extend<uint>(test, 3);
+//after [ 1, 3 ]
+```
+
+###PrintArray() 関数 - 配列をいい感じに出力
+C#では、`Console.WriteLine()`で大体のことはできますが、
+配列をそのまま出力しようとすると`System.Int32`と、型がそのまま出力されてしまいます。
+そのままではデバッグするのもストレスなので、いい感じに出力してくれる関数を作ってみました。
+
+```c#
+/// <summary>
+/// 配列をいい感じに出力します。
+/// </summary>
+/// <typeparam name="T">アンマネージド型</typeparam>
+/// <param name="source">ソース元である配列</param>
+private void PrintArray<T>(IList<T> source)
+{
+ Console.WriteLine(string.Format("({0})", source.Count) + "[ " + string.Join(", ", source) + " ]");
+}
+```
+
+```c#
+//こんな配列が
+int[] hoge = { 0, 1, 2, 3, 4 };
+
+//こうすると
+Console.WriteLine(hoge);
+
+//こうなるから
+//Output: System.Int32
+
+//自作関数でラップすると
+PrintArray(hoge);
+
+//いい感じに出力される
+//Output: [ 0, 1, 2, 3, 4 ]
+```
+
+###StrMid() 関数 - 数値の中から数値を取り出す
+
+例えば、こんな数字から
+
+```
+123456
+```
+
+`3`のインデックス部分を取り出したいとします。
+パッと思いつくのは、`string`にして`SubString()`する方法でした。
+
+```c#
+//int文字列から特定の位置にある数字を取り出す
+//12345
+// ↑indexN
+//return 2
+
+/// <summary>
+/// Integer から指定されたインデックスにある数値を取り出します。
+/// 12345678 の インデックス 5 の場合 返り値は 5 です。
+/// </summary>
+/// <param name="source">ソース元である数値</param>
+/// <param name="indexN">インデックス</param>
+/// <returns>取り出された数値</returns>
+private int StrMid(int source, int indexN)
+{
+ var s = source.ToString().Substring(indexN, 1);
+ return int.Parse(s);
+}
+```
+
+##実行してみた!
+
+![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/7bf71298-6c7b-b488-be1e-b75a6c63e212.png)
+
+環境は .NET Framework 4.7.2 です。
+WinFormで適当にそれっぽいものを作って実際に実行してみます。
+
+使用例:
+
+```c#
+using (SHA256NotManaged sha256 = SHA256NotManaged.Create())
+{
+ var plainText = textBox1.Text;
+ var encryptedText = sha256.ComputeHash(plainText);
+ textBox2.Text = encryptedText;
+}
+```
+
+実行結果:
+
+![afqbu-uhkdc.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/249da2e8-8711-3fcf-1cbb-492fa3b451fb.gif)
+
+出てきたハッシュをSHA256データーベースで検索してみます。
+
+![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/1960c983-71b2-b8f1-8e5a-32c19b3578b7.png)
+
+正しいようです。
+
+###処理時間
+
+今回は、アルゴリズムに集点をおいて、可読性を優先してコードを書いたのもあり
+速度については一切考慮しておりませんが、一応ランタイムのものと比較してみます。
+
+比較にあたって、下記記事を参考にソースコードを拝借いたしました。
+ありがとうございました。
+
+> 参考記事: [C#でStopwatchを使った時間計測を1行でできるようにする](https://takachan.hatenablog.com/entry/2019/03/05/230952)
+
+```c#
+using (SHA256NotManaged sha256 = SHA256NotManaged.Create())
+{
+ TimeSpan elapsed = StopwatchEx.Context(() => { sha256.ComputeHash("abc"); }, 1000);
+ Console.WriteLine($"自作: {elapsed.TotalMilliseconds}");
+}
+
+using (System.Security.Cryptography.SHA256 sha256_ = System.Security.Cryptography.SHA256.Create())
+{
+ TimeSpan elapsed = StopwatchEx.Context(() => { sha256_.ComputeHash(abc); }, 1000);
+ Console.WriteLine($"本家: {elapsed.TotalMilliseconds}");
+}
+```
+
+今回は5回ほど計測してみました。
+それぞれ1000回の試行にかかった平均時間(ミリ秒)です。
+ランタイムのものと比較すると、約800倍近く時間がかかっていますね。
+手も足も出ませんでした。
+
+~~正直ランタイムといい勝負するのではないかと期待していた~~ 打ち砕かれました。
+
+```
+自作: 857.9315
+本家: 1.1778
+
+自作: 920.8264
+本家: 1.8223
+
+自作: 804.2666
+本家: 1.009
+
+自作: 803.6653
+本家: 1.0139
+
+自作: 821.0931
+本家: 1.0187
+```
+
+#最後に
+
+一番苦戦したのは理論代数の分野の理解です。
+一向に理解できないせいで計算があわず、2時間格闘しました。。。
+結局ふわっとしか理解できていません。
+詳しい方がいらっしゃれば、些細な情報でもコメント欄にて解説頂けるとありがたいです。
+
+フルソースコードは僕のGithubリポジトリにあります。
+コピペで動きます。
+[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535270/472a7984-d9cb-5a9b-84a7-22e8efb1bfa5.png)](https://github.com/kkent030315/SHA256-Algorithm-CSharp)
+
+
+当記事内で間違いや誤字脱字等ありましたら、お手数ですがコメント欄にてご指摘いただけますと幸いです。
+
+**最後まで読んでいただき、ありがとうございました。**