當前位置:開發者網絡 >> 技術教程 >> .NET教程 >> .NET Framework >> 內容
精彩推薦
分類最新教程
分類熱點教程
    
Microsoft .NET 中的簡化加密
作者:未知
日期:2005-04-07
人氣:
投稿:snow(轉貼)
來源:未知
字體:
收藏:加入瀏覽器收藏
以下正文:
適用於:
Microsoft® .NET
安全
Microsoft® Visual Basic® .NET
C#

摘要:學習如何利用 .NET Framework 的加密功能創建類似本文所述的包裝程序來保護您的數據。

下載與本文相關的 CryptoSampleCSSample.msi 和 CryptoSampleVBSample.msi 代碼示例。(請注意,在示例文件中,程序員的註釋使用的是英文,本文中將其譯為中文是為了便於讀者理解。)

目錄
散列簡介
創建示例散列項目
在散列中添加「鹽」值
小結

您希望在計算機上保存一些機密信息嗎?如果是,本文就為您介紹如何進行加密!加密技術就是將有意義的字符編碼成無意義的字符,使不應該訪問這些數據的人員無法讀取它們。加密技術已經存在很多年了,甚至遠在計算機誕生之前就已經存在了。隨著計算機的出現,在計算機領域應用加密技術可以生成幾乎牢不可破的代碼。Microsoft 在 Windows 95 中開發並分發了加密 API。使用 Microsoft .NET,新創建的類可以將這些複雜的算法打包到非常易於使用的屬性和方法中。

散列簡介
如果您只是不想讓別人竊取您的密碼,那麼可以為密碼數據創建一個散列。散列是一種單向算法,一旦數據被轉換,將無法再獲得其原始值。大多數開發人員使用數據庫存儲密碼。但是,在數據庫中查找用戶數據的人員也能夠看到這些密碼。不過,您可以使用散列算法對密碼進行加密,然後再將其存儲在數據庫中。用戶輸入密碼後,您可以再次使用散列算法對其進行解密,然後將其與存儲在數據庫中的散列進行比較。散列的缺點之一是,即使原始數據只發生一個小小的改動,數據的散列也會發生非常大的變化。Pork 和 Porky 這兩個單詞非常相似,但使用散列算法加密後的結果卻相去甚遠。您可能根本看不出二者之間有什麼相似之處。

.NET 開發人員可以使用多種散列算法類。最常用的是 SHA1 和 MD5。下面我們看一下如何為「Paul」這樣的普通字符串生成散列,使任何人都無法識別它。

使用 SHA1 生成散列
我們創建一個新例程,然後使用它為字符串「Paul」生成散列。在 Visual Studio® .NET 中打開一個新的 Windows 應用程序,在窗體上放置一個命令按鈕。當該命令按鈕上發生 Click 事件時,將調用名為 HashText() 的方法。您可以將以下代碼添加到該窗體中,看一下此散列算法的實際效果。編寫下列代碼之前,需要導入命名空間 System.Security.Cryptography。

Private Sub HashText(ByVal TextToHash As String)
Dim SHA1 As SHA1CryptoServiceProvider
Dim bytValue() As Byte
Dim bytHash() As Byte

' 創建新的加密服務提供程序對像
SHA1 = New SHA1CryptoServiceProvider

' 將原始字符串轉換成字節數組
bytValue = _
System.Text.Encoding.UTF8.GetBytes(TextToHash)

' 計算散列,並返回一個字節數組
bytHash = SHA1.ComputeHash(bytValue)

SHA1.Clear()

' 返回散列值的 Base64 編碼字符串
Debug.WriteLine(Convert.ToBase64String(bytHash))
End Sub
您可以傳遞不同的字符串值來調用該例程,查看散列值的變化。例如,如果將字符串「Paul」傳遞給該例程,Debug(調試)窗口將顯示以下文本:

w2h6uYgMJt/nq5ZqihcBteAXwv8=
現在,將此過程中的輸入值更改為「Pauly」。您將看到以下輸出結果:

