如何使用C#加密解密XML文档

 如何使用C#加密解密XML文档

 

      .NETFramework提供了几种类,可用于对 XML 数据进行加密和解密,以及创建和验证 XML数字签名。这些类提供了维护 XML 数据的保密性和完整性的方法。在这里,我们只涉及如何使用.NETFramework本身提供了的EncryptedXml类进行加密和解密。该类提供了一些方法,能够让用户使用不同的算法进行加密和解密XML

      要使用EncryptedXml类进行加密和解密,我们需要搞清楚两个主题。

1.    加密算法的选取。

2.    如何进行加密。

      涉及到加密算法,一般分为两种类型的算法,对称加密算法(SymmetricAlgorithm)和非对称加密算法(AsymmetricAlgorithm)。两种算法的区别在于,使用对称加密算法进行加密和解密,加密过程和解密过程都是使用同一个密钥;对于非对称加密和解密,则是使用一个相互匹配密钥对----公钥和私钥进行加解密。【顺便简单的提及一下非对称加密方式和原理。对于这种类型的加密,加密密钥和解密密钥是相对的说法,如果用加密密钥加密那么只有解密密钥才能恢复,如果用解密密钥加密则只有加密密钥能解密,所以它们被称为密钥对,其中的一个可以在网络上发送、公布,叫做公钥,而另一个则只有密钥对的所有人才持有,叫做私钥,非对称公开密钥系统又叫做公钥系统。非对称加密算法的基本原理是,如果发信方想发送只有收信方才能解读的加密信息,发信方必须首先知道收信方的公钥,然后利用收信方的公钥来加密原文;收信方收到加密密文后,使用自己的私钥才能解密密文。显然,采用不对称加密算法,收发信双方在通信之前,收信方必须将自己早已随机生成的公钥送给发信方,而自己保留私钥。】另外,这两种加密在效率上有比较大的区别,前者效率高,后者效率要相对低一些。所以,加密大量数据,一般都是采用对称加密算法将数据进行加密,为了使加密更加安全,再使用非对称加密算法将加密数据的对称加密密钥进行加密,这样,在进行网络传输时,即保证了加密数据的效率,又保证了加密数据的安全性,当然,在没有将加密的数据进行网络传输时,就没有必要使用非对称加密了,普通的对称加密就可以满足需求。(大家可以仔细Google一下。)

       对于这两种加密,常见对称加密算法有DES, AES(又称为Rijndael,其密钥长度分128,192,256位这三种), TripleDES等;非对称加密算法常见的有RSA算法。这几种加密算法对应的类分别是:DESCryptoServiceProviderRijndaelManagedTripleDESCryptoServiceProvider以及RSACryptoServiceProvider。前有三种都从SymmetricAlgorithm继承过来,最后一种从AsymmetricAlgorithm继承过来。


好了,基本上关于加密解密,我们已以有一个大致的了解,那么,下面详细的描述一下如何进行加密和解密。

在下面的描述中,我参考了以下文档:

XMLEncryption Syntax and ProcessingW3C XML加解密标准文档

XML加密和数字签名该主题下含有XML各种类型的加解密

假定现在有如下XML文档(test.xml),我们要对其进行加密/解密:

加密的大致流程如下:

1.    通过从磁盘加载 XML文件创建 XmlDocument 对象。XmlDocument对象包含要加密的 XML 元素。

2.     XmlDocument对象中找到指定的元素,然后创建一个新的 XmlElement 对象来表示要加密的元素。

3.    创建 EncryptedXml类的新实例,并使用它通过指定的加密算法对XmlElement 进行加密。

4.    构造一个 EncryptedData对象,用XML 加密元素的 URL 标识符、EncryptedKey信息等填充它,并将加密数据填充到该结构体里面去。(很重要,很繁琐的步骤)

5.     EncryptedData元素替换原始 XmlDocument 对象中的元素。

我们直接拿test.xml来加密,要加密的元素是<number>,加密粒度是该结点,加密数据采用最简单的DES加密,加密后的XML文档变为:

从该加密后的XML里面可以看得出来,<EncryptedData>这个节点里面的内容为加密后的核心内容,但这里面真正被加密的内容在哪?很明显,<CipherValue>3oIDgOJMW0/Ev3duGiCvsVdDgPzP7X0399xwVKMR8MQUGO1AMTIlFA==这一长串就是加密后的真正数据。那其它部分都是些什么内容?这里,我们需要熟悉一下W3C关于加密的标准。可参见XMLEncryption Syntax and Processing文档。


从加密后的XML里面可以看出,<EncryptedData>节点包含了被加密后的数据和其它信息,W3C为其定义的标准结构如下:

(其中,“?”表示出现0次或1次,“+”表示出现1次或多次,“*”表示0次或多次,空元素标签表示该元素的内容为空)

 

 

 

其中:

1.    <EncryptionMethod>用于描述加密方式。一般情况下,需要设置该项。

2.    <ds:KeyInfo>用于描述用于加密数据的key。一般情况下,需要设置该项的<KeyName>节点。

3.    <EncryptedKey>用于描述将加密数据的key①进行加密的新key②的信息。有点绕,实际上,它出现于这样的情形:当我们使用对称加密key(如:DES加密)对数据进行加密,为了加密更加安全,然后采用另外一种高级加密方式(如:RSA加密),对这个key再进行一次加密,并将这个key加密后的生成的加密数据保存到<EncryptedKey>里面的<CipherData>节点下面。解密时,需要通过给出key②,先将原始加密数据的key①解密出来,再将原始数据进行解密。要注意一点,<EncryptedKey>的数据结构实际上和<EncryptedData>的数据结构是类似的。给出一个简单的例子,如:

 

