C#.NET中對稱和非對稱加密、解密方法彙總

 在安全性要求比較高的系統中都會涉及到數據的加密、解密。.NET爲我們封裝了常用的加密算法,例如:MD5,DES,RSA等。有可逆加密,也有非可逆加密;有對稱加密,也有非對稱加密。加密、解密一般會用在軟件的註冊碼,系統密碼,通訊中。今天我就來分享,彙總一下C#.NET加密、解密的實現方法。

 

 一、不可逆加密

不可逆加密一般不會涉及到解密。也就是是加密之後的密文不能還原成原來的明文。這種算法一般用於生成自信摘要,確保數據的完整性及防篡改。
使用FormsAuthentication類加密

using System.Web.Security;
      
    namespace EncyptDemo
    {
        public class WebEncryptor
        {
            /// <summary>
            /// SHA1加密字符串
            /// </summary>
            /// <param name="source">源字符串</param>
            /// <returns>加密後的字符串</returns>
            public string SHA1(string source)
            {
                return FormsAuthentication.HashPasswordForStoringInConfigFile(source, "SHA1");
            }
      
            /// <summary>
            /// MD5加密字符串
            /// </summary>
            /// <param name="source">源字符串</param>
            /// <returns>加密後的字符串</returns>
            public string MD5(string source)
            {
                return FormsAuthentication.HashPasswordForStoringInConfigFile(source, "MD5"); ;
            }
      
        }
    }

   

使用MD5CryptoServiceProvider類生成MD5字符串

using System;
    using System.Security.Cryptography;
    using System.Text;
      
    namespace EncyptDemo
    {
        public class MD5Helper
        {
            public string GetMD5_32(string s, string _input_charset = "utf8")
            {
                MD5 md5 = new MD5CryptoServiceProvider();
                byte[] t = md5.ComputeHash(Encoding.GetEncoding(_input_charset).GetBytes(s));
                StringBuilder sb = new StringBuilder(32);
                for (int i = 0; i < t.Length; i++)
                {
                    sb.Append(t[i].ToString("x").PadLeft(2, '0'));
                }
                return sb.ToString();
            }
      
            //16位加密
            public static string GetMd5_16(string s)
            {
                MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
                string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(s)), 4, 8);
                t2 = t2.Replace("-", "");
                return t2;
            }
      
        }
    }

 

 二、可逆加密、解密

可逆加密,這種算法加密之後的密碼文可以解密成原來的明文。比如通訊的時候,數據的發送方和接收方約定好加密和解密的key,發送放把原始數據加密之後開始發送,接收放收到數據之後開始解密,把密文原來成明文。

可逆加密又分爲對稱加密和非對稱加密。所謂對稱加密就是加密和解密的算法一樣,也就是用來加密的key和解密的key是完全一樣的。而非對稱加密加密的key和解密的key是不一樣的,加密是用公鑰,解密是用私鑰。
2.1、對稱加密

對稱加密算法是應用較早的加密算法,技術成熟。在對稱加密算法中,數據發信方將明文(原始數據)和加密密鑰一起經過特殊加密算法處理後,使其變成複雜的加密密文發送出去。收信方收到密文後,若想解讀原文,則需要使用加密用過的密鑰及相同算法的逆算法對密文進行解密,才能使其恢復成可讀明文。

在對稱加密算法中,使用的密鑰只有一個,發收信雙方都使用這個密鑰對數據進行加密和解密,這就要求解密方事先必須知道加密密鑰。對稱加密算法的特點是算法公開、計算量小、加密速度快、加密效率高。不足之處是,交易雙方都使用同樣鑰匙,安全性得不到保證。此外,每對用戶每次使用對稱加密算法時,都需要使用其他人不知道的惟一鑰匙,這會使得發收信雙方所擁有的鑰匙數量成幾何級數增長,密鑰管理成爲用戶的負擔。對稱加密算法在分佈式網絡系統上使用較爲困難,主要是因爲密鑰管理困難,使用成本較高。在計算機專網系統中廣泛使用的對稱加密算法有DES、IDEA和AES。