proywxJ0znMpGF5sbB18+7GSAsM=
如您所見,輸入字符串的一個小小變化就會產生完全不同的字符組合。這正是散列算法之所以有效的原因,它使我們很難找到輸入字符串的規律,也很難根據加密後的字符弄清楚字符串原來的模樣。

使用 MD5 也可以生成散列
瞭解一種散列類的使用方法後,基本上就瞭解了所有的散列類。下面的方法用於 MD5 散列算法。注意,除了 CryptoServiceProvider 類不同外,代碼是完全相同的。

Private Sub HashTextMD5(ByVal TextToHash As String)
Dim md5 As MD5CryptoServiceProvider
Dim bytValue() As Byte
Dim bytHash() As Byte

' 創建新的加密服務提供程序對像
md5 = New MD5CryptoServiceProvider

' 將原始字符串轉換成字節數組
bytValue = System.Text.Encoding. _
UTF8.GetBytes(TextToHash)

' 計算散列,並返回一個字節數組
bytHash = md5.ComputeHash(bytValue)

md5.Clear()

' 返回散列值的 Base64 編碼字符串
Debug.WriteLine(Convert.ToBase64String(bytHash))
End Sub
輸入「Paul」之後,MD5 散列算法的輸出結果如下所示:

nVWBsHh1MKNctPioSyqyTQ==
同樣,加密後的字符串看起來也與原始輸入相去甚遠。這些散列算法對於創建沒有任何意義的密碼來說非常有用,也使黑客很難猜出這些密碼。之所以使用散列算法,是因為可以用這種算法對密碼進行加密並將其存儲在數據庫中。然後,當用戶輸入真實密碼時,您先對密碼進行解密,然後通過網絡發送到數據庫中,比較它與數據庫中的密碼是否匹配。請記住,散列是單向操作。使用散列算法對原始密碼加密後將無法再恢復。

如何選擇算法
本文介紹的兩種散列算法都執行同一種操作。不同之處只在於生成散列的密鑰大小以及使用的算法。使用的密鑰越大,加密就越安全。例如,MD5 使用的加密密鑰比 SHA1 使用的密鑰大,因此 MD5 散列較難破解。

對於散列算法要考慮的另外一點是,從實踐或理論的角度上看是否存在衝突的可能性。衝突是我們所不希望的,因為兩個不同的單詞可能會生成相同的散列。例如,SHA1 從實踐或理論上來講沒有發生衝突的可能性。MD5 從理論上講有發生衝突的可能性,但從實踐上講沒有發生衝突的可能性。因此,選擇哪種算法歸根結底取決於您所需要的安全級別。

創建示例散列項目
本文包含兩個示例散列項目,以更普通的方式說明如何使用不同的散列算法加密任意字符串。這兩個示例項目的名稱分別為 CryptoSampleVB.sln 和 CryptoSampleCS.sln。前者是 Visual Basic .NET 解決方案,後者是 C# 解決方案。兩個解決方案都包括一個類似圖 1 的窗體,該窗體允許您輸入要通過散列算法為其加密的原始字符串,還提供一個用來選擇散列算法的選項按鈕和一個顯示散列結果的文本框。



圖 1:創建一個通用散列屏幕來嘗試兩種散列算法。

單擊此屏幕上的 Hash(散列)按鈕時,將運行該按鈕的 Click 事件過程。此事件過程將調用一個名為 HashString() 的例程。

' Visual Basic .NET
Private Sub btnHash_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnHash.Click
txtHashed.Text = HashString(txtOriginal.Text)
End Sub
// C#
private void cmdHash_Click(object sender,
System.EventArgs e)
{
txtHashed.Text = HashString(txtOriginal.Text);
}
HashString() 方法接受輸入的值並調用 SetHash() 方法。此方法將根據窗體上選項按鈕的設置來決定使用哪個加密服務提供程序創建該方法的實例並返回該方法。將為該窗體創建一個名為 mHash 的 HashAlgorithm 類型的成員變量。HashAlgorithm 類型是創建所有散列加密服務提供程序的基類。

' Visual Basic .NET
Private mhash As HashAlgorithm

