.NET非对称加密RSA算法的密钥保存

.NET中非对称加密RSA算法的密钥保存

前面介绍了如何在.NET中使用RSA算法进行数据加密和签名,很多时候,我们需要重复的使用一组密钥,因此就需要将这组密钥保存起来。接下来,我给大家介绍3种在.Net中保存密钥的方法。

第一种方法:将密钥导出保存为本地文件。

首先要强调的是,出于安全性考虑,不建议使用这种方法保存私钥,如果使用,请在密钥导出的时候只导出公钥。

RSACryptoServiceProvider对象提供了一个ToXmlString(bool includePrivateParameters)方法,我们可以使用此方法将密钥导出为一个xml格式的string,然后将其保存到一个文件中,这个方法的参数为true时会导出私钥,否则不导出私钥。需要的时候,我们可以使用FromXmlString(string xmlString)方法,将保存的密钥信息加载到RSACryptoServiceProvider对象中。下面的代码实现了导出和导入操作:

 1: static void SaveKey2File(RSACryptoServiceProvider rsa, string fileName)
 2: {
 3:     FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
 4:     string key = rsa.ToXmlString(false);
 5:     fs.Write(Encoding.UTF8.GetBytes(key), 0, key.Length);
 6:     fs.Close();
 7:     fs.Dispose();
 8: }
 9:  
 10: static void LoadKeyFromFile(RSACryptoServiceProvider rsa, string fileName)
 11: {
 12:     FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
 13:     byte[] data = new byte[fs.Length];
 14:     fs.Read(data, 0, (int)fs.Length);
 15:     fs.Close();
 16:     fs.Dispose();
 17:     rsa.FromXmlString(Encoding.UTF8.GetString(data));
 18: }

实际的工作中,出于安全性考虑,很少会用的上面的方法保存密钥,但如果你想看看密钥长什么样子,这个方法还是挺有用的~~

第二种方法:将密钥保存到密钥容器。

什么是密钥容器(key container)呢?Window系统提供两种密钥库(key store)用来保存密钥(User Key Store和Machine Key Store),而密钥容器就是用来保存密钥的一个单位,每个密钥容器都包含了一组密钥对(公钥和私钥)和一些其它的信息,例如是否允许导出密钥,密钥的种类(Exchange或Signatrue)等等,我们可以通过密钥容器的名称来访问它们。

使用CspParameters对象创建或使用密钥容器:

 1: //实例化CspParameters对象
 2: CspParameters cspPara = new CspParameters();
 3: //指定CspParameters对象实例的名称
 4: cspPara.KeyContainerName = "key_container_test";
 5: //设置密钥类型为Exchange
 6: cspPara.KeyNumber = 1;
 7: //设置密钥容器保存到计算机密钥库(默认为用户密钥库)
 8: cspPara.Flags = CspProviderFlags.UseMachineKeyStore;
 9: //实例化RSA对象的时候,将CspParameters对象作为构造函数的参数传递给RSA对象,
 10: //如果名称为key_container_test的密钥容器不存在,RSA对象会创建这个密钥容器;
 11: //如果名称为key_container_test的密钥容器已经存在,RSA对象会使用这个密钥容器中的密钥进行实例化
 12: RSACryptoServiceProvider rsaPro = new RSACryptoServiceProvider(cspPara);
 13:  

删除密钥容器:当我们不再需要某个密钥容器的时候,可以使用下面的方法进行删除。

 1: CspParameters cspPara = new CspParameters();
 2: cspPara.KeyContainerName = "key_container_test";
 3: cspPara.Flags = CspProviderFlags.UseMachineKeyStore;
 4: RSACryptoServiceProvider rsaPro = new RSACryptoServiceProvider(cspPara);
 5: //不在密钥库中保存此密钥容器
 6: rsaPro.PersistKeyInCsp = false;
 7: //释放rsaPro占用的所有资源,包括密钥容器。
 8: rsaPro.Clear();

除非知道密钥容器的名称,否则无法从密钥库中提取到这个密钥容器,所以在本机使用的密钥(尤其是私钥)保存在密钥容器中是比较安全的做法。

