Symmetric Encrypt and Decrypt text with .NET 對稱加密和解密

There are many symmetric algorithm provided by .NET which not only can encrypt text. But I use DES with text data as an example.
You can find those symmetric algorithm under System.Security.Cryptography. RijndaelManaged, DESCryptoServiceProvider, RC2CryptoServiceProvider and RtipleDESCryptoServiceProvider are all implementation of SymmetricAlgorithm class.
.NET提供很多對稱加密法的演算法, 它們不止能對文字加密. 但我這裡以DES對文字加密作為例子. 你可以在System.Security.Cryptography找到其他對稱演算法的實作. RijndaelManaged, DESCryptoServiceProvider, RC2CryptoServiceProvider and RtipleDESCryptoServiceProvider都是SymmetricAlgorithm類別的實作.
There is two ideas we should know. We need two elements to do the encryption and decryption. An initial vector and the secret key of course. So what it the initial vector you may ask. If we encrypt the data with only using the secret key. The cypher will be in chunks all with same length. This will make it easier to break the cypher by finding pattern of a chunk. So, we use a recurrsive like mechanism to encrypt our data. Use three input to generate the output encrypted chunk. The last encrypted chunk, the current not yet encrypted chunk and the secret key. Here is where the initial vector comes in, we don’t have the ‘last encrypted chunk’ when we are at the very begining of the encryption.
有兩個概念我們要知道. 我們需要兩個元素去實行加密和解密. 一個初始向量和金鑰. 你有可能會問初始向量有甚麼用途. 如果我們用一個金鑰去做加密. 密件將會是多個同一長度的片段所組成. 這樣可以對密件的每個片段對照找出模式,比較容易破解. 所以我們用一種類似遞回的方法對我們的資料加密. 由三個輸入去產生一個加密過的片段. 分別是對上一個已加密片段, 現時還沒加密的資料片段和金鑰. 因為我們在演算法開始時並沒有’上一個加密過的片段’, 這就是要用到初始向量的時候了.
Another idea is, different symmetric algorithm have different valid key length, that is a key length range. We will see how to due with this later.
Encryption as follow :

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

加密如下 :

        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);
            }

Decryption as follow :

解密如下 :

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;
        }

The process is not complicated, you can understand it by reading comments. Create a stream, wrap it as a crypto service provider, write/read data through the provider.
There is only one thing to talk about, the static method GetValidKeyFromString(key, l_CryptoProvider). Code as follow :

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

        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;
        }

I assume you give a key with short or equal length as the default valid key length. If you have a key that is longer than that, you can just use it. If you are using a shorter key, This method will extend it to the valid length by repeating the key over and over again like this “IAMTHEKEYIAMTHEKEY…”. The KeySize property is the default valid key length. You may also notice that I wrap my cypher with base64, this is not necessary, I do this just in case I need to save the cypher to some text base container such as app.config.

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

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s