// C#
private HashAlgorithm mhash;
SetHash() 方法如下所示:

' Visual Basic .NET
Private Function SetHash() As HashAlgorithm
If optSHA1.Checked Then
Return New SHA1CryptoServiceProvider
Else
If optMD5.Checked Then
Return New MD5CryptoServiceProvider
End If
End If
End Function

// C#
private HashAlgorithm SetHash()
{
if(this.optSHA1.Checked)
return new SHA1CryptoServiceProvider();
else
return new MD5CryptoServiceProvider();
}
根據您在窗體上選擇的選項按鈕,此方法將創建並返回一個不同的 HashAlgorithm 類型。HashString() 方法在該窗體上執行實際的數據加密:

' Visual Basic .NET
Private Function HashString(ByVal Value As String) _
As String
Dim bytValue() As Byte
Dim bytHash() As Byte

' 創建新的加密服務提供程序對像
mhash = SetHash()

' 將原始字符串轉換成字節數組
bytValue = System.Text.Encoding.UTF8.GetBytes(Value)

' 計算散列,並返回一個字節數組
bytHash = mhash.ComputeHash(bytValue)

mhash.Clear()

' 返回散列值的 Base64 編碼字符串
Return Convert.ToBase64String(bytHash)
End Function


// C#
private string HashString(string Value)
{
mhash = SetHash();

// 將原始字符串轉換成字節數組
byte[] bytValue = System.Text.Encoding.UTF8.GetBytes(Value);

// 計算散列,並返回一個字節數組
byte[] bytHash = mhash.ComputeHash(bytValue);

mhash.Clear();

// 返回散列值的 Base64 編碼字符串
return Convert.ToBase64String(bytHash);
}
在 HashString 方法中,我們創建了兩個字節數組。第一個數組用來保存用戶的原始字符串輸入。我們使用 System.Text.Encoding.UTF8.GetBytes() 方法將該字符串轉換成字節數組。將原始字符串轉換成字節數組後,現在使用服務提供程序的 ComputeHash() 方法計算該字符串的散列值。此方法接受字節數組作為輸入,然後返回該字符串加密格式的字節數組。

注意:完成之後清除散列變量是一個好的做法。因此,您看到計算該字符串的散列後,我們調用了 Clear 方法。
現在我們已經獲得了加密的字節數組,這正是要從該方法返回的數組。因為我們要將原始值和加密值都作為字符串數據類型而不是字節數組進行處理,所以要通過使用 Convert.ToBase64String 方法返回加密的字節。此方法負責將字節數組轉換成 Base64 編碼的字符串。Base64 編碼的使用非常重要,因為有可能需要將此字符串推到 Web 頁上或將其存儲到數據庫中。如果不進行轉換,加密字符串中的某些高階 ASCII 字符將無法正確顯示或存儲。

在散列中添加一些「鹽」值
到目前為止,散列算法暴露出來的問題之一是,如果兩個用戶碰巧使用相同的密碼,那麼散列值將完全相同。如果黑客看到您存儲密碼的表格,會從中找到規律並明白您很可能使用了常見的詞語,然後黑客會開始詞典攻擊以確定這些密碼。要確保任何兩個用戶密碼的散列值都不相同,一種方法是在加密密碼之前,在每個用戶的密碼中添加一個唯一的值。這個唯一值稱為「鹽」值。在進行此操作時,需要確保將使用的鹽值存儲為用戶記錄的一部分。如果您使用表格存儲用戶 ID 和密碼,我建議您使用不同的表格來存儲鹽值。這樣,即使數據庫洩漏,鹽值也可以為您提供一層額外的安全保護。

在用戶密碼中添加鹽值的方法有多種。最簡單的方法是摘取用戶的某些信息(例如姓、名、電子郵件地址或員工 ID)並將其添加到用戶密碼中,然後再進行加密。這種方法的缺點是,因為您需要存儲鹽值,所以如果黑客找到該值,將會對您所做的一切操作瞭如指掌。當然,黑客需要花費額外的時間來破解鹽值,但這對黑客來說簡直是易如反掌。