using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
      
    namespace EncyptDemo
    {
        public class SymmetryEncrypt
        {
            private SymmetricAlgorithm mobjCryptoService;
            private string Key;
            /// <summary>  
            /// 對稱加密類的構造函數  
            /// </summary>  
            public SymmetryEncrypt()
            {
                mobjCryptoService = new RijndaelManaged();
                Key = "Guz(%&hj7x89H$yuBI0456FtmaT5&fvHUFCy76*h%(HilJ$lhj!y6&(*jkP87jH7";
            }
            /// <summary>  
            /// 獲得密鑰  
            /// </summary>  
            /// <returns>密鑰</returns>  
            private byte[] GetLegalKey()
            {
                string sTemp = Key;
                mobjCryptoService.GenerateKey();
                byte[] bytTemp = mobjCryptoService.Key;
                int KeyLength = bytTemp.Length;
                if (sTemp.Length > KeyLength)
                    sTemp = sTemp.Substring(0, KeyLength);
                else if (sTemp.Length < KeyLength)
                    sTemp = sTemp.PadRight(KeyLength, ' ');
                return ASCIIEncoding.ASCII.GetBytes(sTemp);
            }
            /// <summary>  
            /// 獲得初始向量IV  
            /// </summary>  
            /// <returns>初試向量IV</returns>  
            private byte[] GetLegalIV()
            {
                string sTemp = "E4ghj*Ghg7!rNIfb&95GUY86GfghUb#er57HBh(u%g6HJ($jhWk7&!hg4ui%$hjk";
                mobjCryptoService.GenerateIV();
                byte[] bytTemp = mobjCryptoService.IV;
                int IVLength = bytTemp.Length;
                if (sTemp.Length > IVLength)
                    sTemp = sTemp.Substring(0, IVLength);
                else if (sTemp.Length < IVLength)
                    sTemp = sTemp.PadRight(IVLength, ' ');
                return ASCIIEncoding.ASCII.GetBytes(sTemp);
            }
            /// <summary>  
            /// 加密方法  
            /// </summary>  
            /// <param name="Source">待加密的串</param>  
            /// <returns>經過加密的串</returns>  
            public string Encrypto(string Source)
            {
                byte[] bytIn = UTF8Encoding.UTF8.GetBytes(Source);
                MemoryStream ms = new MemoryStream();
                mobjCryptoService.Key = GetLegalKey();
                mobjCryptoService.IV = GetLegalIV();
                ICryptoTransform encrypto = mobjCryptoService.CreateEncryptor();
                CryptoStream cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Write);
                cs.Write(bytIn, 0, bytIn.Length);
                cs.FlushFinalBlock();
                ms.Close();
                byte[] bytOut = ms.ToArray();
                return Convert.ToBase64String(bytOut);
            }
            /// <summary>  
            /// 解密方法  
            /// </summary>  
            /// <param name="Source">待解密的串</param>  
            /// <returns>經過解密的串</returns>  
            public string Decrypto(string Source)
            {
                byte[] bytIn = Convert.FromBase64String(Source);
                MemoryStream ms = new MemoryStream(bytIn, 0, bytIn.Length);
                mobjCryptoService.Key = GetLegalKey();
                mobjCryptoService.IV = GetLegalIV();
                ICryptoTransform encrypto = mobjCryptoService.CreateDecryptor();
                CryptoStream cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Read);
                StreamReader sr = new StreamReader(cs);
                return sr.ReadToEnd();
            }
      
        }
    }

在對稱加密算法中比較著名和常用的就是大名鼎鼎的DES加密算法,它安全性比較高的一種算法,目前只有一種方法可以破解該算法,那就是窮舉法。下面我們來看看.NET中要使用DES加密怎麼實現?

 