注:实际当我们实例化一个RSACryptoServiceProvider 对象的时候,如果不指定具体的CspParameters 对象,RSACryptoServiceProvider 对象会生成一个临时的密钥容器,并且在RSACryptoServiceProvider 对象销毁的时候自动删除这个临时的密钥容器。

第三种方法:使用数字证书。

如果你的密钥需要在不同的机器上使用,那么将密钥保存在数字证书中是一个不错的选择。实际上,说将密钥保存在数字证书中并不准确,应该是先生成一个数字证书,然后在使用数字证书中的密钥。

如何生成一个数字证书呢?正式的数字证书需要到CA去申请,当然还要奉上一笔银子。还好我们可以使用.Net SDK提供的MakeCert.exe来生成临时的数字证书。具体如何生成请访问证书创建工具

.Net中用来访问证书的对象是X509Certificate2,我们可以用它来加载一个数字证书并获得数字证书中的密钥。

如果证书是以文件的形式保存在本地的话,可以用下面的方法加载:

 1: static byte[] EncryptDataByCert(byte[] data)
 2: { 
 3:     //实例化一个X509Certificate2对象,并加载证书testCertificate.cer
 4:     X509Certificate2 cert = new X509Certificate2(@"c:\testCertificate.cer");
 5:     //将证书的公钥强制转换成一个RSACryptoServiceProvider对象,然后可以使用这个对象执行加密操作
 6:     RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
 7:     byte[] enData = rsa.Encrypt(data, false);
 8:     return enData;
 9: }

一般情况下,对于数字证书来说,保存公钥的证书使用.cer做扩展名,而保存了私钥的证书会使用.pfx做扩展名,当我们加载一个私钥的数字证书时,需要提供私钥的保护密码,代码如下:

 1: static string DecryptByCert(byte[] endata)
 2: {
 3:     //实例化一个X509Certificate2对象,并加载证书testCertificate.pfx。
 4:     //由于证书testCertificate.pfx包含私钥,所以需要提供私钥的保护密码(第二个参数)
 5:     X509Certificate2 cert = new X509Certificate2(@"c:\testCertificate.pfx", "123456");
 6:     //将证书testCertificate.pfx的私钥强制转换为一个RSACryptoServiceProvider对象,用于解密操作
 7:     RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
 8:     byte[] data = rsa.Decrypt(endata, false);
 9:     return data;
 10: }

如果证书保存在计算机的证书存储区(Certificate Store)中,我们就需要使用另一个对象X509Store来访问证书存储区。根据访问权限,证书存储区分为当前用户(Current User)和本地计算机(Local Machine)两个部分,前者用来保存当前登录的用户所能使用的数字证书,而后者用来保存登录到本机所能使用的数字证书。不管是当前用户还是本地计算机,都包含多个逻辑存储区,它们通过不同的名称来区分,每个逻辑存储区可以保存多个数字证书。更详细的介绍,可以参考证书。具体的访问证书存储区的代码如下:

 1: private X509Certificate2 GetCertificate(string CertName)
 2: {
 3:     //声明X509Store对象,指定存储区的名称和存储区的类型
 4:     //StoreName中定义了系统默认的一些存储区的逻辑名称
 5:     X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
 6:     //以只读的方式打开这个存储区,OpenFlags定义的打开的方式
 7:     store.Open(OpenFlags.ReadOnly);
 8:     //获取这个存储区中的数字证书的集合
 9:     X509Certificate2Collection certCol = store.Certificates;
 10:     //查找满足证书名称的证书并返回
 11:     foreach (X509Certificate2 cert in certCol)
 12:     {
 13:         if (cert.SubjectName.Name == "CN="  + CertName)
 14:         {
 15:             store.Close();
 16:             return cert;
 17:         }
 18:     }
 19:     store.Close();
 20:     return null;
 21: }

我们也可以通过X509Certificate2Collection 对象在当前存储区中添加删除证书,详细的信息可以参考证书存储区

上面的介绍是我自己对密钥保存的一些理解,大家可以根据的具体情况,去选择具体的方法,希望对大家有所帮助。如果哪位大神有更好的方法,希望留下你的方法共我们学习。

,

