文章編號: 307010 - 上次校閱: 2007年1月23日 - 版次: 7.6

如何使用 Visual C# 加密及解密檔案

本文曾發行於 CHT307010
如需本文的 Microsoft Visual Basic .NET 版本,請參閱 301070? (http://support.microsoft.com/kb/301070/ )
本文參照下列 Microsoft .NET Framework 類別庫的命名空間:
  • System.IO
  • System.Security
  • System.Security.Cryptography
注意 本文不適用於 Microsoft .NET Framework 2.0。

在此頁中

全部展開 | 全部摺疊

結論

本文將告訴您,如何使用 Microsoft .NET Framework 所提供的加密類別將文字檔加密成無法讀取的狀態,然後再將該文字檔解密回原始格式。

需求

下面清單列出了建議使用的硬體、軟體、網路基礎架構以及必須具備的 Service Pack:
  • Microsoft Windows 2000 Professional、Windows 2000 Server、Windows 2000 Advanced Server、Windows NT 4.0 Server 或 Microsoft Windows XP Professional
  • Microsoft Visual Studio 2005 或 Microsoft Visual Studio .NET

加密及解密

Microsoft .NET Framework 中的 System.Security.Cryptographic 命名空間提供各種工具來幫助您進行加密及解密。CryptoStream 類別是所提供的眾多類別之一。CryptoStream 類別的目的,是要在將內容以資料流的方式送出至檔案時,加密或解密該內容。

加密檔案

如果要加密檔案,請依照下列步驟執行:
  1. 啟動 Visual Studio 2005 或 Visual Studio .NET。
  2. 按一下 [專案] 下的 [Visual C#],然後按一下 [範本] 下的 [主控台應用程式]。Visual C# .NET 會為您建立 Static 類別,同時附上一個空的 Main() 程序。
  3. 在下列命名空間上使用 using 陳述式 (如後面的範例程式碼所示):
    • 系統
    • System.Security
    • System.Security.Cryptography
    • System.Text
    • System.IO
    如此您就不必在後面的程式碼中限定這些命名空間的宣告。您必須先使用這些陳述式,才能進行任何其他宣告。
    using System;
    using System.IO;
    using System.Security;
    using System.Security.Cryptography;
    using System.Runtime.InteropServices;
    using System.Text;
    					
  4. 產生一個秘密金鑰來加密及解密資料。DESCryptoServiceProvider 是以對稱加密演算法為基礎。對稱加密需要有金鑰及初始化向量 (IV) 來加密資料。如果要解密資料,您必須要有相同的金鑰及相同的 IV。您也必須使用相同的加密演算法。您可以使用下面其中一種方法來產生金鑰:
    • 方法 1 您可以提示使用者輸入密碼。然後使用密碼來做為金鑰及 IV。
    • 方法 2 當您建立新的對稱密碼編譯類別執行個體時,即會自動為工作階段建立新的金鑰及 IV。使用受管理之對稱密碼編譯類別所產生的金鑰及 IV 來加密及解密檔案。

      如需有關如何產生及散發金鑰的詳細資訊,請參閱 Microsoft .NET Framework SDK 文件,或參閱下列 Microsoft Developer Network (MSDN) 網站:
      產生用來加密及解密的金鑰 (英文)
      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcongeneratingkeysforencryptiondecryption.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpcongeneratingkeysforencryptiondecryption.asp)
  5. 新增下列函式,為工作階段產生新的金鑰 (如步驟 4 的方法 2 所指示):
    //  Call this function to remove the key from memory after use for security.
    [System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint="RtlZeroMemory")]
    public static extern bool ZeroMemory(ref string Destination, int Length);
    		
    // Function to Generate a 64 bits Key.
    static string GenerateKey() 
    {
    	// Create an instance of Symetric Algorithm. Key and IV is generated automatically.
    	DESCryptoServiceProvider desCrypto =(DESCryptoServiceProvider)DESCryptoServiceProvider.Create();
    
    	// Use the Automatically generated key for Encryption. 
    	return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
    }
  6. 在名為 EncryptFile 的類別中建立方法。EncryptFile 類別必須要有下列三個參數:
    • sInputFilename
    • sOutputFilename
    • sKey (用來加密及解密檔案的秘密金鑰。)
    static void EncryptFile(string sInputFilename,
    		string sOutputFilename,
    		string sKey)
    					
  7. EncryptFile 程序中,建立一個輸入 FileStream 物件及一個輸出 FileStream 物件。這些物件可以從目標檔案讀取,也可寫入至目標檔案。
    FileStream fsInput = new FileStream(sInputFilename, 
    				FileMode.Open, 
    				FileAccess.Read);
    
    FileStream fsEncrypted = new FileStream(sOutputFilename, 
    				FileMode.Create, 
    				FileAccess.Write);
    					
  8. 宣告 DESCryptoServiceProvider 類別的執行個體。這代表用於該檔案的實際加密及解密技術。此時,如果您偏好使用 RSAsecutiry 或其他密碼編譯技術,您可以建立不同的提供者。
    DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
    					
  9. 您必須提供秘密金鑰給密碼編譯提供者,以做為位元組陣列。System.Text 命名空間會提供一個名為 GetBytes() 的函式。GetBytes() 函式的其中一項編碼功能是會採用字串,然後再傳回位元組陣列。每項密碼編譯技術的金鑰大小各不相同。例如,資料加密標準 (DES) 採用 64 位元的金鑰,相當於 8 個位元組或 8 個字元。

    如果您不提供金鑰,提供者會隨機產生一個金鑰。如此可成功加密檔案,但無法解密檔案。請注意,您也必須提供初始化向量 (IV)。此值會用來做為加密的一部分。跟金鑰一樣,如果您不提供 IV,就會隨機產生一個 IV 值。因為在加密及解密時,該值必須相同,所以您絕不能允許隨機產生這些值。
    DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
    DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
    					
  10. 使用密碼編譯提供者來取得加密物件 (CreateEncryptor) 及現有的輸出 FileStream 物件,來做為建構函式的一部分,以建立 CryptoStream 類別的執行個體。
    ICryptoTransform desencrypt = DES.CreateEncryptor();
    CryptoStream cryptostream = new CryptoStream(fsEncrypted, 
    					desencrypt, 
    					CryptoStreamMode.Write);
    					
  11. 讀取輸入檔,然後寫出至輸出檔。通過 CryptoStream 物件,會在此使用您提供的金鑰來加密檔案。
    byte[] bytearrayinput = new byte[fsInput.Length - 1];
    fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
    cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
    					

