首頁 > 軟體

C#中實現AES演演算法加密解讀

2023-02-27 06:00:23

先上效果圖

檔案和加密檔案之間的轉換。

先新增輔助類

 public class AES_EnorDecrypt
    {
        //定義預設金鑰
        private static byte[] _aesKeyByte = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
        private static string _aesKeyStr = Encoding.UTF8.GetString(_aesKeyByte);

        /// <summary>
        /// 隨機生成金鑰,預設金鑰長度為32,不足在加密時自動填充空格
        /// </summary>
        /// <param name="n">金鑰長度</param>
        /// <returns></returns>
        public static string GetIv(int n)
        {
            string s = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            char[] arrChar = new char[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                arrChar[i] = Convert.ToChar(s.Substring(i, 1));
            }
            StringBuilder num = new StringBuilder();
            Random rnd = new Random(DateTime.Now.Millisecond);
            for (int i = 0; i < n; i++)
            {
                num.Append(arrChar[rnd.Next(0, arrChar.Length)].ToString());
            }
            _aesKeyByte = Encoding.UTF8.GetBytes(num.ToString());
            return _aesKeyStr = Encoding.UTF8.GetString(_aesKeyByte);
        }

        /// <summary>
        /// AES加密,針對文字類檔案
        /// </summary>
        /// <param name="Data">被加密的明文</param>
        /// <param name="Key">金鑰</param>
        /// <param name="Vector">金鑰向量</param>
        /// <returns>密文</returns>
        public static string AESEncrypt(string Data, string Key, string Vector)
        {
            byte[] plainBytes = Encoding.UTF8.GetBytes(Data);
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
            byte[] Cryptograph = null;//加密後的密文
            Rijndael Aes = Rijndael.Create();
            try
            {
                using (MemoryStream Memory = new MemoryStream())
                {
                    //把記憶體流物件包裝成加密流物件
                    using (CryptoStream Encryptor = new CryptoStream(Memory, Aes.CreateEncryptor(bKey, bVector), CryptoStreamMode.Write))
                    {
                        Encryptor.Write(plainBytes, 0, plainBytes.Length);
                        Encryptor.FlushFinalBlock();
                        Cryptograph = Memory.ToArray();
                    }
                }
            }
            catch
            {
                Cryptograph = null;
            }
            return Convert.ToBase64String(Cryptograph);
        }

        /// <summary>
        /// AES加密,任意檔案
        /// </summary>
        /// <param name="Data">被加密的明文</param>
        /// <param name="Key">金鑰</param>
        /// <param name="Vector">金鑰向量</param>
        /// <returns>密文</returns>
        public static byte[] AESEncrypt(byte[] Data, string Key, string Vector)
        {
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
            byte[] Cryptograph = null;//加密後的密文
            Rijndael Aes = Rijndael.Create();
            try
            {
                using (MemoryStream Memory = new MemoryStream())
                {
                    //把記憶體流物件包裝成加密流物件
                    using (CryptoStream Encryptor = new CryptoStream(Memory, Aes.CreateEncryptor(bKey, bVector), CryptoStreamMode.Write))
                    {
                        Encryptor.Write(Data, 0, Data.Length);
                        Encryptor.FlushFinalBlock();
                        Cryptograph = Memory.ToArray();
                    }
                }
            }
            catch
            {
                Cryptograph = null;
            }
            return Cryptograph;
        }

        /// <summary>
        /// AES解密,針對文字檔案
        /// </summary>
        /// <param name="Data">被解密的密文</param>
        /// <param name="Key">金鑰</param>
        /// <param name="Vector">金鑰向量</param>
        /// <returns>明文</returns>
        public static string AESDecrypt(string Data, string Key, string Vector)
        {
            byte[] encryptedBytes = Convert.FromBase64String(Data);
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
            byte[] original = null;//解密後的明文
            Rijndael Aes = Rijndael.Create();
            try
            {
                using (MemoryStream Memory = new MemoryStream(encryptedBytes))
                {
                    //把記憶體流物件包裝成加密物件
                    using (CryptoStream Decryptor = new CryptoStream(Memory, Aes.CreateDecryptor(bKey, bVector), CryptoStreamMode.Read))
                    {
                        //明文儲存區
                        using (MemoryStream originalMemory = new MemoryStream())
                        {
                            byte[] Buffer = new byte[1024];
                            int readBytes = 0;
                            while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
                            {
                                originalMemory.Write(Buffer, 0, readBytes);
                            }
                            original = originalMemory.ToArray();
                        }
                    }
                }
            }
            catch
            {
                original = null;
            }
            return Encoding.UTF8.GetString(original);
        }

        /// <summary>
        /// AES解密,任意檔案
        /// </summary>
        /// <param name="Data">被解密的密文</param>
        /// <param name="Key">金鑰</param>
        /// <param name="Vector">金鑰向量</param>
        /// <returns>明文</returns>
        public static byte[] AESDecrypt(byte[] Data, string Key, string Vector)
        {
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);
            byte[] original = null;//解密後的明文
            Rijndael Aes = Rijndael.Create();
            try
            {
                using (MemoryStream Memory = new MemoryStream(Data))
                {
                    //把記憶體流物件包裝成加密物件
                    using (CryptoStream Decryptor = new CryptoStream(Memory, Aes.CreateDecryptor(bKey, bVector), CryptoStreamMode.Read))
                    {
                        //明文儲存區
                        using (MemoryStream originalMemory = new MemoryStream())
                        {
                            byte[] Buffer = new byte[1024];
                            int readBytes = 0;
                            while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
                            {
                                originalMemory.Write(Buffer, 0, readBytes);
                            }
                            original = originalMemory.ToArray();
                        }
                    }
                }
            }
            catch
            {
                original = null;
            }
            return original;
        }
    }

