前面介紹了如何在.NET中使用RSA算法進(jìn)行數據加密和簽名,很多時(shí)候,我們需要重復的使用一組密鑰,因此就需要將這組密鑰保存起來(lái)。接下來(lái),我給大家介紹3種在.Net中保存密鑰的方法。
首先要強調的是,出于安全性考慮,不建議使用這種方法保存私鑰,如果使用,請在密鑰導出的時(shí)候只導出公鑰。
RSACryptoServiceProvider對象提供了一個(gè)ToXmlString(bool includePrivateParameters)方法,我們可以使用此方法將密鑰導出為一個(gè)xml格式的string,然后將其保存到一個(gè)文件中,這個(gè)方法的參數為true時(shí)會(huì )導出私鑰,否則不導出私鑰。需要的時(shí)候,我們可以使用FromXmlString(string xmlString)方法,將保存的密鑰信息加載到RSACryptoServiceProvider對象中。下面的代碼實(shí)現了導出和導入操作:
static void SaveKey2File(RSACryptoServiceProvider rsa, string fileName)
{
FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
string key = rsa.ToXmlString(false);
fs.Write(Encoding.UTF8.GetBytes(key), 0, key.Length);
fs.Close();
fs.Dispose();
}
static void LoadKeyFromFile(RSACryptoServiceProvider rsa, string fileName)
{
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, (int)fs.Length);
fs.Close();
fs.Dispose();
rsa.FromXmlString(Encoding.UTF8.GetString(data));
}
實(shí)際的工作中,出于安全性考慮,很少會(huì )用的上面的方法保存密鑰,但如果你想看看密鑰長(cháng)什么樣子,這個(gè)方法還是挺有用的~~
什么是密鑰容器(key container)呢?Window系統提供兩種密鑰庫(key store)用來(lái)保存密鑰(User Key Store和Machine Key Store),而密鑰容器就是用來(lái)保存密鑰的一個(gè)單位,每個(gè)密鑰容器都包含了一組密鑰對(公鑰和私鑰)和一些其它的信息,例如是否允許導出密鑰,密鑰的種類(lèi)(Exchange或Signatrue)等等,我們可以通過(guò)密鑰容器的名稱(chēng)來(lái)訪(fǎng)問(wèn)它們。
使用CspParameters對象創(chuàng )建或使用密鑰容器:
//實(shí)例化CspParameters對象
CspParameters cspPara = new CspParameters();
//指定CspParameters對象實(shí)例的名稱(chēng)
cspPara.KeyContainerName = "key_container_test";
//設置密鑰類(lèi)型為Exchange
cspPara.KeyNumber = 1;
//設置密鑰容器保存到計算機密鑰庫(默認為用戶(hù)密鑰庫)
cspPara.Flags = CspProviderFlags.UseMachineKeyStore;
//實(shí)例化RSA對象的時(shí)候,將CspParameters對象作為構造函數的參數傳遞給RSA對象,
//如果名稱(chēng)為key_container_test的密鑰容器不存在,RSA對象會(huì )創(chuàng )建這個(gè)密鑰容器;
//如果名稱(chēng)為key_container_test的密鑰容器已經(jīng)存在,RSA對象會(huì )使用這個(gè)密鑰容
//器中的密鑰進(jìn)行實(shí)例化
RSACryptoServiceProvider rsaPro = new RSACryptoServiceProvider(cspPara);
刪除密鑰容器:當我們不再需要某個(gè)密鑰容器的時(shí)候,可以使用下面的方法進(jìn)行刪除。
CspParameters cspPara = new CspParameters();
cspPara.KeyContainerName = "key_container_test";
cspPara.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider rsaPro = new RSACryptoServiceProvider(cspPara);
//不在密鑰庫中保存此密鑰容器
rsaPro.PersistKeyInCsp = false;
//釋放rsaPro占用的所有資源,包括密鑰容器。
rsaPro.Clear();
除非知道密鑰容器的名稱(chēng),否則無(wú)法從密鑰庫中提取到這個(gè)密鑰容器,所以在本機使用的密鑰(尤其是私鑰)保存在密鑰容器中是比較安全的做法。
注:實(shí)際當我們實(shí)例化一個(gè)RSACryptoServiceProvider 對象的時(shí)候,如果不指定具體的CspParameters 對象,RSACryptoServiceProvider 對象會(huì )生成一個(gè)臨時(shí)的密鑰容器,并且在RSACryptoServiceProvider 對象銷(xiāo)毀的時(shí)候自動(dòng)刪除這個(gè)臨時(shí)的密鑰容器。
如果你的密鑰需要在不同的機器上使用,那么將密鑰保存在數字證書(shū)中是一個(gè)不錯的選擇。實(shí)際上,說(shuō)將密鑰保存在數字證書(shū)中并不準確,應該是先生成一個(gè)數字證書(shū),然后在使用數字證書(shū)中的密鑰。
如何生成一個(gè)數字證書(shū)呢?正式的數字證書(shū)需要到CA去申請,當然還要奉上一筆銀子。還好我們可以使用.Net SDK提供的MakeCert.exe來(lái)生成臨時(shí)的數字證書(shū)。具體如何生成請訪(fǎng)問(wèn)證書(shū)創(chuàng )建工具。
.Net中用來(lái)訪(fǎng)問(wèn)證書(shū)的對象是X509Certificate2,我們可以用它來(lái)加載一個(gè)數字證書(shū)并獲得數字證書(shū)中的密鑰。
如果證書(shū)是以文件的形式保存在本地的話(huà),可以用下面的方法加載:
static byte[] EncryptDataByCert(byte[] data)
{
//實(shí)例化一個(gè)X509Certificate2對象,并加載證書(shū)testCertificate.cer
X509Certificate2 cert = new X509Certificate2(@"c:\testCertificate.cer");
//將證書(shū)的公鑰強制轉換成一個(gè)RSACryptoServiceProvider對象,然后可以使用這個(gè)對象執行加密操作
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] enData = rsa.Encrypt(data, false);
return enData;
}
一般情況下,對于數字證書(shū)來(lái)說(shuō),保存公鑰的證書(shū)使用.cer做擴展名,而保存了私鑰的證書(shū)會(huì )使用.pfx做擴展名,當我們加載一個(gè)私鑰的數字證書(shū)時(shí),需要提供私鑰的保護密碼,代碼如下:
static string DecryptByCert(byte[] endata)
{
//實(shí)例化一個(gè)X509Certificate2對象,并加載證書(shū)testCertificate.pfx。
//由于證書(shū)testCertificate.pfx包含私鑰,所以需要提供私鑰的保護密碼(第二個(gè)參數)
X509Certificate2 cert = new X509Certificate2(@"c:\testCertificate.pfx", "123456");
//將證書(shū)testCertificate.pfx的私鑰強制轉換為一個(gè)RSACryptoServiceProvider對象,用于解密操作
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
byte[] data = rsa.Decrypt(endata, false);
return data;
}
如果證書(shū)保存在計算機的證書(shū)存儲區(Certificate Store)中,我們就需要使用另一個(gè)對象X509Store來(lái)訪(fǎng)問(wèn)證書(shū)存儲區。根據訪(fǎng)問(wèn)權限,證書(shū)存儲區分為當前用戶(hù)(Current User)和本地計算機(Local Machine)兩個(gè)部分,前者用來(lái)保存當前登錄的用戶(hù)所能使用的數字證書(shū),而后者用來(lái)保存登錄到本機所能使用的數字證書(shū)。不管是當前用戶(hù)還是本地計算機,都包含多個(gè)邏輯存儲區,它們通過(guò)不同的名稱(chēng)來(lái)區分,每個(gè)邏輯存儲區可以保存多個(gè)數字證書(shū)。更詳細的介紹,可以參考
證書(shū)。具體的訪(fǎng)問(wèn)證書(shū)存儲區的代碼如下:
private X509Certificate2 GetCertificate(string CertName)
{
//聲明X509Store對象,指定存儲區的名稱(chēng)和存儲區的類(lèi)型
//StoreName中定義了系統默認的一些存儲區的邏輯名稱(chēng)
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
//以只讀的方式打開(kāi)這個(gè)存儲區,OpenFlags定義的打開(kāi)的方式
store.Open(OpenFlags.ReadOnly);
//獲取這個(gè)存儲區中的數字證書(shū)的集合
X509Certificate2Collection certCol = store.Certificates;
//查找滿(mǎn)足證書(shū)名稱(chēng)的證書(shū)并返回
foreach (X509Certificate2 cert in certCol)
{
if (cert.SubjectName.Name == "CN=" + CertName)
{
store.Close();
return cert;
}
}
store.Close();
return null;
}
我們也可以通過(guò)X509Certificate2Collection 對象在當前存儲區中添加刪除證書(shū),詳細的信息可以參考證書(shū)存儲區。
上面的介紹是我自己對密鑰保存的一些理解,大家可以根據的具體情況,去選擇具體的方法,希望對大家有所幫助。如果哪位大神有更好的方法,希望留下你的方法共我們學(xué)習。