这里面<CipherValue>xyzabc</CipherValue>中的数据实际上就是被加密后的key.

MSDN上所给出的DEMO称这种方式为非对称加密方式,但我认为有些不妥,因为加密真正数据的部分并没有采用非对称加密方式,而只是将key①使用了非对称加密。

4.    元素<CipherData>代表加密数据的结构,其中的<CipherValue>代表真正加密后的数据。

 


对于以上所有结构,在.NET Framework的类里面都有对应的类。

EncryptedData Class

EncryptionMethod Class

KeyInfo Class

EncryptedKey Class

CipherData Class

CipherReference Class

KeyInfoName Class

……

(个人感觉,这些类实在是太多了,有点抽象过度的嫌疑。)

另外,我们需要了解一下加密粒度(Encryption Granularity)。加密粒度分两种,一是加密节点内部的数据,另一个加密整个节点。比如,我们可以选择只加密<number>的内容19834209,当然也可以选择加密<number>整个结点。

实际上,通过上面的内容可以看出,加密XML最核心的东西实际上只有两个部分,第一是将原始数据进行加密,第二是构建<EncryptedData>节点内的数据。

 

OK,整个加密的流程以及相关的背景知识介绍得差不多了。下面通过封装一个加解密的类(GTXXmlCrypt)来说明如何使用EncryptedXml进行加解密。

首先,需要描述我们所使用的EncryptedXml类里面几个重要的方法,这几个方法也是在这个封装类里面用到的,简单描述一下,详细请见MSDN

1. publicbyte[] EncryptData(XmlElementinputElement,SymmetricAlgorithmsymmetricAlgorithm,boolcontent);

a)    通过给定的对称加密算法,对指定的节点进行加密,content指明是否是加密整个节点还是只加密节点内容。

b)    在该类中,用于加密节点的方法只能通过称加密算法,而不能够通过非对称加密节点,可能他们认为使用非对称加密的实际含义是:先通过对称加密将数据进行加密,再用非对称加密将前者(对称加密的key)进行加密,这种方式才叫非对称加密。

2. publicbyte[] DecryptData(EncryptedDataencryptedData,SymmetricAlgorithmsymmetricAlgorithm);

a)    通过给定的对称解密算法(加密和解密的key是一样的),将数据进行解密,同样,只能使用对称加密方式。

3. publicvoid DecryptDocument();

a)    解密整个文档,但它使用的前提是:在被加密后的文档中,<EncryptedData>节点必须含有<KeyInfo>,而且其中的<KeyName>必须有值。可以通过将要封装的类实验。

4. publicstatic void ReplaceElement(XmlElementinputElement,EncryptedDataencryptedData,boolcontent);

a)    当加密完后,需要用EncryptedData来替换加密的节点。

5. publicstatic byte[] DecryptKey(byte[] keyData, SymmetricAlgorithmsymmetricAlgorithm);

a)    该方法通过对称加密算法解密“加密原数据的密钥”。

6. publicstatic byte[] DecryptKey(byte[] keyData, RSA rsa, bool useOAEP);

a)    该方法通过非对称加密算法解密“加密原数据的密钥”。

 

我们需要封装的GTXXmlCrypt类有以下需求:

1.    能够加载一个xml文件进行加解密;

2.    能够某个XmlDocument进行加解密;

3.    能够对单个指定的节点进行加解密;

4.    能够指定各种加解密的方式,如指定加密方式为DESAESRSA等;

 

该类的框架如下:(请原谅我只给出了一个代码的架子,也没有用类图的形式表示出来,因为我会将所有代码全部贴上来。)

 

该类基本上比较简单,我们只重点分析一下加密的核心功能。

 

 

加密某个节点分以下步骤:

<!--[if !supportLists]-->1.<!--[endif]-->创建 EncryptedXml 类的新实例,并使用它通过对称密钥对 XmlElement进行加密。EncryptData 方法将加密的元素作为加密字节的数组返回。

<!--[if !supportLists]-->2.<!--[endif]-->构造一个 EncryptedData 对象,然后用 XML加密元素的 URL 标识符填充它。此 URL 标识符使解密方知道 XML 包含一个加密元素。可以使用 XmlEncElementUrl字段指定 URL 标识符。

<!--[if !supportLists]-->3.<!--[endif]-->创建 EncryptionMethod 对象,该对象被初始化为用来生成密钥的加密算法的URL标识符。将 EncryptionMethod 对象传递给 EncryptionMethod属性。

<!--[if !supportLists]-->4.<!--[endif]-->如果用户给加密数据的key设置了一个名称,那就创建一个KeyInfo的对象,并通过KeyInfoName为其指定一个名称。

<!--[if !supportLists]-->5.<!--[endif]-->将加密的元素数据添加到 EncryptedData 对象中。

<!--[if !supportLists]-->6.<!--[endif]-->用EncryptedData 元素替换原始 XmlDocument对象中的元素。

 

使用RSA加密的整体流程和这个类似,只不过多了一步加密sessionkey(加密原始数据的key)的过程。在这里我就不再赘述了,请直接看源代码。

以下是解密的代码:

解密分两步:

<!--[if !supportLists]-->1.    <!--[endif]-->如果给出了keyName和解密密钥,那么直接使用EncryptedXmlDecryptDocument方法。

<!--[if !supportLists]-->2.    <!--[endif]-->否则,我们就需要一个节点一个节点的解密了。对于非对称的解密,一般直接使用DecryptDocument方法就可以了。

 


整个代码如下:

GTXXmlCrypt代码:

 

测试代码:

 

主函数:

 

 


 

如果该代码存在问题或者您有好的建议,请第一时间通知我,让我再将这份代码完善一下。

 

哎,CSDN的这个blog编辑器实在是太令人失望了,格式实在是太难调整了。

 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值