AES是塊加密,塊的長度是16位元組,如果原文不到16的位元組,就會進行填充至16個位元組。

開始實現

介面佈局

textbox1為檔案位置,textbox2為密碼。

設定好後

public partial class FormAes : Form
    {
        #region 屬性
        private static string _aesKeyVector = "q2T_=R/*33vc";
        #endregion

        public FormAes()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 選擇檔案
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textBox1_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Multiselect = true;//該值確定是否可以選擇多個檔案
            dialog.Title = "請選擇資料夾";
            dialog.Filter = "所有檔案(*.*)|*.*";
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                textBox1.Text = dialog.FileName;
            }
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(textBox1.Text.Trim()) || string.IsNullOrWhiteSpace(textBox2.Text.Trim()))
                return;
            backgroundWorker1.RunWorkerAsync();
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(textBox1.Text.Trim()) || string.IsNullOrWhiteSpace(textBox2.Text.Trim()))
                return;
            backgroundWorker2.RunWorkerAsync();
        }


        /// <summary>
        /// 後臺執行緒執行的方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
           
            string KeyVector = _aesKeyVector;//金鑰向量
            string path = Path.GetDirectoryName(textBox1.Text);
            string name = Path.GetFileName(textBox1.Text);
            name += ".En";
            #region   加密
            FileStream sr = new FileStream(textBox1.Text, FileMode.Open, FileAccess.Read);
            FileStream sw = new FileStream(path + "\" + name, FileMode.Create, FileAccess.Write);

            if (sr.Length > 50 * 1024 * 1024)//如果檔案大於50M,採取分塊加密,按50MB讀寫
            {
                byte[] mybyte = new byte[52428800];//每50MB加密一次                  
                int numBytesRead = 52428800;//每次加密的流大小
                long leftBytes = sr.Length;//剩餘需要加密的流大小
                long readBytes = 0;//已經讀取的流大小
                //每50MB加密後會變成50MB+16B
                byte[] encrpy = new byte[52428816];
                while (true)
                {
                    if (leftBytes > numBytesRead)
                    {
                        sr.Read(mybyte, 0, mybyte.Length);
                        encrpy = AES_EnorDecrypt.AESEncrypt(mybyte, textBox2.Text, KeyVector);
                        sw.Write(encrpy, 0, encrpy.Length);
                        leftBytes -= numBytesRead;
                        readBytes += numBytesRead;
                        backgroundWorker1.ReportProgress((int)(readBytes * 100 / sr.Length));
                    }
                    else//重新設定讀取流大小,避免最後多餘空值
                    {
                        byte[] newByte = new byte[leftBytes];
                        sr.Read(newByte, 0, newByte.Length);
                        byte[] newWriteByte;
                        newWriteByte = AES_EnorDecrypt.AESEncrypt(newByte, textBox2.Text, KeyVector);
                        sw.Write(newWriteByte, 0, newWriteByte.Length);
                        readBytes += leftBytes;
                        backgroundWorker1.ReportProgress((int)(readBytes * 100 / sr.Length));
                        break;
                    }
                }
            }
            else
            {
                byte[] mybyte = new byte[sr.Length];
                sr.Read(mybyte, 0, (int)sr.Length);
                mybyte = AES_EnorDecrypt.AESEncrypt(mybyte, textBox2.Text, KeyVector);
                sw.Write(mybyte, 0, mybyte.Length);
                backgroundWorker1.ReportProgress(100);
            }

            sr.Close();
            sw.Close();

            #endregion
        }
       
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

        /// <summary>
        /// 執行完成時觸發
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("加密成功!");
        }

        /// <summary>
        /// 後臺執行緒執行的方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                string KeyVector = _aesKeyVector;//金鑰向量
                string path = Path.GetDirectoryName(textBox1.Text);
                string name = Path.GetFileName(textBox1.Text);
                if (name.EndsWith(".En"))
                {
                    name = name.Remove(name.Length - 3, 3);
                }

                #region  解密
                FileStream sr = new FileStream(textBox1.Text, FileMode.Open, FileAccess.Read);
                FileStream sw = new FileStream(path + "\" + name, FileMode.Create, FileAccess.Write);

                if (sr.Length > 50 * 1024 * 1024)//如果檔案大於50M,採取分塊解密,按50MB讀寫
                {
                    byte[] mybyte = new byte[52428816];//解密緩衝區50MB+16B
                    byte[] decrpt = new byte[52428800];//解密後的50MB
                    int numBytesRead = 52428816;//每次解密的流大小
                    long leftBytes = sr.Length;//剩餘需要解密的流大小
                    long readBytes = 0;//已經讀取的流大小
                    try
                    {
                        while (true)
                        {
                            if (leftBytes > numBytesRead)
                            {
                                sr.Read(mybyte, 0, mybyte.Length);
                                decrpt = AES_EnorDecrypt.AESDecrypt(mybyte, textBox2.Text, KeyVector);
                                sw.Write(decrpt, 0, decrpt.Length);
                                leftBytes -= numBytesRead;
                                readBytes += numBytesRead;
                                backgroundWorker2.ReportProgress((int)(readBytes * 100 / sr.Length));
                            }
                            else//重新設定讀取流大小,避免最後多餘空值
                            {
                                byte[] newByte = new byte[leftBytes];
                                sr.Read(newByte, 0, newByte.Length);
                                byte[] newWriteByte;
                                newWriteByte = AES_EnorDecrypt.AESDecrypt(newByte, textBox2.Text, KeyVector);
                                sw.Write(newWriteByte, 0, newWriteByte.Length);
                                readBytes += leftBytes;
                                backgroundWorker2.ReportProgress((int)(readBytes * 100 / sr.Length));
                                break;
                            }
                        }
                    }
                    catch
                    {
                        sr.Close();
                        sw.Close();
                        File.Delete(path + "\" + name);
                        e.Cancel = true;
                    }
                    sr.Close();
                    sw.Close();
                }
                else
                {
                    byte[] mybyte = new byte[(int)sr.Length];
                    sr.Read(mybyte, 0, (int)sr.Length);
                    try
                    {
                        mybyte = AES_EnorDecrypt.AESDecrypt(mybyte, textBox2.Text, KeyVector);
                        sw.Write(mybyte, 0, mybyte.Length);
                        backgroundWorker2.ReportProgress(100);
                    }
                    catch
                    {
                        sr.Close();
                        sw.Close();
                        File.Delete(path + "\" + name);
                        e.Cancel = true;
                    }
                    sr.Close();
                    sw.Close();
                }
                #endregion
            }
            catch
            {
                e.Cancel = true;
            }
        }

        private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
           progressBar1.Value = e.ProgressPercentage;
        }

        /// <summary>
        /// 執行完成時觸發
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
               MessageBox.Show("解密失敗n密碼錯誤或加密檔案被篡改,無法解密");                
            }
            else
            {
                MessageBox.Show("解密成功!");
            }
        }   
    }

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com