.NET_RSA加密全接触(重、难点解析)

.NET_RSA加密全接触(重、难点解析)

.NET Framework提供了两个类供我们使用RSA算法,分别是:用于加密数据的RSACryptoServiceProvider和用于数字签名的DSACryptoServiceProvider,本文主要谈论RSACryptoServiceProvider的使用以及微软实现RSA算法时的一些主要特点。

1.NETRSA密钥格式

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互通,仅此而已!”

上一篇:Python小代码_14_交换 2 个变量的 3 种方式


下一篇:Base64加密工具-iBlogs