.NET中加密與解密的方法
1.加密。
Response.Redirect("DetailInfo.aspx?id=" + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes
("sp10006")).Replace("+","%2B"));
2.解密。
string ID = System.Text.Encoding.Default.GetString(Convert.FromBase64String(Request.QueryString["id"].ToString().Replace
("%2B","+")));
.NET將原來獨立的API和SDK合并到一個架構中,這對於程式開發人員非常有利。它將CryptoAPI改編進.NET的System.Security.Cryptography
名字空間,使密碼服務擺脫了SDK平台的神秘性,變成了簡單的.NET名字空間的使用。由於隨著整個架構組件一起共用,密碼服務更容易實現了
,現在僅僅需要學習System.Security.Cryptography名字空間的功能和用於解決特定方案的類。
加密和解密的演算法
System.Security.Cryptography名字空間包含了實現安全方案的類,例如加密和解密資料、管理密鑰、驗證資料的完整性並確保資料沒有
被篡改等等。本文重點討論加密和解密。
加密和解密的演算法分為對稱(symmetric)演算法和不對稱(asymmetric)演算法。對稱演算法在加密和解密資料時使用相同的密鑰和初始化向量
,典型的有DES、 TripleDES和Rijndael演算法,它適用於不需要傳遞密鑰的情況,主要用於本地文檔或資料的加密。不對稱演算法有兩個不同的密
鑰,分別是公用密鑰和私人密鑰,公用密鑰在網路中傳遞,用於加密資料,而私人密鑰用於解密資料。不對稱演算法主要有RSA、DSA等,主要用
於網路資料的加密。
加密和解密本地文檔
下面的例子是加密和解密本地文本,使用的是Rijndael對稱演算法。
對稱演算法在資料流通過時對它進行加密。因此首先需要建立一個正常的流(例如I/O流)。文章使用FileStream類將文字檔讀入位元組數組
,也使用該類作為輸出機制。
接下來定義相應的物件變數。在定義SymmetricAlgorithm抽象類別的物件變數時我們可以指定任何一種對稱式加密演算法提供者。代碼使用的
是Rijndael演算法,但是很容易改為DES或者TripleDES演算法。.NET使用強大的隨機密鑰設定了提供者的執行個體,選擇自己的密鑰是比較危險的,
接受電腦產生的密鑰是一個更好的選擇,文中的代碼使用的是電腦產生的密鑰。
下一步,演算法執行個體提供了一個對象來執行實際資料轉送。每種演算法都有CreateEncryptor和CreateDecryptor兩個方法,它們返回實現
ICryptoTransform介面的對象。
最後,現在使用BinaryReader的ReadBytes方法讀取源檔案,它會返回一個位元組數組。BinaryReader讀取源檔案的輸入資料流,在作為
CryptoStream.Write方法的參數時調用ReadBytes方法。指定的CryptoStream執行個體被告知它應該操作的下層流,該對象將執行資料傳遞,無論流
的目的是讀或者寫。
下面是加密和解密一個文字檔的來源程式片斷:
namespace com.billdawson.crypto
{
class TextFileCrypt
{
public static void Main(string[] args)
{
string file = args[0];
string tempfile = Path.GetTempFileName();
//開啟指定的檔案
FileStream fsIn = File.Open(file,FileMode.Open,
FileAccess.Read);
FileStream fsOut = File.Open(tempfile, FileMode.Open,
FileAccess.Write);
//定義對稱演算法對象執行個體和介面
SymmetricAlgorithm symm = new RijndaelManaged();
ICryptoTransform transform = symm.CreateEncryptor();
CryptoStream cstream = new CryptoStream(fsOut,transform,
ryptoStreamMode.Write);
BinaryReader br = new BinaryReader(fsIn);
// 讀取源檔案到cryptostream
cstream.Write(br.ReadBytes((int)fsIn.Length),0,(int)fsIn.Length);
cstream.FlushFinalBlock();
cstream.Close();
fsIn.Close();
fsOut.Close();
Console.WriteLine("created encrypted file {0}", tempfile);
Console.WriteLine("will now decrypt and show contents");
// 反向操作--解密剛才加密的臨時檔案
fsIn = File.Open(tempfile,FileMode.Open,FileAccess.Read);
transform = symm.CreateDecryptor();
cstream = new CryptoStream(fsIn,transform,
CryptoStreamMode.Read);
StreamReader sr = new StreamReader(cstream);
Console.WriteLine("decrypted file text: " + sr.ReadToEnd());
fsIn.Close();
}
}
}
加密網路資料
如果我有一個只想自己看到的文檔,我不會簡單的通過e-mail發送給你。我將使用對稱演算法加密它;如果有人截取了它,他們也不能閱讀
該文檔,因為他們沒有用於加密的唯一密鑰。但是你也沒有密鑰。我需要使用某種方式將密鑰給你,這樣你才能解密文檔,但是不能冒密鑰和
文檔被截取的風險。
非對稱演算法就是一種解決方案。這類演算法使用的兩個密鑰有如下關係:使用公用祕密金鑰加密的資訊只能被相應的私人密鑰解密。因此,我首
要求你給我發送你的公用密鑰。在發送給我的途中可能有人會截取它,但是沒有關係,因為他們只能使用該密鑰給你的資訊加密。我使用你的
公用祕密金鑰加密文檔並發送給你
使用私人密鑰解密該文檔,這是唯一可以解密的密鑰,並且沒有通過網路傳遞。
不對稱演算法比對稱演算法計算的花費多、速度慢。因此我們不希望線上對話中使用不對稱演算法加密所有資訊。相反,我們使用對稱演算法。下
面的例子中我們使用不對稱式加密來加密對稱金鑰。接著就使用對稱演算法加密了。實際上安全介面層(SSL)建立伺服器和瀏覽器之間的安全對話
使用的就是這種工作方式。
樣本是一個TCP程式,分為伺服器端和用戶端。伺服器端的工作流程是:
從用戶端接收公用密鑰。
使用公用祕密金鑰加密未來使用的對稱金鑰。
將加密了的對稱金鑰發送給用戶端。
給用戶端發送使用該對稱金鑰密碼編譯的資訊。
代碼如下:
namespace com.billdawson.crypto
{
public class CryptoServer
{
private const int RSA_KEY_SIZE_BITS = 1024;
private const int RSA_KEY_SIZE_BYTES = 252;
private const int TDES_KEY_SIZE_BITS = 192;
public static void Main(string[] args)
{
int port;
string msg;
TcpListener listener;
TcpClient client;
SymmetricAlgorithm symm;
RSACryptoServiceProvider rsa;
//擷取連接埠
try
{
port = Int32.Parse(args[0]);
msg = args[1];
}
catch
{
Console.WriteLine(USAGE);
return;
}
//建立監聽
try
{
listener = new TcpListener(port);
listener.Start();
Console.WriteLine("Listening on port {0}...",port);
client = listener.AcceptTcpClient();
Console.WriteLine("connection....");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
return;
}
try
{
rsa = new RSACryptoServiceProvider();
rsa.KeySize = RSA_KEY_SIZE_BITS;
// 擷取用戶端公用密鑰
rsa.ImportParameters(getClientPublicKey(client));
symm = new TripleDESCryptoServiceProvider();
symm.KeySize = TDES_KEY_SIZE_BITS;
//使用用戶端的公用祕密金鑰加密對稱金鑰並發送給客。
encryptAndSendSymmetricKey(client, rsa, symm);
//使用對稱金鑰密碼編譯資訊並發送
encryptAndSendSecretMessage(client, symm, msg);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
finally
{
try
{
client.Close();
listener.Stop();
}
catch
{
//錯誤
}
Console.WriteLine("Server exiting...");
}
}
private static RSAParameters getClientPublicKey(TcpClient client)
{
// 從位元組流擷取序列化的公用密鑰,通過串並轉換寫入類的執行個體
byte[] buffer = new byte[RSA_KEY_SIZE_BYTES];
NetworkStream ns = client.GetStream();
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
RSAParameters result;
int len = 0;
int totalLen = 0;
byte[] inputByteArray = new byte[pToDecrypt.Length / 2];
for(int x = 0; x < pToDecrypt.Length / 2; x++)
{
int i = (Convert.ToInt32(pToDecrypt.Substring(x * 2, 2), 16));
inputByteArray[x] = (byte)i;
}
//建立加密對象的密鑰和位移量,此值重要,不能修改
des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(),CryptoStreamMode.Write);
//Flush the data through the crypto stream into the memory stream
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
//Get the decrypted data back from the memory stream
//建立StringBuild對象,CreateDecrypt使用的是流對象,必須把解密後的文本變成流對象
StringBuilder ret = new StringBuilder();
return System.Text.Encoding.Default.GetString(ms.ToArray());
}
2.2 範例
使用非對稱演算法加密訊息的四個主要步驟
1. 擷取寄件者的私密金鑰和接收者的公開金鑰。
2. 藉助隨機密鑰(random key)和初始化向量,用對稱演算法加密訊息。
3. 用接收者的公開金鑰為2步驟中的密鑰和初始化向量加密。
4. 用寄件者的私密金鑰對訊息進行數位簽章處理。
對應的解密的四個步驟
1. 擷取寄件者的公開金鑰和接收者的私密金鑰。
2. 驗證數位簽章。
3. 解密密鑰和初始化向量。
4. 使用解密後的密鑰和初始化向量解密訊息。
程式碼分析:
1. 擷取密鑰
...{
X509CertificateStore x509Store = null;
if (location == "CurrentUser")
...{
x509Store = X509CertificateStore.CurrentUserStore(X509CertificateStore.MyStore);
}
else
...{
x509Store = X509CertificateStore.LocalMachineStore(X509CertificateStore.MyStore);
}
bool open = x509Store.OpenRead();
X509Certificate sender_cert = null;
X509Certificate receiver_cert = null;
if (!open)
...{
throw new Exception("unable to open the certificate store");
}
sender_cert = x509Store.FindCertificateBySubjectName("CN=XinChen, E=none@none.com")[0];
receiver_cert = x509Store.FindCertificateBySubjectName("CN=Sherry, E=none@none.com")[0];
RSAParameters sender_privateKey = sender_cert.Key.ExportParameters(true);
RSAParameters receiver_publicKey = receiver_cert.PublicKey.ExportParameters(false);
}
2. 對稱演算法加密 (不指定初始密鑰和初始向量,則由系統自動產生)
...{
SymmetricAlgorithm symmProvider = SymmetricAlgorithm.Create("TripleDES");
encryptor = symmProvider.CreateEncryptor();
CryptoStream encStream = new CryptoStream(data, encryptor, CryptoStreamMode.Read);
MemoryStream encrypted = new MemoryStream();
byte[] buffer = new byte[1024];
int count = 0;
while ((count = encStream.Read(buffer,0,1024)) > 0)
...{
encrypted.Write(buffer,0,count);
}
}
3. 用接收者的公開金鑰為2步驟中的密鑰和初始化向量加密
...{
byte[] key;
byte[] iv;
RSACryptoServiceProvider asymmetricProvider = new RSACryptoServiceProvider();
asymmetricProvider.ImportParameters(receiver_publicKey);
key = asymmetricProvider.Encrypt(symmProvider.Key,false);
iv = asymmetricProvider.Encrypt(symmProvider.IV,false);
}
4. 建立數位簽章
使用密鑰為訊息散列進行加密
...{
byte[] signature;
asymmetricProvider.ImportParameters(sender_privateKey);
signature = asymmetricProvider.SignData(encrypted.ToArray(), new SHA1CryptoServiceProvider());
}
上面四個步驟的最後輸出為encrypted、key、iv和signature
解密的代碼示範:
1. 擷取密鑰
...
2. 驗證數位簽章
...{
asymmetricProvider.ImportParameters(sender_publicKey);
bool verify = asymmetricProvider.VerifyData(encrypted, new SHA1CryptoServiceProvider(), signature)
}
3. 解密密鑰和初始化向量
...{
asymmetricProvider.ImportParameters(receiver_privateKey);
byte[] decryptedKey = asymmetricProvider.Decrypt(key, false);
byte[] decryptediv = asymmetricProvider.Decrypt(iv, false);
}
4. 使用解密後的密鑰和初始化向量解密訊息。
...{
SymmetricAlgorithm symmProvider = SymmetricAlgorithm.Create("TripleDES");
ICryptoTransform decryptor = symmProvider.CreateDecryptor(decryptedKey, decryptediv);
CryptoStream decStream = new CryptoStream(encrypted, decryptor, CryptoStreamMode.Read);
}
3.Hash散列舉例
...{
System.Security.Cryptography.HashAlgorithm
System.Security.Cryptography.KeyedHashAlgorithm
System.Security.Cryptography.MD5
System.Security.Cryptography.SHA1
System.Security.Cryptography.SHA256
System.Security.Cryptography.SHA384
System.Security.Cryptography.SHA512
}
public static string Encrypt(string password)
...{
password = password.ToLower();
Byte[] clearBytes = new UnicodeEncoding().GetBytes(password);
Byte[] hashedBytes = ((HashAlgorithm) CryptoConfig.CreateFromName("MD5")).ComputeHash(clearBytes);
return BitConverter.ToString(hashedBytes);
}