创建加密应用程序
此演練提供用于對內容進行加密和解密的代碼示例。這些代碼示例是專為 Windows 窗體應用程序設計的。此應用程序并不演示實際方案(如使用智能卡),而是演示加密和解密的基礎。
此演練使用下列加密準則:
-
使用?RijndaelManaged?類(一種對稱算法)并利用它自動生成的?Key?和?IV?對數據進行加密和解密。
-
使用?RSACryptoServiceProvider(一種不對稱算法)對?RijndaelManaged?加密的數據的密鑰進行加密和解密。不對稱算法最適用于少量數據,如密鑰。
注意 如果您的目的是保護計算機上的數據,而不是與其他人交換加密內容,可考慮使用?ProtectedData?或ProtectedMemory?類。
下表對本主題中的加密任務進行了總結。
| 創建 Windows 窗體應用程序 | 列出運行應用程序所需的控件。 |
| 聲明全局對象 | 聲明字符串路徑變量、CspParameters?和RSACryptoServiceProvider?以獲得 Form 類的全局上下文。 |
| 創建不對稱密鑰 | 創建一個不對稱的公鑰/私鑰值對,并為其指定一個密鑰容器名稱。 |
| 加密文件 | 顯示一個對話框以選擇要加密的文件并對該文件進行加密。 |
| 解密文件 | 顯示一個對話框以選擇要解密的已加密文件并對該文件進行解密。 |
| 獲取私鑰 | 獲取使用密鑰容器名的完全密鑰對。 |
| 導出公鑰 | 將只帶有公共參數的密鑰保存到一個 XML 文件中。 |
| 導入公鑰 | 將密鑰從一個 XML 文件加載到密鑰容器中。 |
| 測試應用程序 | 列出用于測試此應用程序的過程。 |
此應用程序需要引用下面的命名空間:
-
System.IO
-
System.Security.Cryptography
大多數代碼示例都被設計為按鈕控件的事件處理程序。下表列出了示例應用程序所需的控件以及這些控件為與代碼示例匹配所需的名稱:
| Button | buttonEncryptFile | 加密文件 |
| Button | buttonDecryptFile | 解密文件 |
| Button | buttonCreateAsmKeys | 創建密鑰 |
| Button | buttonExportPublicKey | 導出公鑰 |
| Button | buttonImportPublicKey | 導入公鑰 |
| Button | buttonGetPrivateKey | 獲取私鑰 |
| Label | label1 | ? |
| OpenFileDialog | openFileDialog1 | ? |
| OpenFileDialog | openFileDialog2 | ? |
雙擊 Visual Studio 設計器中的按鈕以創建按鈕的事件處理程序。
聲明全局對象將下列代碼示例添加到窗體的構造函數中。編輯表示您的環境和首選項的字符串變量。
C# VB // Declare CspParmeters and RsaCryptoServiceProvider // objects with global scope of your Form class. CspParameters cspp = new CspParameters(); RSACryptoServiceProvider rsa;// Path variables for source, encryption, and // decryption folders. Must end with a backslash. const string EncrFolder = @"c:\Encrypt\"; const string DecrFolder = @"c:\Decrypt\"; const string SrcFolder = @"c:\docs\";// Public key file const string PubKeyFile = @"c:\encrypt\rsaPublicKey.txt";// Key container name for // private/public key value pair. const string keyName = "Key01"; 創建不對稱密鑰此任務將創建用于對?RijndaelManaged?密鑰(用于加密內容)進行加密和解密的不對稱密鑰,并在標簽控件上顯示密鑰容器名稱。
將下列代碼示例添加為“創建密鑰”按鈕的單擊事件處理程序:buttonCreateAsmKeys_Click。
C# VB private void buttonCreateAsmKeys_Click(object sender, System.EventArgs e) {// Stores a key pair in the key container.cspp.KeyContainerName = keyName;rsa = new RSACryptoServiceProvider(cspp);rsa.PersistKeyInCsp = true;if (rsa.PublicOnly == true)label1.Text = "Key: " + cspp.KeyContainerName + " - Public Only";elselabel1.Text = "Key: " + cspp.KeyContainerName + " - Full Key Pair";} 加密文件此任務涉及兩個方法:“加密文件”按鈕的事件處理程序方法?buttonEncryptFile_Click?以及?EncryptFile?方法。第一個方法將顯示一個對話框以便選擇某個文件并將相應的文件名傳遞給第二個方法,后者將執行加密操作。
將加密的內容、密鑰和 IV 全部保存到一個稱為加密包的?FileStream?中。
EncryptFile?方法執行下列操作:
創建?RijndaelManaged?對稱算法以對內容進行加密。
創建一個?RSACryptoServiceProvider?實例以對?RijndaelManaged?密鑰進行加密。
使用?CryptoStream?將源文件的?FileStream?讀取到加密文件的目標?FileStream?中并進行加密(以字節塊為單位)。
確定加密密鑰和 IV 的長度并創建其長度值的字節數組。
將密鑰、IV 及其長度值寫入到加密包中。
加密包的格式如下:
-
密鑰長度,字節 0 - 3
-
IV 長度,字節 4 - 7
-
加密密鑰
-
IV
-
密碼文本
通過獲得密鑰和 IV 的長度,可以確定加密包的所有部分的起始點和長度以便對文件進行加密。
將下列代碼示例添加為“加密文件”按鈕的單擊事件處理程序:buttonEncryptFile_Click。
C# VB private void buttonEncryptFile_Click(object sender, System.EventArgs e) {if (rsa == null)MessageBox.Show("Key not set.");else{// Display a dialog box to select a file to encrypt.openFileDialog1.InitialDirectory = SrcFolder;if (openFileDialog1.ShowDialog() == DialogResult.OK){string fName = openFileDialog1.FileName;if (fName != null){FileInfo fInfo = new FileInfo(fName);// Pass the file name without the path.string name = fInfo.Name;EncryptFile(name);}}} }向窗體中添加以下?EncryptFile?方法。
C# VB private void EncryptFile(string inFile) {// Create instance of Rijndael for// symetric encryption of the data.RijndaelManaged rjndl = new RijndaelManaged();rjndl.KeySize = 256;rjndl.BlockSize = 256;rjndl.Mode = CipherMode.CBC;ICryptoTransform transform = rjndl.CreateEncryptor();// Use RSACryptoServiceProvider to// enrypt the Rijndael key.byte[] keyEncrypted = rsa.Encrypt(rjndl.Key, false);// Create byte arrays to contain// the length values of the key and IV.byte[] LenK = new byte[4];byte[] LenIV = new byte[4];int lKey = keyEncrypted.Length;LenK = BitConverter.GetBytes(lKey);int lIV = rjndl.IV.Length;LenIV = BitConverter.GetBytes(lIV);// Write the following to the FileStream// for the encrypted file (outFs):// - length of the key// - length of the IV// - ecrypted key// - the IV// - the encrypted cipher content// Change the file's extension to ".enc"string outFile = EncrFolder + inFile.Substring(0, inFile.LastIndexOf(".")) + ".enc";using (FileStream outFs = new FileStream(outFile, FileMode.Create)){outFs.Write(LenK, 0, 4);outFs.Write(LenIV, 0, 4);outFs.Write(keyEncrypted, 0, lKey);outFs.Write(rjndl.IV, 0, lIV);// Now write the cipher text using// a CryptoStream for encrypting.using (CryptoStream outStreamEncrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write)){// By encrypting a chunk at// a time, you can save memory// and accommodate large files.int count = 0;int offset = 0;// blockSizeBytes can be any arbitrary size.int blockSizeBytes = rjndl.BlockSize / 8;byte[] data = new byte[blockSizeBytes];int bytesRead = 0;using (FileStream inFs = new FileStream(inFile, FileMode.Open)){do{count = inFs.Read(data, 0, blockSizeBytes);offset += count;outStreamEncrypted.Write(data, 0, count);bytesRead += blockSizeBytes;}while (count > 0);inFs.Close();}outStreamEncrypted.FlushFinalBlock();outStreamEncrypted.Close();}outFs.Close();}} 解密文件此任務涉及兩個方法:“解密文件”按鈕的事件處理程序方法?buttonEncryptFile_Click?以及?DecryptFile?方法。第一個方法將顯示一個對話框以便選擇某個文件并將相應的文件名傳遞給第二個方法,后者將執行解密操作。
Decrypt?方法執行下列操作:
創建?RijndaelManaged?對稱算法以對內容進行解密。
將加密包的?FileStream?的前 8 個字節讀取到字節數組中,以獲取加密密鑰和 IV 的長度。
從加密包中將密鑰和 IV 提取到字節數組中。
創建一個?RSACryptoServiceProvider?實例以對?RijndaelManaged?密鑰進行解密。
使用?CryptoStream?以將?FileStream?加密包的密碼文本部分讀取到解密文件的?FileStream?中并進行解密(以字節塊為單位)。完成此操作后,解密即告完成。
將下列代碼示例添加為“解密文件”按鈕的單擊事件處理程序。
C# VB private void buttonDecryptFile_Click(object sender, EventArgs e) {if (rsa == null)MessageBox.Show("Key not set.");else{// Display a dialog box to select the encrypted file.openFileDialog2.InitialDirectory = EncrFolder;if (openFileDialog2.ShowDialog() == DialogResult.OK){string fName = openFileDialog2.FileName;if (fName != null){FileInfo fi = new FileInfo(fName);string name = fi.Name;DecryptFile(name);}}} }向窗體中添加以下?DecryptFile?方法。
C# VB private void DecryptFile(string inFile) {// Create instance of Rijndael for// symetric decryption of the data.RijndaelManaged rjndl = new RijndaelManaged();rjndl.KeySize = 256;rjndl.BlockSize = 256;rjndl.Mode = CipherMode.CBC;rjndl.Padding = PaddingMode.None;// Create byte arrays to get the length of// the encrypted key and IV.// These values were stored as 4 bytes each// at the beginning of the encrypted package.byte[] LenK = new byte[4];byte[] LenIV = new byte[4];// Consruct the file name for the decrypted file.string outFile = DecrFolder + inFile.Substring(0, inFile.LastIndexOf(".")) + ".txt";// Use FileStream objects to read the encrypted// file (inFs) and save the decrypted file (outFs).using (FileStream inFs = new FileStream(EncrFolder + inFile, FileMode.Open)){inFs.Seek(0, SeekOrigin.Begin);inFs.Seek(0, SeekOrigin.Begin);inFs.Read(LenK, 0, 3);inFs.Seek(4, SeekOrigin.Begin);inFs.Read(LenIV, 0, 3);// Convert the lengths to integer values.int lenK = BitConverter.ToInt32(LenK, 0);int lenIV = BitConverter.ToInt32(LenIV, 0);// Determine the start postition of// the ciphter text (startC)// and its length(lenC).int startC = lenK + lenIV + 8;int lenC = (int)inFs.Length - startC;// Create the byte arrays for// the encrypted Rijndael key,// the IV, and the cipher text.byte[] KeyEncrypted = new byte[lenK];byte[] IV = new byte[lenIV];// Extract the key and IV// starting from index 8// after the length values.inFs.Seek(8, SeekOrigin.Begin);inFs.Read(KeyEncrypted, 0, lenK);inFs.Seek(8 + lenK, SeekOrigin.Begin);inFs.Read(IV, 0, lenIV);// Use RSACryptoServiceProvider// to decrypt the Rijndael key.byte[] KeyDecrypted = rsa.Decrypt(KeyEncrypted, false);// Decrypt the key.ICryptoTransform transform = rjndl.CreateDecryptor(KeyDecrypted, IV);// Decrypt the cipher text from// from the FileSteam of the encrypted// file (inFs) into the FileStream// for the decrypted file (outFs).using (FileStream outFs = new FileStream(outFile, FileMode.Create)){int count = 0;int offset = 0;// blockSizeBytes can be any arbitrary size.int blockSizeBytes = rjndl.BlockSize / 8;byte[] data = new byte[blockSizeBytes];// By decrypting a chunk a time,// you can save memory and// accommodate large files.// Start at the beginning// of the cipher text.inFs.Seek(startC, SeekOrigin.Begin);using (CryptoStream outStreamDecrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write)){do{count = inFs.Read(data, 0, blockSizeBytes);offset += count;outStreamDecrypted.Write(data, 0, count);}while (count > 0);outStreamDecrypted.FlushFinalBlock();outStreamDecrypted.Close();}outFs.Close();}inFs.Close();}} 導出公鑰此任務將“創建密鑰”按鈕創建的密鑰保存到僅導出公共參數的文件中。
此任務模擬了這樣一種情況:小紅將她的公鑰提供給小明以便后者能夠對發送給她的文件進行加密。小明和其他具有該公鑰的人員將不能對這些文件進行解密,因為他們不具備帶有私有參數的完全密鑰對。
將下列代碼示例添加為“創建密鑰”按鈕的單擊事件處理程序:buttonExportPublicKey_Click。
C# VB void buttonExportPublicKey_Click(object sender, System.EventArgs e) {// Save the public key created by the RSA// to a file. Caution, persisting the// key to a file is a security risk.StreamWriter sw = new StreamWriter(PubKeyFile);sw.Write(rsa.ToXmlString(false));sw.Close(); } 導入公鑰此任務將加載“導出公鑰”按鈕創建的密鑰(僅帶有公共參數)并將其設置為密鑰容器名稱。
此任務模擬這樣一種情況:小明將加載小紅提供的僅帶有公共參數的密鑰,以便他能夠對發送給小紅的文件進行加密。
將下列代碼示例添加為“導入公鑰”按鈕的單擊事件處理程序:buttonImportPublicKey_Click。
C# VB void buttonImportPublicKey_Click(object sender, System.EventArgs e) {StreamReader sr = new StreamReader(PubKeyFile);cspp.KeyContainerName = keyName;rsa = new RSACryptoServiceProvider(cspp);string keytxt = sr.ReadToEnd();rsa.FromXmlString(keytxt);rsa.PersistKeyInCsp = true;if (rsa.PublicOnly == true)label1.Text = "Key: " + cspp.KeyContainerName + " - Public Only";elselabel1.Text = "Key: " + cspp.KeyContainerName + " - Full Key Pair";sr.Close(); } 獲取私鑰此任務將密鑰容器名稱設置為使用“創建密鑰”按鈕創建的密鑰的名稱。密鑰容器將包含帶有私有參數的完全密鑰對。
此任務模擬這樣一種情況:小紅使用自己的私鑰對小明加密的文件進行解密。
將下列代碼示例添加為“獲取私鑰”按鈕的單擊事件處理程序:buttonGetPrivateKey_Click。
C# VB private void buttonGetPrivateKey_Click(object sender, EventArgs e) {cspp.KeyContainerName = keyName;rsa = new RSACryptoServiceProvider(cspp);rsa.PersistKeyInCsp = true;if (rsa.PublicOnly == true)label1.Text = "Key: " + cspp.KeyContainerName + " - Public Only";elselabel1.Text = "Key: " + cspp.KeyContainerName + " - Full Key Pair";} 測試應用程序生成應用程序之后,執行下列測試方案。
創建密鑰、加密和解密
單擊“創建密鑰”按鈕。標簽將顯示密鑰名稱,并顯示此密鑰是一個完全密鑰對。
單擊“導出公鑰”按鈕。請注意,導出公鑰參數不會更改當前密鑰。
單擊“加密文件”按鈕并選擇一個文件。
單擊“解密文件”按鈕并選擇剛剛加密的文件。
檢查剛剛解密的文件。
關閉并重新啟動應用程序以在下一個方案中對檢索保留的密鑰容器進行測試。
使用公鑰進行加密
單擊“導入公鑰”按鈕。標簽將顯示密鑰名稱,并顯示此密鑰僅是公用的。
單擊“加密文件”按鈕并選擇一個文件。
單擊“解密文件”按鈕并選擇剛剛加密的文件。此操作將失敗,因為您必須具有私鑰才能進行解密。
此方案演示了僅使用公鑰對發送給其他人的文件進行加密的情況。通常,此人將僅為您提供公鑰,而保留用于解密的私鑰。
使用私鑰進行解密
單擊“獲取私鑰”按鈕。標簽將顯示密鑰名稱,并顯示此密鑰是否為一個完全密鑰對。
單擊“解密文件”按鈕并選擇剛剛加密的文件。此操作將成功,因為您具有用于解密的完全密鑰對。
轉載于:https://www.cnblogs.com/amylis_chen/p/4789923.html
總結
- 上一篇: 【Python开发】Python 适合大
- 下一篇: Foxmail 绑定企业邮箱