using System;
  using System.IO;
  using System.Security.Cryptography;
  using System.Text;
    
  namespace EncyptDemo
  {
      public class DESHeper
      {
          //默認密鑰向量
          private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
          /// <summary>
          /// DES加密字符串
          /// </summary>
          /// <param name="encryptString">待加密的字符串</param>
          /// <param name="encryptKey">加密密鑰,要求爲8位</param>
          /// <returns>加密成功返回加密後的字符串,失敗返回源串</returns>
          public static string EncryptDES(string encryptString, string encryptKey)
          {
              try
              {
                  byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8));
                  byte[] rgbIV = Keys;
                  byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                  DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
                  MemoryStream mStream = new MemoryStream();
                  CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                  cStream.Write(inputByteArray, 0, inputByteArray.Length);
                  cStream.FlushFinalBlock();
                  return Convert.ToBase64String(mStream.ToArray());
              }
              catch
              {
                  return encryptString;
              }
          }
    
          /// <summary>
          /// DES解密字符串
          /// </summary>
          /// <param name="decryptString">待解密的字符串</param>
          /// <param name="decryptKey">解密密鑰,要求爲8位,和加密密鑰相同</param>
          /// <returns>解密成功返回解密後的字符串,失敗返源串</returns>
          public static string DecryptDES(string decryptString, string decryptKey)
          {
              try
              {
                  byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey);
                  byte[] rgbIV = Keys;
                  byte[] inputByteArray = Convert.FromBase64String(decryptString);
                  DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
                  MemoryStream mStream = new MemoryStream();
                  CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                  cStream.Write(inputByteArray, 0, inputByteArray.Length);
                  cStream.FlushFinalBlock();
                  return Encoding.UTF8.GetString(mStream.ToArray());
              }
              catch
              {
                  return decryptString;
              }
          }
    
          /// <summary>
          /// DES加密方法
          /// </summary>
          /// <param name="strPlain">明文</param>
          /// <param name="strDESKey">密鑰</param>
          /// <param name="strDESIV">向量</param>
          /// <returns>密文</returns>
          public string DESEncrypt(string strPlain, string strDESKey, string strDESIV)
          {
              //把密鑰轉換成字節數組
              byte[] bytesDESKey = ASCIIEncoding.ASCII.GetBytes(strDESKey);
              //把向量轉換成字節數組
              byte[] bytesDESIV = ASCIIEncoding.ASCII.GetBytes(strDESIV);
              //聲明1個新的DES對象
              DESCryptoServiceProvider desEncrypt = new DESCryptoServiceProvider();
              //開闢一塊內存流
              MemoryStream msEncrypt = new MemoryStream();
              //把內存流對象包裝成加密流對象
              CryptoStream csEncrypt = new CryptoStream(msEncrypt, desEncrypt.CreateEncryptor(bytesDESKey, bytesDESIV), CryptoStreamMode.Write);
              //把加密流對象包裝成寫入流對象
              StreamWriter swEncrypt = new StreamWriter(csEncrypt);
              //寫入流對象寫入明文
              swEncrypt.WriteLine(strPlain);
              //寫入流關閉
              swEncrypt.Close();
              //加密流關閉
              csEncrypt.Close();
              //把內存流轉換成字節數組,內存流現在已經是密文了
              byte[] bytesCipher = msEncrypt.ToArray();
              //內存流關閉
              msEncrypt.Close();
              //把密文字節數組轉換爲字符串,並返回
              return UnicodeEncoding.Unicode.GetString(bytesCipher);
          }
    
          /// <summary>
          /// DES解密方法
          /// </summary>
          /// <param name="strCipher">密文</param>
          /// <param name="strDESKey">密鑰</param>
          /// <param name="strDESIV">向量</param>
          /// <returns>明文</returns>
          public string DESDecrypt(string strCipher, string strDESKey, string strDESIV)
          {
              //把密鑰轉換成字節數組
              byte[] bytesDESKey = ASCIIEncoding.ASCII.GetBytes(strDESKey);
              //把向量轉換成字節數組
              byte[] bytesDESIV = ASCIIEncoding.ASCII.GetBytes(strDESIV);
              //把密文轉換成字節數組
              byte[] bytesCipher = UnicodeEncoding.Unicode.GetBytes(strCipher);
              //聲明1個新的DES對象
              DESCryptoServiceProvider desDecrypt = new DESCryptoServiceProvider();
              //開闢一塊內存流,並存放密文字節數組
              MemoryStream msDecrypt = new MemoryStream(bytesCipher);
              //把內存流對象包裝成解密流對象
              CryptoStream csDecrypt = new CryptoStream(msDecrypt, desDecrypt.CreateDecryptor(bytesDESKey, bytesDESIV), CryptoStreamMode.Read);
              //把解密流對象包裝成讀出流對象
              StreamReader srDecrypt = new StreamReader(csDecrypt);
              //明文=讀出流的讀出內容
              string strPlainText = srDecrypt.ReadLine();
              //讀出流關閉
              srDecrypt.Close();
              //解密流關閉
              csDecrypt.Close();
              //內存流關閉
              msDecrypt.Close();
              //返回明文
              return strPlainText;
          }
    
      }
  }

 

除此之外我們還可以用DES加密文件:

