转换通过允许您在生成引用的数据的哈希值之前修改该数据,使您可以对已签名的内容进行更多的控制。例如,信封式签名转换在对XML文档进行签名之前会移除Signature节点。引用可以指定任何数量的转换,这些转换按照在Transforms元素中指定的顺序而做用。.NET Framework中的类除了支持我们前面提到的信封式签名转换以外,还支持下列转换:
XPath表达式被指定为Transform元素下的XPath元素的文本内容。需要注意的是,XPath转换充当筛选器,而不是充当在作为输入传递的XML中选择节点的手段。该转换针对作为输入传递给该转换的每个节点计算XPath表达式,结果被转换为布尔值。输入节点将被考虑以便传递计算,并且如果计算的结果为true,则输入节点将被包含在转换的输出中。考虑转换的以下XML输入:
Some data
Even more data
假设我们只希望选择“b”节点进行签名。带有XPath表达式“ancestor-or-self::b”的XPath转换将返回以下节点集(它正是我们所需要的):
Some data
Even more data
该XPath表达式的Transform元素看起来类似于下面的代码片段:
>
要在签名时以编程方式创建转换,请创建某个转换对象的实例,适当设置它的属性,并且将其添加到它所应用于的引用。以下示例将上一个示例中使用的XPath转换添加到某个引用中,以便创建转换:
// Add an XPath transform to a reference.
// Assume a Reference object in refr
XmlDocument doc = new XmlDocument();
doc.LoadXml("ancestor-or-self::b");
XmlDsigXPathTransform xptrans = new XmlDsigXPathTransform();
xptrans.LoadInnerXml(doc.ChildNodes);
refr.AddTransform(xptrans);
规范化的目的是为两个逻辑上相同但可能不是由相同的文本表示的XML片段产生相同的XML数据。例如,请观察下面两个代码片段。它们在逻辑上是相同的;它们的不同之处仅仅在于文本表示。但是如果您要对它们按原样进行哈希运算,则得到的哈希值将是不同的:
Some text
Some text
要避免该问题,该标准中指定的默认规范化算法将执行很多任务,包括消除开始和结束标记中的空白以及将空元素转换为开始/结束标记对。但是,它不会更改元素内容中的任何空白。所执行操作的完整列表可以在Canonical XML(位于http://www.w3.org/TR/xml-c14n)中得到。
签名引擎在必要时自动规范化数据,以便符合W3C标准。特别地,每当签名引擎需要将XML数据转换为二进制数据以便进行哈希运算时,都会规范化该数据。例如,当它准备对SignedInfo元素及其子元素进行签名时,就会发生这种情况。当它准备引用或转换的输出以便进行签名时,也可能发生这种情况。例如,如果您使用基于ID的引用(指向包含签名的文档中的其他XML数据),并且该引用没有与其相关联的转换,则签名引擎在对该引用的XML数据进行哈希运算之前将规范化该数据。
XML签名标准提供了KeyInfo元素,帮助进行密钥管理。该元素可以存储密钥名称、密钥值、密钥检索方法或证书信息,以帮助接收方验证签名。该标准没有指定应当如何信任以及是否应当信任KeyInfo元素中的任何信息。如果发送方和接收方共享一个受信任密钥列表,或者如果您发现了其他某种用于将密钥名称映射到密钥的方法,则KeyInfo元素可能很有用。.NET Framework 1.x具有一些对密钥名称、值和检索方法的支持,.NET Framework 2.0还包含对X.509证书的支持。让我们假设发送方和接收方共享一个密钥列表,接收方对于他期望从其接收消息的每个发送方都具有一个公钥。签名应用程序可以添加以下代码,以便向签名中添加KeyInfo元素:
// Adds an KeyInfo element with RSA public key information to the
// signature.
// Assumes a SignedXml object in sig, and an RSA object in key.
KeyInfo ki = new KeyInfo();
ki.AddClause(new RSAKeyValue(key));
sig.KeyInfo = ki;
该代码应当在调用ComputeSignature之前添加。它将在签名中产生一个KeyInfo元素,该元素看起来类似于以下代码:
这表示用来对XML文档进行签名的RSA公钥。接收方应用程序应当将该密钥与受信任密钥列表进行比较,如果该公钥不在列表中,则不应当信任文档。否则,攻击者就可以在传输过程中替换已经签名的文档,并且用另外一个密钥对其进行签名。如果签名包含与此类似的RSAKeyValue,则验证代码可以调用SignedXml类的CheckSignature方法(它不采用任何参数),并且.NET Framework将根据RSAKeyValue元素计算出该密钥。下面是一个示例:
// Verify a signature that includes RSAKeyInfo or DSAKeyInfo.
// Assume a SignedXml object in sig.
bool verified = sig.CheckSignature();