另外一種方法是使用 .NET Framework 類 RNGCryptoServiceProvider 創建一個隨機的數字字符串。RNG 表示隨機數生成器。該類可以創建一個任意長度的隨機字節數組,長度由您指定。您可以使用此隨機字節數組作為散列算法的鹽值。要採用這種方法,必須安全地存儲該鹽值。

在圖 2 所示的示例中,您需要在文本框中輸入一個字符串,選擇特定的散列類型,然後生成鹽值以及包含該鹽值和原始字符串的散列值。


圖 2:在散列值中添加鹽值以創建更安全的密碼散列
(需要存儲鹽值以便再次創建相同的散列。)

該示例與本文中的上一個示例基本相同,不同之處在於創建鹽值的例程。在此屏幕上的按鈕的 Click 事件下,首先調用一個名為 CreateSalt() 的方法來生成一個唯一的鹽值,然後將該值存儲到 txtSalt 文本框中。獲得唯一的鹽值後,再調用 HashString() 方法,將這兩個值結合起來。

' Visual Basic .NET
Private Sub btnHash_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnHash.Click
txtSalt.Text = CreateSalt()
txtHashed.Text = HashString(txtSalt.Text & _
txtOriginal.Text)
End Sub

// C#
private void cmdHash_Click(object sender, System.EventArgs e)
{
txtSalt.Text = CreateSalt();
txtHashed.Text = HashString(txtOriginal.Text);
}
CreateSalt() 方法的代碼非常簡單。它首先創建一個長度為 8 個字節的字節數組,然後您創建一個新的 RNGCryptoServiceProvider 類實例。使用該對象的 GetBytes() 方法,將生成的隨機字符集填充到字節數組中。然後將此字節數組轉換成 Base64 編碼字符串並從該函數返回。

' Visual Basic .NET
Private Function CreateSalt() As String
Dim bytSalt(8) As Byte
Dim rng As New RNGCryptoServiceProvider

rng.GetBytes(bytSalt)

Return Convert.ToBase64String(bytSalt)
End Function

// C#
private string CreateSalt()
{
byte[] bytSalt = new byte[8];
RNGCryptoServiceProvider rng;

rng = new RNGCryptoServiceProvider();

rng.GetBytes(bytSalt);

return Convert.ToBase64String(bytSalt);
}
數據加密是一個雙行道
如果需要在兩個或多個人員或計算機之間來回發送信息,並希望對方能夠讀取數據,而其他人不可以讀取,那麼加密則是最好的方式!加密算法使您可以將數據掩蓋起來,除了特定人員能夠對其解密外,其他人員不大可能通過數學方法讀取該數據。但如果您希望某個人能夠讀取該數據,您可以為其提供一個特定的「密鑰」,使其能夠解密並讀取數據。.NET Framework 中有多種可用的加密/解密算法。本文主要介紹對稱算法,包括以下幾種:

DES
RC2
Rijndael
TripleDES
對稱算法(或密鑰算法)使用一個密鑰和一個初始化向量 (IV) 來保證數據的安全。使用該數據的雙方都必須知道這個密鑰和初始化向量才能夠加密和解密數據。必須確保該密鑰的安全,否則其他人將有可能解密該數據並讀取該消息。初始化向量只是一個隨機生成的字符集,使用它可以確保任何兩個文本都不會生成相同的加密數據。使用 .NET 中不同的加密類的內置方法可以導出密鑰,至於如何導出密鑰,則不屬於本文要討論的內容。

其他類型的加密算法稱為不對稱算法。不對稱算法使用公鑰/私鑰對來創建加密數據。不對稱算法將在下文進行討論。

如何在不同的情況下選擇不同的加密方法
對稱算法(或密鑰算法)的速度非常快,非常適於加密大型的數據流。這些算法可以加密數據,也可以解密數據。它們都相當安全,但如果有足夠的時間,也可能會被破密,因為有人可能會搜索每個已知的密鑰值組合。由於每種算法都使用固定的密鑰長度或 ASCII 字符,因此計算機程序可以嘗試每個可能的密鑰組合併最終找到正確的那個組合。這些類型的算法一般用於存儲和檢索數據庫的連接字符串。

