用.NET做對稱加密和解密

  • 3882
  • 0

因為今天又有朋友問我,從我舊的Blog貼過來的。就是.NET對加密技術支援的一個簡介,包含一些基本元素的介紹。

.NET提供很多對稱加密法的演算法, 它們不止能對文字加密. 但我這裡以DES對文字加密作為例子. 你可以在System.Security.Cryptography找到其他對稱演算法的實作. RijndaelManaged, DESCryptoServiceProvider, RC2CryptoServiceProvider and RtipleDESCryptoServiceProvider都是SymmetricAlgorithm類別的實作.

有兩個概念我們要知道. 我們需要兩個元素去實行加密和解密. 一個初始向量和金鑰. 你有可能會問初始向量有甚麼用途. 如果我們用一個金鑰去做加密. 密件將會是多個同一長度的片段所組成. 這樣可以對密件的每個片段對照找出模式,比較容易破解. 所以我們用一種類似遞回的方法對我們的資料加密. 由三個輸入去產生一個加密過的片段. 分別是對上一個已加密片段, 現時還沒加密的資料片段和金鑰. 因為我們在演算法開始時並沒有’上一個加密過的片段’, 這就是要用到初始向量的時候了.

另一個概念是, 不同的對稱加密法有不同的合法金鑰長度, 就是有一個合法的金鑰長度範圍. 我們稍後再處理這個問題.

加密如下 :

 

public static string EncryptToString(string PlainText, string key, string initialVector)
        {
            // Create a memory stream.
            MemoryStream ms = new MemoryStream();
            // Create the algorithm provider
            DESCryptoServiceProvider l_CryptoProvider = new DESCryptoServiceProvider();

            byte[] l_key = GetValidKeyFromString(key, l_CryptoProvider);
            byte[] l_initialVector = System.Text.Encoding.Unicode.GetBytes(initialVector);
            // Create a CryptoStream using the memory stream and the
            // CSP DES key.
            CryptoStream encStream = new CryptoStream(ms, l_CryptoProvider.CreateEncryptor(l_key, l_initialVector), CryptoStreamMode.Write);
            // Create a StreamWriter to write a string
            // to the stream.
            StreamWriter sw = new StreamWriter(encStream);
            // Write the plaintext to the stream.
            sw.WriteLine(PlainText);
            // Close the StreamWriter and CryptoStream.
            sw.Flush();
            sw.Close();
            encStream.Close();
            // Get an array of bytes that represents
            // the memory stream.
            byte[] buffer = ms.ToArray();
            // Close the memory stream.
            ms.Close();
            // Return the encrypted byte array.
            // Convert to Base64 so that we can write it into text file
            return Convert.ToBase64String(buffer);
            }

解密如下 :

過程並不複雜, 看註解應該就能了解. 創建一個記憶體流, 包裝成密碼服務提供者, 對它做寫入/讀取.
還剩一件事, 靜態方法GetValidKeyFromString(key, l_CryptoProvider), 程式碼如下 :

我假設你給定的金鑰長度小於或等於預設合法金鑰長度. 如果你給的金鑰長度比較長, 那就直接用你的金鑰. 如果你的金鑰較短, 這返法就會把金鑰不斷重覆讓它增長到合法長度為止, 就好像”我是金鑰我是金鑰…”. KeySize屬性會告訴你預設合法金鑰長度. 你可以發現我用base64編碼把密件做包裝, 這不是必需的, 我這樣做只是以防我要把密件寫到文字儲存器, 例如應用程式組態.

 public static byte[] GetValidKeyFromString(string keyString, SymmetricAlgorithm cryptoProvider)
        {
            // Get the string as Byte using Unicode since Unicode character is 8bit
            // and also .NET support Unicode by default
            byte[] l_keySeed = System.Text.Encoding.Unicode.GetBytes(keyString);
            int l_seedLength = l_keySeed.Length;
            int l_validKeySize = cryptoProvider.KeySize / 8;
            byte[] l_validKey = new byte[l_validKeySize];
            for(int index = 0; index<l_validKeySize; index++)
            {
                l_validKey[index] = l_keySeed[index % l_seedLength];
            }
            return l_validKey;
        }
public static string DecryptFromString(string CypherText, string key, string initialVector)
        {
            string val = string.Empty;
            if (!string.IsNullOrEmpty(CypherText))
            {
                // Create a memory stream to the passed buffer.
                // Convert from base64 back to binary first
                byte[] l_CypherByte = Convert.FromBase64String(CypherText);
                MemoryStream ms = new MemoryStream(l_CypherByte);
                DESCryptoServiceProvider l_CryptoProvider = new DESCryptoServiceProvider();

                byte[] l_key = GetValidKeyFromString(key, l_CryptoProvider);
                byte[] l_initialVector = System.Text.Encoding.Unicode.GetBytes(initialVector);
                // Create a CryptoStream using the memory stream and the
                // CSP DES key.
                CryptoStream encStream = new CryptoStream(ms, l_CryptoProvider.CreateDecryptor(l_key, l_initialVector), CryptoStreamMode.Read);
                // Create a StreamReader for reading the stream.
                StreamReader sr = new StreamReader(encStream);
                // Read the stream as a string.
                val = sr.ReadLine();
                // Close the streams.
                sr.Close();
                encStream.Close();
                ms.Close();
            }
            return val;
        }

My WP Blog with english technical docs.