xml加密(XML Encryption)是w3c加密xml的标准。这个加密过程包括加密xml文档的元素及其子元素,通过加密,xml的初始内容将被替换,但其xml格式仍然被完好的保留。

浅析XML的数据加密算法

我们有3个加密xml的方法:

1、仅仅使用对称加密的方法加密xml

这种加密方法只使用一个密钥,也就是说无论是加密xml还是解密xml都使用一个相同的密钥。因为这个密钥不会在被加密的xml中保存,所以我们需要在加密和解密的过程中加载这个密钥并保护它不被窃取。

2、使用对称加密和非对称加密相结合的方法来加密xml

这种方法需要一个用于加密数据的对称密钥和一个用于保护这个对称密钥的非对称密钥。被加密的对称密钥和被加密的数据一起保存在xml文档中。当用私有非对称密钥解密密钥的时候要用公开非对称密钥对密钥进行加密。

3、使用X.509加密xml,这种方法是用X.509作为非对称密钥,它由诸如VeriSign之类的第三方提供。

不管xml加密是如何完成的,保存加密数据总是用两种方法之一。

1、加密后所有的元素都被命名为<EncryptedData>

2、加密后只有数据被替换,而元素名称仍然是可读的,不会发生变化。

这种微妙的变化是非常重要的。例如:

如果你的xml文档中包括被称为<employee>的根元素,该根元素有一个下存储了一段详细信息的被称做<WrittenWarning>的子元素。如果你发送这个xml,并且想<WrittenWarning>这个元素被保护起来,那么使用第1中方法的话<WrittenWarning>将被替换为<EncryptedData>,你不会从加密后的文档中获取到任何可读的信息。

如果使用第2种方法,那么<WrittenWarning>元素仍然被保留,只用数据会被加密。任何得到这个文档的人虽然不能知道该元素下的详细信息,但仍然知道有一些事情发生在这个雇员身上。另外,<WrittenWarning>元素的所有属性也不会被加密。

所以,如果没有特殊需求,我们一般都用第1种方法。在.net 2.0中你可以通过修改一个Boolean值的属性,便可以非常简单的选择使用哪种方法。

author元素及其子元素都将被<EncryptedData>给替换掉,另外还包括其他一些元素,如加密算法,密钥等。

<EncryptedData>元素
仔细看看<EncryptedData>元素的树形结构,你会发现<EncryptedData>元素下分解出了很多子元素。其中<KeyInfo>元素与xml数字签名中的<KeyInfo>元素是相同的。
EncryptedData元素被包含在“http://www.w3.org/2001/04/xmlenc#”命名空间中。它是被加密数据的根元素。

EncryptionMethod元素指定加密数据的对称方法。做这件事需要使用一个包含了w3 url的算法属性 - “http://www.w3.org/2001/04/xmlenc#aes256-cbc”,它指出数据是用AES(Rijndael)以256k的密钥加密的。

KeyInfo元素来自xml数字签名,它保存着对称密钥的信息,除此之外该元素还能保存更多的信息。

KeyInfo元素下的EncryptedKey元素及其子元素包含着关于被保存的密钥的信息。

KeyInfo下的EncryptionMethod元素包含的非对称加密方法用来加密对称密钥。做这件事需要把一个算法属性设置给w3 url。例如:“http://www.w3.org/2001/04/xmlenc#rsa-1_5”说明使用了RSA非对称算法来加密对称密钥。

KeyName元素是一个标识符,用来发现密钥。稍后在我们编程的时候你将会发现它的重要性。

CipherData元素和CipherValue元素出现在EncryptedKey元素和EncryptedData元素下,它们包含着密码数据。事实上密码数据保存在CipherValue元素下的。EncryptedKey元素下保存的是被加密的密钥,EncryptedData元素下的CipherValue保存的是被加密的数据。

使用.net加密xml
本文提供了一个简单的加密、解密xml的应用程序,下面我们一起来看一看相关的代码。这个示例只有一些基本功能,你可以再额外加一些如选择节点之类的功能

首先加载非对称公开密钥来加密密钥
// 创建一个用于加密密钥的非对称密钥
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
// 加载一个公开密钥
XmlDocument pubKeys = new XmlDocument();
pubKeys.Load(Application.StartupPath + "\\xml.dev.keys.public");
// 使用公开密钥加密密钥
rsa.FromXmlString(pubKeys.OuterXml);

接下来加载xml文档并选择一个需要加密的节点。下面的代码示例了如何使用一个XPath表达式来选择节点。如果不选择节点,则整个xml文档都将被加密。
// xml文档
this.xmlEncDoc = new XmlDocument();

// 给xml文档加载一些节点和数据(省略)

XmlElement encElement;
// 如果没有xpath则
if (xpath == string.Empty)
{
encElement = this.xmlEncDoc.DocumentElement;
}
else
{
XmlNamespaceManager xmlns = this.xmlCntrlr.xmlnsManager;
// 通过xpath选择出需要加密的元素
encElement = this.xmlEncDoc.SelectSingleNode(xpath, xmlns) as XmlElement;
}

使用EncryptedXml类去加密数据和密钥
// 完成加密xml的类
EncryptedXml xmlEnc = new EncryptedXml(this.xmlEncDoc);
// 增加一个“session”密钥,使用rsa编码
xmlEnc.AddKeyNameMapping("session", rsa);
// 使用“session”密钥来加密数据
// 这些信息被保存在KeyInfo元素下
EncryptedData encData = xmlEnc.Encrypt(encElement, "session");

用加密后的元素替换初始元素
// 用加密后的元素替换初始元素
EncryptedXml.ReplaceElement(encElement, encData, false);
使用.net解密xml
首先加载私有非对称密钥来解密密钥
// 创建一个用于解密密钥的非对称密钥
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
// 加载私有密钥
XmlDocument privKeys = new XmlDocument();
privKeys.Load(Application.StartupPath + "\\xml.dev.keys.private");
// 使用私有密钥来解密密钥
rsa.FromXmlString(privKeys.OuterXml);

增加一个密钥名称并映射到被加密的文档中
// 增加一个密钥名称并映射到被加密的文档中
EncryptedXml encXml = new EncryptedXml(xmlEncDoc);
encXml.AddKeyNameMapping("session", rsa);

通过指定的密钥来解密文档的每一个EncryptedData元素
// 解密所有<EncryptedData>元素
encXml.DecryptDocument();

总结

xml加密(XML Encryption)是w3c加密xml的标准。加密后的文档仍然是xml格式。我们使用非对称算法和对称算法来加密xml,对称算法用于加密数据,非对称算法用于加密对称算法中的密钥,加密后的数据被保存在EncryptedData元素下。EncryptedData元素包含着一些列用于描述算法的子元素,同时也包含着密钥信息。