不對稱算法(或公鑰算法)沒有對稱算法快,但其代碼較難破密。這些算法取決於兩個密鑰,一個是私鑰,另一個是公鑰。公鑰用來加密消息,私鑰是可以解密該消息的唯一密鑰。公鑰和私鑰通過數學方法鏈接在一起,因此要成功進行加密交換,必須獲得這兩個密鑰。由於可能會影響到計算機性能,因此不對稱算法不太適用於加密大量數據。不對稱算法的常見用法是將對稱密鑰和初始化向量加密並傳輸給對方。然後在雙方之間來回發送的消息中使用對稱算法加密和解密數據。

如果您不打算再恢復原始值,尤其不希望別人發現原始值,那麼請使用散列值。散列可以將任意長度的字符串加密為固定的字節集。此操作是單向的,因此通常用於密碼這樣的少量數據。當用戶在安全的輸入屏幕上輸入用戶密碼後,程序將對此密碼進行加密並將散列值存儲到數據庫中。即使數據庫洩漏,也沒有人能夠讀取密碼,因為密碼已被加密。當用戶登錄到該系統進行輸入時,將使用相同的算法解密用戶鍵入的密碼,如果兩個散列值相匹配,系統則可以確定用戶輸入的值與以前存儲的值相同。

加密練習
示例應用程序中包括一個窗體,允許您使用 DES 和 TripleDES 加密服務提供程序進行加密練習。該窗體名為 frmEncrypt,如圖 3 所示。



圖 3:加密算法允許您加密並解密值。

在該屏幕上,首先需要單擊 Gen Key(生成密鑰)按鈕,然後單擊 Gen IV(生成 IV)按鈕。然後在 Original String(原始字符串)文本框中輸入一些數據並單擊 Encrypt(加密)按鈕。單擊 Encrypt(加密)按鈕後,加密後的文本將顯示在 Encrypted String(加密字符串)文本框中。如果您希望在自己的應用程序中使用這個加密的字符串,則需要記下生成的密鑰和 IV,因為要解密連接字符串以便再次使用,您需要提供這兩個值。如果丟失了密鑰和 IV,將再也無法恢復連接字符串。

現在看一下其源代碼,瞭解如何實現加密和解密例程。首先看一下該類的成員變量,它用於保存相應加密服務提供程序的引用。該成員變量的類型為 SymmetricAlgorithm。所有的對稱算法類都是從這個基類繼承而來的。

' Visual Basic .NET
Private mCSP As SymmetricAlgorithm
// C#
private SymmetricAlgorithm mCSP;
根據您在此窗體上選擇的選項按鈕,此 mCSP 變量將被指定給特定的對稱算法類。SetEnc() 方法將負責創建適當的類型並將其返回到不同的方法。

' Visual Basic .NET
Private Function SetEnc() As SymmetricAlgorithm
If optDES.Checked Then
Return New DESCryptoServiceProvider
Else
If optTripleDES.Checked Then
Return New TripleDESCryptoServiceProvider
End If
End If
End Function

// C#
private SymmetricAlgorithm SetEnc()
{
if(optDES.Checked)
return new DESCryptoServiceProvider();
else
return new TripleDESCryptoServiceProvider();
}
如您所見,根據您在該窗體上選擇的選項按鈕,將創建 DESCryptoServiceProvider 對像或 TripleDESCryptoServiceProvider 對象。

實現加密和解密的密鑰
要使用對稱算法,必須提供要使用的密鑰。每個 CryptoSymmetricAlgorithm 實現都提供一種 GenerateKey 方法。它們實際上使用的是公共語言運行時 (CLR) 類中內置的隨機數生成器類。我們來看一下 Gen Key(生成密鑰)按鈕的 Click 事件處理程序,看它如何生成要使用的隨機密鑰值。

' Visual Basic .NET
Private Sub btnKeyGen_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles btnKeyGen.Click
mCSP = SetEnc()