using System;
  using System.IO;
  using System.Security.Cryptography;
    
  namespace EncyptDemo
  {
      public class FileEncrypt
      {
          private static void EncryptData(String pathInput, String pathOutput, byte[] desKey, byte[] desIV)
          {
              //Create the file streams to handle the input and output files.
              FileStream fin = new FileStream(pathInput, FileMode.Open, FileAccess.Read);
              FileStream fout = new FileStream(pathOutput, FileMode.OpenOrCreate, FileAccess.Write);
              fout.SetLength(0);
    
              //Create variables to help with read and write.
              byte[] bin = new byte[100]; //This is intermediate storage for the encryption.
              long rdlen = 0;              //This is the total number of bytes written.
              long totlen = fin.Length;    //This is the total length of the input file.
              int len;                     //This is the number of bytes to be written at a time.
    
              DES des = new DESCryptoServiceProvider();
              CryptoStream encStream = new CryptoStream(fout, des.CreateEncryptor(desKey, desIV), CryptoStreamMode.Write);
    
              //Read from the input file, then encrypt and write to the output file.
              while (rdlen < totlen)
              {
                  len = fin.Read(bin, 0, 100);
                  encStream.Write(bin, 0, len);
                  rdlen = rdlen + len;
              }
    
              encStream.Close();
              fout.Close();
              fin.Close();
          }
    
          //解密文件
          private static void DecryptData(String pathInput, String pathOutput, byte[] desKey, byte[] desIV)
          {
              //Create the file streams to handle the input and output files.
              FileStream fin = new FileStream(pathInput, FileMode.Open, FileAccess.Read);
              FileStream fout = new FileStream(pathOutput, FileMode.OpenOrCreate, FileAccess.Write);
              fout.SetLength(0);
    
              //Create variables to help with read and write.
              byte[] bin = new byte[100]; //This is intermediate storage for the encryption.
              long rdlen = 0;              //This is the total number of bytes written.
              long totlen = fin.Length;    //This is the total length of the input file.
              int len;                     //This is the number of bytes to be written at a time.
    
              DES des = new DESCryptoServiceProvider();
              CryptoStream encStream = new CryptoStream(fout, des.CreateDecryptor(desKey, desIV), CryptoStreamMode.Write);
    
              //Read from the input file, then encrypt and write to the output file.
              while (rdlen < totlen)
              {
                  len = fin.Read(bin, 0, 100);
                  encStream.Write(bin, 0, len);
                  rdlen = rdlen + len;
              }
    
              encStream.Close();
              fout.Close();
              fin.Close();
          }
      }
  }

 

 2.2、非對稱加密

不對稱加密算法使用兩把完全不同但又是完全匹配的一對鑰匙—公鑰和私鑰。在使用不對稱加密算法加密文件時,只有使用匹配的一對公鑰和私鑰,才能完成對明文的加密和解密過程。加密明文時採用公鑰加密,解密密文時使用私鑰才能完成,而且發信方(加密者)知道收信方的公鑰,只有收信方(解密者)纔是唯一知道自己私鑰的人。不對稱加密算法的基本原理是,如果發信方想發送只有收信方纔能解讀的加密信息,發信方必須首先知道收信方的公鑰,然後利用收信方的公鑰來加密原文;收信方收到加密密文後,使用自己的私鑰才能解密密文。顯然,採用不對稱加密算法,收發信雙方在通信之前,收信方必須將自己早已隨機生成的公鑰送給發信方,而自己保留私鑰。由於不對稱算法擁有兩個密鑰,因而特別適用於分佈式系統中的數據加密。

廣泛應用的不對稱加密算法有RSA算法和美國國家標準局提出的DSA。以不對稱加密算法爲基礎的加密技術應用非常廣泛。尤其是在Linux系統下會經常用到這種非對稱加密。反正你只需要記住一點有公鑰和私鑰對的就是非對稱加密。

下面我們來看看C#中的怎麼實現RSA非對稱加密。

首先,我們要生成一個公鑰和私鑰對。

using System.IO;
    using System.Security.Cryptography;
      
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                using (StreamWriter writer = new StreamWriter("PrivateKey.xml",false))  //這個文件要保密...
                {
                    writer.WriteLine(rsa.ToXmlString(true));
                }
                using (StreamWriter writer = new StreamWriter("PublicKey.xml",false))
                {
                    writer.WriteLine(rsa.ToXmlString(false));
      
                }
            }
        }
    }