解密檔案

如果要解密檔案,請依照下列步驟執行:
  1. 建立一個方法,將其命名為 DecryptFile。解密程序與加密程序類似,但是 DecryptFile 程序與 EncryptFile 程序間有兩個明顯的差異。
    • 建立 CryptoStream 物件時,會使用 CreateDecryptor,而不是使用 CreateEncryptor,以指定該物件的用法。
    • 將解密文字寫入目的地檔案時,CryptoStream 物件即成為來源資料流,而不是目的地資料流。
    static void DecryptFile(string sInputFilename, 
    	                string sOutputFilename,
    	                string sKey)
    {
    	DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
    	//A 64 bit key and IV is required for this provider.
    	//Set secret key For DES algorithm.
    	DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
    	//Set initialization vector.
    	DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
    
    	//Create a file stream to read the encrypted file back.
    	FileStream fsread = new FileStream(sInputFilename, 
    		                           FileMode.Open, 
    		                           FileAccess.Read);
    	//Create a DES decryptor from the DES instance.
    	ICryptoTransform desdecrypt = DES.CreateDecryptor();
    	//Create crypto stream set to read and do a 
    	//DES decryption transform on incoming bytes.
    	CryptoStream cryptostreamDecr = new CryptoStream(fsread, 
    		                                         desdecrypt,
    		                                         CryptoStreamMode.Read);
    	//Print the contents of the decrypted file.
    	StreamWriter fsDecrypted = new StreamWriter(sOutputFilename);
    	fsDecrypted.Write(new StreamReader(cryptostreamDecr).ReadToEnd());
    	fsDecrypted.Flush();
    	fsDecrypted.Close();
    }
    					
  2. 將下列程式行新增至 Main() 程序,以呼叫 EncryptFileDecryptFile
    static void Main()
    {
          // Must be 64 bits, 8 bytes.
          // Distribute this key to the user who will decrypt this file.
          string sSecretKey;
             
          // Get the key for the file to encrypt.
          sSecretKey = GenerateKey();
    
          // For additional security pin the key.
          GCHandle gch = GCHandle.Alloc( sSecretKey,GCHandleType.Pinned );
             
          // Encrypt the file.        
          EncryptFile(@"C:\MyData.txt", 
             @"C:\Encrypted.txt", 
             sSecretKey);
    
          // Decrypt the file.
          DecryptFile(@"C:\Encrypted.txt", 
             @"C:\Decrypted.txt", 
             sSecretKey);
    
          // Remove the key from memory. 
          ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2);
          gch.Free();
    }
  3. 儲存檔案。執行應用程式。確定用於輸入檔名稱的路徑是指向現有的檔案。