mCSP.GenerateKey()

txtKey.Text = Convert.ToBase64String(mCSP.Key)
End Sub
// C#
private void btnKeyGen_Click(object sender,
System.EventArgs e)
{
mCSP = SetEnc();

mCSP.GenerateKey();

txtKey.Text = Convert.ToBase64String(mCSP.Key);
}
獲取服務提供程序的特定實現後,只需調用 GenerateKey 方法來創建一個用於加密的新的隨機密鑰。密鑰的大小取決於用來加密的特定提供程序。例如,DES 密鑰的大小為 64 位,而 TripleDES 密鑰的大小為 192 位。每個 SymmetricAlgorithm 類上都有一個 KeySize 屬性,它將返回用於生成密鑰的密鑰大小。

我們還需要生成初始化向量 (IV)。IV 將幫助算法生成最終加密字符串的數據塊。IV 用於開始第一個塊的加密。如果不提供 IV,那麼只要密鑰相同,在字符串之間傳遞的通用數據將保持同一種模式。因此,需要使用 IV 作為加密數據的「隨機」組件。通過這種方式,只要使用的 IV 不同,即使密鑰相同,同一個數據也會被加密成完全不同的值。下面是生成新的 IV 的 Gen IV(生成 IV)按鈕的源代碼。

' Visual Basic .NET
Private Sub btnIVGen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnIVGen.Click
mCSP.GenerateIV()

txtIV.Text = Convert.ToBase64String(mCSP.IV)
End Sub

// C#
private void btnIVGen_Click(object sender,
System.EventArgs e)
{
mCSP.GenerateIV();

txtIV.Text = Convert.ToBase64String(mCSP.IV);
}
此代碼看起來與生成密鑰的代碼非常相似。每個加密服務提供程序類上都有一個 GenerateIV() 方法。如果未提供 IV,該方法將生成一個 IV。

加密數據
獲得密鑰和初始化向量後,現在可以使用 Key、IV 和 Original String 值來創建原始字符串值的加密版本。單擊 Encrypt(加密)按鈕將運行以下代碼。

' Visual Basic .NET
Private Sub btnEncrypt_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cmdEncrypt.Click
txtEncrypted.Text = EncryptString(txtOriginal.Text)
End Sub

// C#
private void cmdEncrypt_Click(object sender, System.EventArgs e)
{
txtEncrypted.Text = EncryptString(txtOriginal.Text);
}
Click 事件過程將調用名為 EncryptString() 的方法,從 Original String(原始字符串)文本框中接受值並對其進行加密。然後返回該值並將其放到 Encrypted String(加密字符串)文本框中。下面是 EncryptString() 方法的代碼。

' Visual Basic .NET
Private Function EncryptString(ByVal Value As String) _
As String
Dim ct As ICryptoTransform
Dim ms As MemoryStream
Dim cs As CryptoStream
Dim byt() As Byte

ct = mCSP.CreateEncryptor(mCSP.Key, mCSP.IV)

byt = Encoding.UTF8.GetBytes(Value)

ms = New MemoryStream
cs = New CryptoStream(ms, ct, CryptoStreamMode.Write)
cs.Write(byt, 0, byt.Length)
cs.FlushFinalBlock()

cs.Close()

Return Convert.ToBase64String(ms.ToArray())
End Function

// C#
private string EncryptString(string Value)
{
ICryptoTransform ct;
MemoryStream ms;
CryptoStream cs;
byte[] byt;

ct = mCSP.CreateEncryptor(mCSP.Key, mCSP.IV);

byt = Encoding.UTF8.GetBytes(Value);

ms = new MemoryStream();
cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
cs.Write(byt, 0, byt.Length);
cs.FlushFinalBlock();

cs.Close();

return Convert.ToBase64String(ms.ToArray());
}
現在我們分開看一下各行代碼並瞭解這些代碼的作用。首先是加密進程的幾個變量。