前面介绍了如何在.NET中使用RSA算法进行数据加密和签名,很多时候,我们需要重复的使用一组密钥,因此就需要将这组密钥保存起来。接下来,我给大家介绍3种在.Net中保存密钥的方法。

第一种方法:将密钥导出保存为本地文件。

首先要强调的是,出于安全性考虑,不建议使用这种方法保存私钥,如果使用,请在密钥导出的时候只导出公钥。

RSACryptoServiceProvider对象提供了一个ToXmlString(bool includePrivateParameters)方法,我们可以使用此方法将密钥导出为一个xml格式的string,然后将其保存到一个文件中,这个方法的参数为true时会导出私钥,否则不导出私钥。需要的时候,我们可以使用FromXmlString(string xmlString)方法,将保存的密钥信息加载到RSACryptoServiceProvider对象中。下面的代码实现了导出和导入操作:

 1: static void SaveKey2File(RSACryptoServiceProvider rsa, string fileName)
 2: {
 3:     FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
 4:     string key = rsa.ToXmlString(false);
 5:     fs.Write(Encoding.UTF8.GetBytes(key), 0, key.Length);
 6:     fs.Close();
 7:     fs.Dispose();
 8: }
 9:  
 10: static void LoadKeyFromFile(RSACryptoServiceProvider rsa, string fileName)
 11: {
 12:     FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
 13:     byte[] data = new byte[fs.Length];
 14:     fs.Read(data, 0, (int)fs.Length);
 15:     fs.Close();
 16:     fs.Dispose();
 17:     rsa.FromXmlString(Encoding.UTF8.GetString(data));
 18: }

实际的工作中,出于安全性考虑,很少会用的上面的方法保存密钥,但如果你想看看密钥长什么样子,这个方法还是挺有用的~~

第二种方法:将密钥保存到密钥容器。

什么是密钥容器(key container)呢?Window系统提供两种密钥库(key store)用来保存密钥(User Key Store和Machine Key Store),而密钥容器就是用来保存密钥的一个单位,每个密钥容器都包含了一组密钥对(公钥和私钥)和一些其它的信息,例如是否允许导出密钥,密钥的种类(Exchange或Signatrue)等等,我们可以通过密钥容器的名称来访问它们。

使用CspParameters对象创建或使用密钥容器:

 1: //实例化CspParameters对象
 2: CspParameters cspPara = new CspParameters();
 3: //指定CspParameters对象实例的名称
 4: cspPara.KeyContainerName = "key_container_test";
 5: //设置密钥类型为Exchange
 6: cspPara.KeyNumber = 1;
 7: //设置密钥容器保存到计算机密钥库(默认为用户密钥库)
 8: cspPara.Flags = CspProviderFlags.UseMachineKeyStore;
 9: //实例化RSA对象的时候,将CspParameters对象作为构造函数的参数传递给RSA对象,
 10: //如果名称为key_container_test的密钥容器不存在,RSA对象会创建这个密钥容器;
 11: //如果名称为key_container_test的密钥容器已经存在,RSA对象会使用这个密钥容器中的密钥进行实例化
 12: RSACryptoServiceProvider rsaPro = new RSACryptoServiceProvider(cspPara);
 13:  

删除密钥容器:当我们不再需要某个密钥容器的时候,可以使用下面的方法进行删除。

 1: CspParameters cspPara = new CspParameters();
 2: cspPara.KeyContainerName = "key_container_test";
 3: cspPara.Flags = CspProviderFlags.UseMachineKeyStore;
 4: RSACryptoServiceProvider rsaPro = new RSACryptoServiceProvider(cspPara);
 5: //不在密钥库中保存此密钥容器
 6: rsaPro.PersistKeyInCsp = false;
 7: //释放rsaPro占用的所有资源,包括密钥容器。
 8: rsaPro.Clear();

除非知道密钥容器的名称,否则无法从密钥库中提取到这个密钥容器,所以在本机使用的密钥(尤其是私钥)保存在密钥容器中是比较安全的做法。

注:实际当我们实例化一个RSACryptoServiceProvider 对象的时候,如果不指定具体的CspParameters 对象,RSACryptoServiceProvider 对象会生成一个临时的密钥容器,并且在RSACryptoServiceProvider 对象销毁的时候自动删除这个临时的密钥容器。

第三种方法:使用数字证书。

如果你的密钥需要在不同的机器上使用,那么将密钥保存在数字证书中是一个不错的选择。实际上,说将密钥保存在数字证书中并不准确,应该是先生成一个数字证书,然后在使用数字证书中的密钥。

如何生成一个数字证书呢?正式的数字证书需要到CA去申请,当然还要奉上一笔银子。还好我们可以使用.Net SDK提供的MakeCert.exe来生成临时的数字证书。具体如何生成请访问证书创建工具

.Net中用来访问证书的对象是X509Certificate2,我们可以用它来加载一个数字证书并获得数字证书中的密钥。

如果证书是以文件的形式保存在本地的话,可以用下面的方法加载:

 1: static byte[] EncryptDataByCert(byte[] data)
 2: { 
 3:     //实例化一个X509Certificate2对象,并加载证书testCertificate.cer
 4:     X509Certificate2 cert = new X509Certificate2(@"c:\testCertificate.cer");
 5:     //将证书的公钥强制转换成一个RSACryptoServiceProvider对象,然后可以使用这个对象执行加密操作
 6:     RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PublicKey.Key;
 7:     byte[] enData = rsa.Encrypt(data, false);
 8:     return enData;
 9: }

一般情况下,对于数字证书来说,保存公钥的证书使用.cer做扩展名,而保存了私钥的证书会使用.pfx做扩展名,当我们加载一个私钥的数字证书时,需要提供私钥的保护密码,代码如下:

 1: static string DecryptByCert(byte[] endata)
 2: {
 3:     //实例化一个X509Certificate2对象,并加载证书testCertificate.pfx。
 4:     //由于证书testCertificate.pfx包含私钥,所以需要提供私钥的保护密码(第二个参数)
 5:     X509Certificate2 cert = new X509Certificate2(@"c:\testCertificate.pfx", "123456");
 6:     //将证书testCertificate.pfx的私钥强制转换为一个RSACryptoServiceProvider对象,用于解密操作
 7:     RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
 8:     byte[] data = rsa.Decrypt(endata, false);
 9:     return data;
 10: }

如果证书保存在计算机的证书存储区(Certificate Store)中,我们就需要使用另一个对象X509Store来访问证书存储区。根据访问权限,证书存储区分为当前用户(Current User)和本地计算机(Local Machine)两个部分,前者用来保存当前登录的用户所能使用的数字证书,而后者用来保存登录到本机所能使用的数字证书。不管是当前用户还是本地计算机,都包含多个逻辑存储区,它们通过不同的名称来区分,每个逻辑存储区可以保存多个数字证书。更详细的介绍,可以参考证书。具体的访问证书存储区的代码如下:

 1: private X509Certificate2 GetCertificate(string CertName)
 2: {
 3:     //声明X509Store对象,指定存储区的名称和存储区的类型
 4:     //StoreName中定义了系统默认的一些存储区的逻辑名称
 5:     X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
 6:     //以只读的方式打开这个存储区,OpenFlags定义的打开的方式
 7:     store.Open(OpenFlags.ReadOnly);
 8:     //获取这个存储区中的数字证书的集合
 9:     X509Certificate2Collection certCol = store.Certificates;
 10:     //查找满足证书名称的证书并返回
 11:     foreach (X509Certificate2 cert in certCol)
 12:     {
 13:         if (cert.SubjectName.Name == "CN="  + CertName)
 14:         {
 15:             store.Close();
 16:             return cert;
 17:         }
 18:     }
 19:     store.Close();
 20:     return null;
 21: }

我们也可以通过X509Certificate2Collection 对象在当前存储区中添加删除证书,详细的信息可以参考证书存储区

上面的介绍是我自己对密钥保存的一些理解,大家可以根据的具体情况,去选择具体的方法,希望对大家有所帮助。如果哪位大神有更好的方法,希望留下你的方法共我们学习。

    原文作者:加密算法
    原文地址: http://www.cnblogs.com/xiaohongwu/archive/2011/07/12/2104517.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