運行上面的控制檯程序會在程序目錄下生成配對的公鑰和私鑰文件。

接下來寫一個RSA加密解密類

using System;
    using System.Collections.Generic;
    using System.linq;
    using System.Text;
    using System.Security.Cryptography;
      
    namespace ClassLibrary1
    {
        public class RSAHelper
        {
            static string PublicKey = @"<RSAKeyValue><Modulus>nwbjN1znmyL2KyOIrRy/PbWZpTi+oekJIoGNc6jHCl0JNZLFHNs70fyf7y44BH8L8MBkSm5sSwCZfLm5nAsDNOmuEV5Uab5DuWYSE4R2Z3NkKexJJ4bnmXEZYXPMzTbXIpyvU2y9YVrz1BjjRPeHsb6daVdrBgjs4+2b/ok9myM=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
            static string PrivateKey = @"<RSAKeyValue><Modulus>nwbjN1znmyL2KyOIrRy/PbWZpTi+oekJIoGNc6jHCl0JNZLFHNs70fyf7y44BH8L8MBkSm5sSwCZfLm5nAsDNOmuEV5Uab5DuWYSE4R2Z3NkKexJJ4bnmXEZYXPMzTbXIpyvU2y9YVrz1BjjRPeHsb6daVdrBgjs4+2b/ok9myM=</Modulus><Exponent>AQAB</Exponent><P>2PfagXD2zKzUGLkAXpC+04u0xvESpO1PbTUOGA2m8auviEMNz8VempJ/reOlJjEO2q2nrUsbtqKd0m96Cxz0Fw==</P><Q>u6Kiit1XhIgRD9jQnQh36y28LOmku2Gqn9KownMSVGhWzkkHQPw77A2h1OirQiKe6aOIO/yxdwTI/9Ds4Kwc1Q==</Q><DP>GfwtPj1yQXcde8yEX88EG7/qqbzrl7cYQSMOihDwgpcmUbJ+L/kaaHbNNd1CxT0w4z3TDC0np4r4TeCuBDC2hw==</DP><DQ>hl8I0jOC2klrFpMpilunLUeaa/uCWiKuQzhkXKR1qvbxu1b3F+XKr9hvXX6mLn2GmkDfbj4fhOFrZC/lg1weZQ==</DQ><InverseQ>P1y+6el2+1LsdwL14hYCILvsTKGokGSKD35N7HakLmHNjXiU05hN1cnXMsGIZGg+pNHmz/yuPmgNLJoNZCQiCg==</InverseQ><D>D27DriO99jg2W4lfQi2AAaUV/Aq9tUjAMjEQYSEH7+GHe0N7DYnZDE/P1Y5OsWEC76I8GV0N9Vlhi9EaSiJndRvUgphTL2YuAjrVr59Il+lwh/LUBN46AX3cmQm3cFf1F1FXKj4S+QCx/qrCH4mmKpynuQsPL/1XiQSWpugI30E=</D></RSAKeyValue>";
            static RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);
      
            public static byte[] EncryptData(byte[] data)
            {
      
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024);
      
                //將公鑰導入到RSA對象中,準備加密;
                rsa.FromXmlString(PublicKey);
      
                //對數據data進行加密,並返回加密結果;
                //第二個參數用來選擇Padding的格式
                byte[] buffer = rsa.Encrypt(data, false);
                return buffer;
            }
      
            public static byte[] DecryptData(byte[] data)
            {
      
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024);
      
                //將私鑰導入RSA中,準備解密;
                rsa.FromXmlString(PrivateKey);
      
                //對數據進行解密,並返回解密結果;
                return rsa.Decrypt(data, false);
      
            }
             
        }
    }

上面的PublicKey和PrivateKey非別是文件PublicKey.xml和PrivateKey.xml裏面的內容。測試這個RSAHelper的方法:

[TestMethod()]
   public void DecryptDataTest()
   {
       byte[] data = System.Text.ASCIIEncoding.ASCII.GetBytes("Hello");
       byte[] data2 = RSAHelper.EncryptData(data);
       byte[] actual;
       actual = RSAHelper.DecryptData(data2);
       var a = System.Text.ASCIIEncoding.ASCII.GetString(actual);
       Assert.AreEqual("Hello", a);
   }

 

可以看到上面通過EncryptData加密的數據能夠通過DecryptData解密成原來的明文(Hello),而這個加密和解密用是不同的密鑰。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章