Dim ct As ICryptoTransform
Dim ms As MemoryStream
Dim cs As CryptoStream
Dim byt() As Byte
ICryptoTransform 是一個接口。需要此接口才能在任何服務提供程序上調用 CreateEncryptor 方法,服務提供程序將返回定義該接口的實際 encryptor 對象。

然後需要將原始字符串轉換成字節數組。大多數 .NET 加密算法處理的是字節數組而不是字符串。

byt = Encoding.UTF8.GetBytes(Value)
現在可以執行實際的加密了。此進程需要創建一個數據流,用於將加密的字節寫入到其中。要使用名為 ms 的 MemoryStream 對像、ICryptoTransform 對像(提供給 CryptoStream 類的構造函數)以及說明您希望在何種模式(讀、寫等)下創建該類的枚舉常數。創建 CryptoStream 對像 cs 後,現在使用 CryptoStream 對象的 Write 方法將數據寫入到內存數據流。這就是進行實際加密的方法,加密每個數據塊時,數據將被寫入 MemoryStream 對象。

ms = New MemoryStream
cs = New CryptoStream(ms, ct, CryptoStreamMode.Write)
cs.Write(byt, 0, byt.Length)
cs.FlushFinalBlock()

cs.Close()
創建 MemoryStream 後,該代碼將在 CryptoStream 對像上執行 FlushFinalBlock 方法,以確保所有數據均被寫入 MemoryStream 對象。該過程將關閉 CryptoStream 對象。

最後,該過程將內存數據流從字節數組轉換回字符串,這樣才可以在窗體上的文本框內顯示該字符串。可以使用 MemoryStream ToArray() 方法從數據流中獲取字節數組,然後調用 Convert.ToBase64String() 方法,該方法接受字節數組輸入並使用 Base64 編碼方法將該字符串編碼為可讀內容。

解密數據
加密數據後,有時還需要解密數據。解密數據的過程非常簡單,與加密過程相似。您需要提供加密過程中使用的密鑰和初始化向量。SymmetricAlgorithm 類的 Key 和 IV 屬性被定義為字節數組。因此,設置這些屬性之前需要提供您創建的字符串並將其轉換成字節數組。下面我們看一下窗體內用於解密字符串的 DecryptString 方法。該方法是從窗體上 Decrypt(解密)按鈕的 Click 事件處理程序中調用的。

' Visual Basic .NET
Private Function DecryptString(ByVal Value As String) _
As String
Dim ct As ICryptoTransform
Dim ms As MemoryStream
Dim cs As CryptoStream
Dim byt() As Byte

ct = mCSP.CreateDecryptor(mCSP.Key, mCSP.IV)

byt = Convert.FromBase64String(Value)

ms = New MemoryStream
cs = New CryptoStream(ms, ct, CryptoStreamMode.Write)
cs.Write(byt, 0, byt.Length)
cs.FlushFinalBlock()

cs.Close()

Return Encoding.UTF8.GetString(ms.ToArray())
End Function

// C#
private string DecryptString(string Value)
{
ICryptoTransform ct;
MemoryStream ms;
CryptoStream cs;
byte[] byt;

ct = mCSP.CreateDecryptor(mCSP.Key, mCSP.IV);

byt = Convert.FromBase64String(Value);

ms = new MemoryStream();
cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
cs.Write(byt, 0, byt.Length);
cs.FlushFinalBlock();

cs.Close();

return Encoding.UTF8.GetString(ms.ToArray());
}
Encrypt 函數和 Decrypt 函數只有三個不同之處。

需要使用 CryptoServiceProvider 類的 CreateDecryptor 方法來創建相應的 ICtryptoTransform 對象。
需要將 Base64 編碼字符串轉換成字節數組。需要使用 Convert.FromBase64String 方法來實現此轉換。
通過對原始字節數組進行轉換,將字節數組轉換成相應的內存數據流。需要將內存數據流從字節數組轉換回可以在窗體上再次顯示的普通字符串。需要使用 Encoding.UTF8.GetString() 方法來實現此轉換。
注意:Encoding.UTF8 類來自於 System.Text 命名空間。
是否可以使其更簡單?!
儘管到目前為止顯示的代碼並不難,但有很多不同的類和接口您可能還不習慣於使用。此外,還要記住很多代碼。下面我們學習如何將 Cryptography 類包裝成易於使用的類。