測試程序

使用文字檔 (.txt) 來測試此程式碼,以確認該程式碼能夠正確地加密及解密該檔案。確定您是將檔案解密至新的檔案 (如本文的 Main() 程序所述),而不是解密至原始檔案。檢查解密後的檔案,然後與原始檔案做比較。

完整的程式碼清單

using System;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Text;

namespace CSEncryptDecrypt
{
   class Class1
   {
      //  Call this function to remove the key from memory after use for security
      [System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint="RtlZeroMemory")]
      public static extern bool ZeroMemory(IntPtr Destination, int Length);
		
      // Function to Generate a 64 bits Key.
      static string GenerateKey() 
      {
         // Create an instance of Symetric Algorithm. Key and IV is generated automatically.
         DESCryptoServiceProvider desCrypto =(DESCryptoServiceProvider)DESCryptoServiceProvider.Create();

         // Use the Automatically generated key for Encryption. 
         return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
      }

      static void EncryptFile(string sInputFilename,
         string sOutputFilename, 
         string sKey) 
      {
         FileStream fsInput = new FileStream(sInputFilename, 
            FileMode.Open, 
            FileAccess.Read);

         FileStream fsEncrypted = new FileStream(sOutputFilename, 
            FileMode.Create, 
            FileAccess.Write);
         DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
         DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
         DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
         ICryptoTransform desencrypt = DES.CreateEncryptor();
         CryptoStream cryptostream = new CryptoStream(fsEncrypted, 
            desencrypt, 
            CryptoStreamMode.Write); 

         byte[] bytearrayinput = new byte[fsInput.Length];
         fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
         cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
         cryptostream.Close();
         fsInput.Close();
         fsEncrypted.Close();
      }

      static void DecryptFile(string sInputFilename, 
         string sOutputFilename,
         string sKey)
      {
         DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
         //A 64 bit key and IV is required for this provider.
         //Set secret key For DES algorithm.
         DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
         //Set initialization vector.
         DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);

         //Create a file stream to read the encrypted file back.
         FileStream fsread = new FileStream(sInputFilename, 
            FileMode.Open, 
            FileAccess.Read);
         //Create a DES decryptor from the DES instance.
         ICryptoTransform desdecrypt = DES.CreateDecryptor();
         //Create crypto stream set to read and do a 
         //DES decryption transform on incoming bytes.
         CryptoStream cryptostreamDecr = new CryptoStream(fsread, 
            desdecrypt,
            CryptoStreamMode.Read);
         //Print the contents of the decrypted file.
         StreamWriter fsDecrypted = new StreamWriter(sOutputFilename);
         fsDecrypted.Write(new StreamReader(cryptostreamDecr).ReadToEnd());
         fsDecrypted.Flush();
         fsDecrypted.Close();
      } 

      static void Main()
      {
         // Must be 64 bits, 8 bytes.
         // Distribute this key to the user who will decrypt this file.
         string sSecretKey;
         
         // Get the Key for the file to Encrypt.
         sSecretKey = GenerateKey();

         // For additional security Pin the key.
         GCHandle gch = GCHandle.Alloc( sSecretKey,GCHandleType.Pinned );
         
         // Encrypt the file.        
         EncryptFile(@"C:\MyData.txt", 
            @"C:\Encrypted.txt", 
            sSecretKey);

         // Decrypt the file.
         DecryptFile(@"C:\Encrypted.txt", 
            @"C:\Decrypted.txt", 
            sSecretKey);

         // Remove the Key from memory. 
         ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2);
         gch.Free();
      }
   }
}

?考

如需有關加密以及使用 .NET 之密碼編譯功能的詳細資訊,請參閱下列 MSDN 網站:
System.Security.Cryptography 命名空間 (英文)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemsecuritycryptography.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemsecuritycryptography.asp)
Microsoft .NET Framework 開發人員中心 (英文)
http://msdn2.microsoft.com/en-us/netframework/default.aspx (http://msdn2.microsoft.com/en-us/netframework/default.aspx)
如需有關 Visual C# .NET 的一般資訊,請參閱下列 Usenet 新聞群組:
microsoft.public.dotnet.languages.csharp (http://go.microsoft.com/fwlink/?linkid=5217)

這篇文章中的資訊適用於:
  • Microsoft Visual C# 2005
  • Microsoft Visual C# .NET 2003 標準版
  • Microsoft Visual C# .NET 2002 Standard Edition
關鍵字:?
kbsecurity kbio kbcrypt kbhowtomaster KB307010
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。
 

文章翻譯

 

Related Support Centers