除了包封式引用(其URI属性为空字符串的Reference元素)以外,在XML签名标准中还定义了其他两个宽泛类型的引用:对分离数据的引用以及通过ID对XML数据进行的引用。分离数据位于包含签名的XML文档的外部,这些引用可以指向另外一个XML文档或任何其他类型的资源。当您在一个签名中对多个资源(例如,一个XML文档以及由该文档引用的其他一些文件)进行签名时,通常会使用该类型的引用。下面的XML代码片段显示了分离引用的一个示例:
为了使用Framework中的类来创建分离引用,只需在创建引用对象时将该引用的URI设置为资源的URI,然后 将该引用添加到签名中,如下所示:
// Create a Reference to detached data, assume a SignedXml object in sig
Reference refr = new Reference("http://www.example.com/foo.jpg");
sig.AddReference(refr);
通过ID对XML数据进行的引用指向包含签名的文档内部或签名本身内部的XML。对于这些引用,签名引擎寻找其ID属性与引用中的URI匹配(不包括#)的元素。以下为一个基于ID的引用的示例:
如果ID为“myData”的元素位于包含签名的文档中,则该引用是完整的,并且签名引擎将在处理该签名时找到它。您通常使用该类型的引用将签名的作用范围限制到示例文档的特定部分。例如,在文档处理应用程序中,审阅者通常只对他审阅的XML文档部分(而不是整个文档)进行签名。该标准还允许您向Object元素中的签名添加任意数据。Object元素的XML看起来类似于以下代码行:
要创建基于ID的引用(该引用指向包含签名的文档中已经存在的元素),请添加如下所示的代码:
// Create a Reference to XML data in the containing document,
// assume a SignedXml object in sig
Reference refr = new Reference("#myData");
sig.AddReference(refr);
如果基于ID的引用指向了签名中的一个Object元素,则除了添加该引用以外,还必须向签名中添加一个DataObject,如刚才显示的代码中所示。数据对象可以包含您传入的任何XML。
// Adds a DataObject with an Id of "#myData" to the signature, assume a
// SignedXml object in sig, and xml data of type XmlNodeList in data
DataObject dobj = new DataObject();
dobj.Id = "myData"; // Note: no #
dobj.Data = data; // XML Data of the Object
sig.AddObject(dobj);
您通常使用指向Object元素的引用来对有关签名的元数据进行签名,例如,签名者的唯一标识符或有关该签名的其他一些信息。
转换通过允许您在生成引用的数据的哈希值之前修改该数据,使您可以对已签名的内容进行更多的控制。例如,信封式签名转换在对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数据进行哈希运算之前将规范化该数据。