名為 PDSACryptography 的程序集中有兩個類,分別為 PDSAHash 和 PDSAEncryption。這兩個類用於封裝創建散列字符串或加密字符串的實際機制。此外,它們還允許您使用枚舉常數來決定要使用哪種散列或加密算法。不必記住每個不同的加密服務提供程序的所有不同的名稱,即可獲得不錯的 Intellisense® 提供程序列表。

使用 PDSAHash 包裝散列
PDSAHash 類包含屬性 HashType、HashObject、OriginalString、HashString、SaltValue、UseSalt 和 SaltLength。與該類相關的方法包括 SetEncryptor、CreateSalt、Reset 和 CreateHash。該類中創建了一個稱為 PDSAHashType 的枚舉,您可以從中選擇要使用的相應散列類。該類的作用是將上文所示的代碼簡化為以下代碼。

Private Sub UsePDSAHash()
Dim ph As New PDSAHash(PDSAHash.PDSAHashType.MD5)

MessageBox.Show(ph.CreateHash("Paul"))
End Sub
我寧願鍵入以上代碼也不願意記住上文顯示的所有代碼。如您所見,這段代碼相當簡單,與上文所述的代碼完全相同,只是被包裝到一個易於使用的接口中。您可以從本文包含的示例中找到完整的類。下面列舉了可以使用該類創建的不同散列算法。

Public Enum PDSAHashType As Byte
MD5
SHA1
SHA256
SHA384
SHA512
End Enum
其中的每個算法都為最終的散列提供了一個不同的安全級別。.NET 中完整的散列類列表如下所示:

MD5CryptoServiceProvider
SHA1CryptoServiceProvider
SHA256Managed
SHA384Managed
SHA512Managed
有關這些不同的散列類型的詳細信息,請參閱 Visual Studio .NET 聯機文檔。

使用 PDSAEncryption 包裝加密
就像 PDSAHash 類可以包裝 .NET Framework 中的所有散列功能一樣,PDSAEncryption 類可用於包裝 .NET Framework 中的各種對稱算法類。PDSAEncryption 類包括以下枚舉類型,允許您創建各種加密/解密對象。

Public Enum PDSAEncryptionType As Byte
DES
RC2
Rijndael
TripleDES
End Enum
同樣,每個服務提供程序都為加密字符串提供了不同的安全級別。這在 Visual Studio .NET 聯機文檔中都有詳細的介紹,這裡不再贅述。

該類包含屬性 EncryptionType、OriginalString、EncryptedString、Key、KeyString、IV、IVString 和 CryptoProvider。其中的大多數屬性都是不言自明的,但 Key 和 IV 屬於字節數組,而 KeyString 和 IVString 是這些字節數組的字符串表示。

該類還包含一些方法。例如 Encrypt 和 Decrypt。還有 GenerateKey 和 GenerateIV 方法,如果沒有現成的密鑰和 IV,可以使用這兩個方法創建一個密鑰和 IV。另外還有 SetEncryptor 方法,它用於創建將在各種方法中使用的新的 CryptoProvider 對象。

該類的作用是使加密和解密字符串更容易實現。例如,下面的代碼片斷顯示了使用該類加密字符串是多麼容易。

Private Sub btnHardCoded_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnHardCoded.Click
Dim ps As New PDSASymmetric( _
PDSASymmetric.PDSAEncryptionType.TripleDES)

MessageBox.Show(ps.Encrypt( _
"Server=localhost;Database=Northwind;uid=sa;pwd=sa"))
End Sub
小結
使用 Microsoft .NET Framework 中的類可以很容易地在計算機中保存機密信息。您會發現 Cryptography 命名空間中的多個類都可以很好完成這一任務。為這些類創建包裝可以幫助您大大減少需要編寫的代碼量。強烈建議您按照本文所述,創建類似的包裝。

相關文章: