.NET_RSA加密全接触(重、难点解析)
.NET_RSA加密全接触(重、难点解析)
.NET Framework提供了两个类供我们使用RSA算法,分别是:用于加密数据的RSACryptoServiceProvider和用于数字签名的DSACryptoServiceProvider,本文主要谈论RSACryptoServiceProvider的使用以及微软实现RSA算法时的一些主要特点。
1、.NET中RSA密钥格式
RSA的密钥有两种表现形式,一种是通过RSAParameters对象来表现,另一种是通过XML字符串来表现,当通过默认构造函数构造一个RSACryptoServiceProvider实例的时候,会自动生成一对公私钥,可以通过调用其ExportParameters()方法或ToXmlString()方法导出密钥(分别对应前面所述的两种表现形式)。这两种表现形式的本质是一样的,前者是一个含有八个字段的结构体,后者是一个含有八个XML配置节的XMLNODE,这八个元素的名称分别为:
D、DP、DQ、Exponent、InverseQ、Modulus、Q
要了解这些元素的意义,首先需要了解构造公私钥的过程
A) 找到两个大素数P和q,相乘得n
B) 选择一个数e,它小于n且与n互素,得到公钥(e,n)
C) 得到一个数d,它满足(d*e)mod ((p-1)*(q-1))=1,得到私钥(d,n)
D) 纯文本m到密文c的加密过程c=(m^e)mod n,解密过程为m=(c^d)mod n,加密解密过程是可逆的。
了解了密钥的构造过程,下面是这八个元素的详细解释:
RSAParameters 字段 |
Contains |
对应的 PKCS #1 字段 |
D |
d,私钥指数 |
privateExponent |
DP |
d mod (p - 1) |
exponent1 |
DQ |
d mod (q - 1) |
exponent2 |
Exponent |
e,公钥指数 |
publicExponent |
InverseQ |
(InverseQ)(q) = 1 mod p |
Coefficient |
Modulus |
N |
Modulus |
P |
P |
prime1 |
Q |
Q |
prime2 |
第三列的“PKCS”指的是一种RSA加密标准,类似于这样的标准有多个,下文会进行叙述!
2、.NET中加密结果的不确定性
Net环境下对同一个字符串用相同的RSA公钥加密,每次的结果都会不一样!如下程序所示:
class Program
{
static void Main(string[] args)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
byte[] c=rsa.Encrypt(UTF8Encoding.UTF8.GetBytes("hello"),true);
Console.WriteLine(Convert.ToBase64String(c));
Console.ReadLine();
}
}
出现这种结果不确定的原因在于:.Net为了加强RSA加密算法的安全性,在每次加密的时候都会生成一定的随机数和原始数据一起被加密,这显然不是单纯标准的RSA加密 。
其实这些随机数的生成也是遵循算法标准的,更专业的说是随机填充算法,比如NoPadding、ISO10126Padding、OAEPPadding、PKCS1Padding、PKCS5Padding、SSL3Padding,而最常见的应该是OAEPPadding【Optimal Asymmetric Encryption Padding】、PKCS1Padding;.Net支持PKCS1Padding或OAEPPadding,其中OAEPPadding仅在XP或更高版本的操作系统上可用。此处不在讨论这些填充算法的具体细节,读者朋友只需知道有这些填充标准即可,需要强调的一点是:不论是.net,java或者是C++等,只要他们用相同的填充标准,即使每次加密的结果都不一样,不同语言之间照样可以互通!
3、关于密钥的Base64编码(AQAB=65537是怎样得到的?)
大家都知道RSACryptoServiceProvider的ToXmlString()方法可以将公私钥导出到一个XML
字符串中,并且是以Base64编码的形式导出的。感兴趣的读者可以用Reflector反编译微软的dll文件,你会发现,这个方法导出的字符串就是分别对RSAParameters的八个字段(八个字节数组)进行Base64编码,然后串在一起得到的。
前面说到,当我们通过默认构造函数创建一个RSACryptoServiceProvider的实例的时候,会自动产生对应于该实例的一对儿公私钥,这种情况下微软选择的公钥都是一样的,都是65537,这个65537转化成的字节数组就是RSAParameters的Exponent的值,AQAB就是由65537编码而来。
65537的转化成的字节数组是什么样的?可能有读者会说,非常简单,一条语句就够了,用Convert.ToBase64Array(“65537”)即可!错!这样想的读者应该思考一下,此处的65537是什么?对,它是一个大素数,是一个数字,并不是一个字符串,我们应该将其转化为二进制串,然后每八位一截取,得到其对应的字节数组!此处限于篇幅不在详细叙述得到“AQAB”的详细过程,读者朋友可以参见该博客所述:http://www.cnblogs.com/midea0978/archive/2007/05/22/755826.html
4、密钥的保存
对于临时会话,无需保存密钥,但是很多情况下我们需要重复的使用一组密钥,此时就需要将密钥保存下来,在.NET中保存密钥主要有三种方法:
1、 将密钥导出到本地文件,然后对文件进行加密;
2、 利用Windows系统的数据保护API;
从Windows2000开始,Windows操作系统提供了一套密码学方面的API,称为DAPI(Data Protection API,数据保护API)。这套API由crypt32.dll库实现,可以实现用户、进程、会话或机器级别上的数据加密保护,从而确保它们的机密性,这样就使我们不再需要负责密钥的管理。
.Net中我们可以将密钥对儿保存到密钥容器中,这个密钥容器由操作系统管理,可以是用户级别的容器,也可以是机器级别的容器。密钥容器是公用的,只不过在不同的语言中有不同的实现,这样在.net中创建的公私钥对儿,C、C++程序也都可以读取。
.NET中可以使用CspParameters对象创建或使用密钥容器
1)实例化CspParameters对象CspParameters cspPara = new CspParameters();
2)指定CspParameters对象实例的名称 ,cspPara.KeyContainerName =
"test"。如果名称为key_container_test的密钥容器不存在,RSA对象会创建这个密钥容器;如果名称为key_container_test的密钥容器已经存在,RSA对象会使用这个密钥容
器。
3)设置密钥类型为Exchange ,cspPara.KeyNumber = 1;
4)设置密钥容器保存到计算机密钥库(默认为用户密钥库,cspPara.Flags
= CspProviderFlags.UseMachineKeyStore
3、 利用数字证书
此处不再详述,参考文档1、“.NET中非对称加密RSA算法的密钥保存”
2、“了解计算机级别和用户级别的 RSA 密钥容器”
5、.net和java端的RSA互通
.net和java的RSA互通要谈论的东西比较多,请参见另一篇文章“.NET和Java的RSA互通,仅此而已!”
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。