C# .NET パスワードの暗号化と複合化をする方法を紹介 (AES暗号化)

新米太郎
新米太郎

皆さんこんにちは新米です。

今回はC#を使ってパスワードの暗号化と複合化をする方法を紹介します。



そもそもAESとは?

Advanced(高度な) Encryption(暗号化)Standard(標準)を略してAESと言います。

AES方式とは共通鍵暗号方式の一つで
2021年時点のアメリカで標準規格として採用されています!

共通鍵暗号方式とは、、暗号と複合に同じ鍵を使う事です。

 

ではAESを簡単に説明すると、、
共通鍵を用いてデータをブロック(固定長のデータ)単位で暗号化する方式
共通ブロック暗号」方式に分類されています。

暗号化は下記の4つの処理から構成される[5]
SubBytes – 換字表(Sボックス)による1バイト単位の置換。
ShiftRows – 4バイト単位の行を一定規則で左シフトする。
MixColumns – ビット演算による4バイト単位の行列変換。
AddRoundKey – ラウンド鍵とのXORをとる。

https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard より

※イメージは以下のような感じ。。

鍵長は128ビット、192ビット、256ビットの3パターンあります。

鍵長が長く(ビット数が多い)なれば安全性が増すが、その分計算量が増えてしまいます。
(処理時間がかかることがデメリット)

暗号化は複数の演算を連続して行うラウンドと呼ばれる処理単位を繰り返すことによって行われます。

 

新米太郎
新米太郎

正直こんな感じで暗号化しているらしいけど
そこまで理解しなくていいと思います。
初めは、高度な暗号化をしているんだな〜ぐらいでいいかとw

Microsoft AES方式の詳細


C#パスワードの暗号化と複合化

サンプルコード

    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Security.Cryptography;
    using System.IO;

    namespace CsDevelopment
    {
        public class Auth
        {
            private const int KEY_SIZE = 256; // 256Bit
            private const int BLOCK_SIZE = 128; // 128Bit
            private const int BUFFER_SIZE = BLOCK_SIZE * 32;  // バッファーサイズはBlockSizeの倍数にする
            private const int SALT_SIZE = 8; // 8以上

            //idとpass
            private string userId = "新米太郎";
            private string password = "123456789";

            /// <summary>
            /// Saltを生成する
            /// </summary>
            /// <param name="str">8文字以上の文字列を指定すること</param>
            private byte[] CreateSalt(string str)
            {
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(str, SALT_SIZE);
                return deriveBytes.Salt;
            }

            /// <summary>
            /// 初期化ベクトルを作成する
            /// </summary>
            /// <param name="str">8文字以上の文字列を指定すること</param>
            private byte[] CreateIV(string str)
            {
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(str, BLOCK_SIZE / 8);
                return deriveBytes.GetBytes(BLOCK_SIZE / 8);
            }

            //パスワードとソルトから暗号鍵を求める
            private byte[] GetKeyFromPassword(string textUserId, byte[] salt)
            {
                //Rfc2898DeriveBytesオブジェクトを作成する
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(textUserId, salt);

                //反復処理回数を指定する デフォルトで1000回
                deriveBytes.IterationCount = 1000;

                //キーを生成する
                return deriveBytes.GetBytes(KEY_SIZE / 8);
            }

            /// <summary>
            /// 暗号化したパスを返す。
            /// </summary>
            /// <param name="id"></param>
            /// <param name="pass"></param>
            /// <returns>パスワードの暗号化の値</returns>
            public string Encrypt(string id, string pass)
            {
                byte[] salt;
                salt = CreateSalt("");
                //string saltValue = Convert.ToBase64String(salt);

                byte[] iv;
                iv = CreateIV("");
                //string ivValue = Convert.ToBase64String(iv);

                byte[] key = GetKeyFromPassword(id, salt);
                //string keyValue = Convert.ToBase64String(key);

                byte[] outBytes = null;
                AesManaged aes = new AesManaged();
                {
                    // AESインスタンスのパラメータ設定
                    aes.KeySize = KEY_SIZE;
                    aes.BlockSize = BLOCK_SIZE;
                    aes.Mode = CipherMode.CBC;
                    aes.Key = key;
                    aes.IV = iv;
                    aes.Padding = PaddingMode.PKCS7;

                    // 暗号化オブジェクト生成
                    ICryptoTransform ct = aes.CreateEncryptor(aes.Key, aes.IV);

                    // 出力ストリーム
                    MemoryStream outStream = new MemoryStream();
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(outStream, ct, CryptoStreamMode.Write))
                        {
                            byte[] buf = Encoding.Unicode.GetBytes(pass);
                            cryptoStream.Write(buf, 0, buf.Length);
                        }
                        outBytes = outStream.ToArray();
                    }
                }

                List<byte> vs = new List<byte>();
                vs.AddRange(salt);
                vs.AddRange(iv);
                vs.AddRange(outBytes);

                return Convert.ToBase64String(vs.ToArray());
            }

            /// <summary>
            /// 暗号化パスワードを複合し返す
            /// </summary>
            /// <param name="id"></param>
            /// <param name="encryptPass"></param>
            /// <returns>123456789</returns>
            public string Decrypt(string id, string encryptPass)
            {
                byte[] bytes = Convert.FromBase64String(encryptPass);

                byte[] salt = new byte[SALT_SIZE];
                byte[] iv = new byte[BLOCK_SIZE / 8];
                byte[] data = new byte[bytes.Length - SALT_SIZE - BLOCK_SIZE / 8];

                // ソルトとIVと暗号データを読み取る
                using (MemoryStream ms = new MemoryStream(bytes))
                {
                    ms.Read(salt, 0, SALT_SIZE);
                    ms.Read(iv, 0, BLOCK_SIZE / 8);
                    ms.Read(data, 0, bytes.Length - SALT_SIZE - BLOCK_SIZE / 8);
                }

                string saltValue = Convert.ToBase64String(salt);
                string ivValue = Convert.ToBase64String(iv);

                byte[] key = GetKeyFromPassword(id, salt);
                string keyValue = Convert.ToBase64String(key);

                string decodeString = "";

                using (AesManaged aes = new AesManaged())
                {
                    // AESインスタンスのパラメータ設定
                    aes.KeySize = KEY_SIZE;
                    aes.BlockSize = BLOCK_SIZE;
                    aes.Mode = CipherMode.CBC;
                    aes.Key = key;
                    aes.IV = iv;
                    aes.Padding = PaddingMode.PKCS7;

                    // 暗号化オブジェクト生成
                    ICryptoTransform ct = aes.CreateDecryptor(aes.Key, aes.IV);

                    using (var inStream = new MemoryStream(data, false))  // 入力ストリームを開く
                    using (var outStream = new MemoryStream())   // 出力ストリーム
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(inStream, ct, CryptoStreamMode.Read))
                        {
                            byte[] buffer = new byte[BUFFER_SIZE];
                            int len = 0;
                            while ((len = cryptoStream.Read(buffer, 0, BUFFER_SIZE)) > 0)
                                outStream.Write(buffer, 0, len);
                        }
                        byte[] outBytes = outStream.ToArray();
                        decodeString = Encoding.Unicode.GetString(outBytes);
                    }
                }
                return decodeString;
            }
  
            static void Main(String[] args)
            {
                //インスタンス生成
                Auth obj = new Auth();

                //暗号処理呼び出し
                string result = obj.Encrypt(obj.userId, obj.password);
                Console.Write("暗号化:");
                Console.WriteLine(result);

                //複合処理呼び出し
                string composite = obj.Decrypt(obj.userId,result);
                Console.Write("複合化:");
                Console.WriteLine(composite);
            }
        }
    }

 

実行結果

暗号化:aDm+hkBerNTYVbX4E+gX6szMG2n/foOIzjNfAI7iQVsa9q0WxMIr8Fge8v1/y5mHBz6JfWVqzPY=
複合化:123456789

まとめ

今回紹介したコードは長くて複雑だなと感じると思います。

全てを理解するのではなく、各メソッドの役割をネットで調べて、どんな役割をしているのか理解するのが大切です。

またデバッグして処理の流れを知ることも重要だと思います。

以上!

新米太郎
新米太郎

最後まで読んでいただきありがとうございました!



コメント

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