681 lines
22 KiB
C#
681 lines
22 KiB
C#
//
|
|
// XmlCanonicalizer.cs - C14N implementation for XML Signature
|
|
// http://www.w3.org/TR/xml-c14n
|
|
//
|
|
// Author:
|
|
// Aleksey Sanin (aleksey@aleksey.com)
|
|
//
|
|
// (C) 2003 Aleksey Sanin (aleksey@aleksey.com)
|
|
//
|
|
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
using System;
|
|
using System.Collections;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Xml;
|
|
|
|
namespace Mono.Xml {
|
|
|
|
internal class XmlCanonicalizer {
|
|
|
|
private enum XmlCanonicalizerState
|
|
{
|
|
BeforeDocElement,
|
|
InsideDocElement,
|
|
AfterDocElement
|
|
}
|
|
|
|
// c14n parameters
|
|
private bool comments;
|
|
private bool exclusive;
|
|
string inclusiveNamespacesPrefixList;
|
|
|
|
// input/output
|
|
private XmlNodeList xnl;
|
|
private StringBuilder res;
|
|
|
|
// namespaces rendering stack
|
|
private XmlCanonicalizerState state;
|
|
private ArrayList visibleNamespaces;
|
|
private int prevVisibleNamespacesStart;
|
|
private int prevVisibleNamespacesEnd;
|
|
private Hashtable propagatedNss;
|
|
|
|
public XmlCanonicalizer (bool withComments, bool excC14N, Hashtable propagatedNamespaces)
|
|
{
|
|
res = new StringBuilder ();
|
|
comments = withComments;
|
|
exclusive = excC14N;
|
|
propagatedNss = propagatedNamespaces;
|
|
}
|
|
|
|
void Initialize ()
|
|
{
|
|
state = XmlCanonicalizerState.BeforeDocElement;
|
|
visibleNamespaces = new ArrayList ();
|
|
prevVisibleNamespacesStart = 0;
|
|
prevVisibleNamespacesEnd = 0;
|
|
res.Length = 0;
|
|
}
|
|
|
|
public Stream Canonicalize (XmlDocument doc)
|
|
{
|
|
if (doc == null)
|
|
throw new ArgumentNullException ("doc");
|
|
Initialize ();
|
|
|
|
FillMissingPrefixes (doc, new XmlNamespaceManager (doc.NameTable), new ArrayList ());
|
|
WriteDocumentNode (doc);
|
|
|
|
UTF8Encoding utf8 = new UTF8Encoding ();
|
|
byte[] data = utf8.GetBytes (res.ToString ());
|
|
return new MemoryStream (data);
|
|
}
|
|
|
|
public Stream Canonicalize (XmlNodeList nodes)
|
|
{
|
|
xnl = nodes;
|
|
if (nodes == null || nodes.Count < 1)
|
|
return new MemoryStream ();
|
|
XmlNode n = nodes [0];
|
|
return Canonicalize (n.NodeType == XmlNodeType.Document ? n as XmlDocument : n.OwnerDocument);
|
|
}
|
|
|
|
// See xml-enc-c14n specification
|
|
public string InclusiveNamespacesPrefixList {
|
|
get { return inclusiveNamespacesPrefixList; }
|
|
set { inclusiveNamespacesPrefixList = value; }
|
|
}
|
|
|
|
XmlAttribute CreateXmlns (XmlNode n)
|
|
{
|
|
XmlAttribute a = n.Prefix.Length == 0 ?
|
|
n.OwnerDocument.CreateAttribute ("xmlns", "http://www.w3.org/2000/xmlns/") :
|
|
n.OwnerDocument.CreateAttribute ("xmlns", n.Prefix, "http://www.w3.org/2000/xmlns/");
|
|
a.Value = n.NamespaceURI;
|
|
return a;
|
|
}
|
|
|
|
// Note that this must be done *before* filtering nodes out
|
|
// by context node list.
|
|
private void FillMissingPrefixes (XmlNode n, XmlNamespaceManager nsmgr, ArrayList tmpList)
|
|
{
|
|
if (n.Prefix.Length == 0 && propagatedNss != null) {
|
|
foreach (DictionaryEntry de in propagatedNss)
|
|
if ((string) de.Value == n.NamespaceURI) {
|
|
n.Prefix = (string) de.Key;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (n.NodeType == XmlNodeType.Element && ((XmlElement) n).HasAttributes) {
|
|
foreach (XmlAttribute a in n.Attributes)
|
|
if (a.NamespaceURI == "http://www.w3.org/2000/xmlns/")
|
|
nsmgr.AddNamespace (a.Prefix.Length == 0 ? String.Empty : a.LocalName, a.Value);
|
|
nsmgr.PushScope ();
|
|
}
|
|
|
|
if (n.NamespaceURI.Length > 0 && nsmgr.LookupPrefix (n.NamespaceURI) == null)
|
|
tmpList.Add (CreateXmlns (n));
|
|
|
|
if (n.NodeType == XmlNodeType.Element && ((XmlElement) n).HasAttributes) {
|
|
foreach (XmlAttribute a in n.Attributes)
|
|
if (a.NamespaceURI.Length > 0 && nsmgr.LookupNamespace (a.Prefix) == null)
|
|
tmpList.Add (CreateXmlns (a));
|
|
}
|
|
|
|
foreach (XmlAttribute a in tmpList)
|
|
((XmlElement) n).SetAttributeNode (a);
|
|
tmpList.Clear ();
|
|
|
|
if (n.HasChildNodes) {
|
|
for (XmlNode c = n.FirstChild; c != null; c = c.NextSibling)
|
|
if (c.NodeType == XmlNodeType.Element)
|
|
FillMissingPrefixes (c, nsmgr, tmpList);
|
|
}
|
|
nsmgr.PopScope ();
|
|
}
|
|
|
|
private void WriteNode (XmlNode node)
|
|
{
|
|
// Console.WriteLine ("C14N Debug: node=" + node.Name);
|
|
|
|
bool visible = IsNodeVisible (node);
|
|
switch (node.NodeType) {
|
|
case XmlNodeType.Document:
|
|
case XmlNodeType.DocumentFragment:
|
|
WriteDocumentNode (node);
|
|
break;
|
|
case XmlNodeType.Element:
|
|
WriteElementNode (node, visible);
|
|
break;
|
|
case XmlNodeType.CDATA:
|
|
case XmlNodeType.SignificantWhitespace:
|
|
case XmlNodeType.Text:
|
|
// CDATA sections are processed as text nodes
|
|
WriteTextNode (node, visible);
|
|
break;
|
|
case XmlNodeType.Whitespace:
|
|
if (state == XmlCanonicalizerState.InsideDocElement)
|
|
WriteTextNode (node, visible);
|
|
break;
|
|
case XmlNodeType.Comment:
|
|
WriteCommentNode (node, visible);
|
|
break;
|
|
case XmlNodeType.ProcessingInstruction:
|
|
WriteProcessingInstructionNode (node, visible);
|
|
break;
|
|
case XmlNodeType.EntityReference:
|
|
for (int i = 0; i < node.ChildNodes.Count; i++)
|
|
WriteNode (node.ChildNodes [i]);
|
|
break;
|
|
case XmlNodeType.Attribute:
|
|
throw new XmlException ("Attribute node is impossible here", null);
|
|
case XmlNodeType.EndElement:
|
|
throw new XmlException ("EndElement node is impossible here", null);
|
|
case XmlNodeType.EndEntity:
|
|
throw new XmlException ("EndEntity node is impossible here", null);
|
|
case XmlNodeType.DocumentType:
|
|
case XmlNodeType.Entity:
|
|
case XmlNodeType.Notation:
|
|
case XmlNodeType.XmlDeclaration:
|
|
// just do nothing
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void WriteDocumentNode (XmlNode node)
|
|
{
|
|
state = XmlCanonicalizerState.BeforeDocElement;
|
|
for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling)
|
|
WriteNode (child);
|
|
}
|
|
|
|
// Element Nodes
|
|
// If the element is not in the node-set, then the result is obtained
|
|
// by processing the namespace axis, then the attribute axis, then
|
|
// processing the child nodes of the element that are in the node-set
|
|
// (in document order). If the element is inthe node-set, then the result
|
|
// is an open angle bracket (<), the element QName, the result of
|
|
// processing the namespace axis, the result of processing the attribute
|
|
// axis, a close angle bracket (>), the result of processing the child
|
|
// nodes of the element that are in the node-set (in document order), an
|
|
// open angle bracket, a forward slash (/), the element QName, and a close
|
|
// angle bracket.
|
|
private void WriteElementNode (XmlNode node, bool visible)
|
|
{
|
|
// Console.WriteLine ("Debug: element node");
|
|
|
|
// remember current state
|
|
int savedPrevVisibleNamespacesStart = prevVisibleNamespacesStart;
|
|
int savedPrevVisibleNamespacesEnd = prevVisibleNamespacesEnd;
|
|
int savedVisibleNamespacesSize = visibleNamespaces.Count;
|
|
XmlCanonicalizerState s = state;
|
|
if (visible && state == XmlCanonicalizerState.BeforeDocElement)
|
|
state = XmlCanonicalizerState.InsideDocElement;
|
|
|
|
// write start tag
|
|
if (visible) {
|
|
res.Append ("<");
|
|
res.Append (node.Name);
|
|
}
|
|
|
|
// this is odd but you can select namespaces
|
|
// and attributes even if node itself is not visible
|
|
WriteNamespacesAxis (node, visible);
|
|
WriteAttributesAxis (node);
|
|
|
|
if (visible)
|
|
res.Append (">");
|
|
|
|
// write children
|
|
for (XmlNode child = node.FirstChild; child != null; child = child.NextSibling)
|
|
WriteNode (child);
|
|
|
|
// write end tag
|
|
if (visible) {
|
|
res.Append ("</");
|
|
res.Append (node.Name);
|
|
res.Append (">");
|
|
}
|
|
|
|
// restore state
|
|
if (visible && s == XmlCanonicalizerState.BeforeDocElement)
|
|
state = XmlCanonicalizerState.AfterDocElement;
|
|
prevVisibleNamespacesStart = savedPrevVisibleNamespacesStart;
|
|
prevVisibleNamespacesEnd = savedPrevVisibleNamespacesEnd;
|
|
if (visibleNamespaces.Count > savedVisibleNamespacesSize) {
|
|
visibleNamespaces.RemoveRange (savedVisibleNamespacesSize,
|
|
visibleNamespaces.Count - savedVisibleNamespacesSize);
|
|
}
|
|
}
|
|
|
|
// Namespace Axis
|
|
// Consider a list L containing only namespace nodes in the
|
|
// axis and in the node-set in lexicographic order (ascending). To begin
|
|
// processing L, if the first node is not the default namespace node (a node
|
|
// with no namespace URI and no local name), then generate a space followed
|
|
// by xmlns="" if and only if the following conditions are met:
|
|
// - the element E that owns the axis is in the node-set
|
|
// - The nearest ancestor element of E in the node-set has a default
|
|
// namespace node in the node-set (default namespace nodes always
|
|
// have non-empty values in XPath)
|
|
// The latter condition eliminates unnecessary occurrences of xmlns="" in
|
|
// the canonical form since an element only receives an xmlns="" if its
|
|
// default namespace is empty and if it has an immediate parent in the
|
|
// canonical form that has a non-empty default namespace. To finish
|
|
// processing L, simply process every namespace node in L, except omit
|
|
// namespace node with local name xml, which defines the xml prefix,
|
|
// if its string value is http://www.w3.org/XML/1998/namespace.
|
|
private void WriteNamespacesAxis (XmlNode node, bool visible)
|
|
{
|
|
// Console.WriteLine ("Debug: namespaces");
|
|
|
|
XmlDocument doc = node.OwnerDocument;
|
|
bool has_empty_namespace = false;
|
|
ArrayList list = new ArrayList ();
|
|
for (XmlNode cur = node; cur != null && cur != doc; cur = cur.ParentNode) {
|
|
foreach (XmlAttribute attribute in cur.Attributes) {
|
|
if (!IsNamespaceNode (attribute))
|
|
continue;
|
|
|
|
// get namespace prefix
|
|
string prefix = string.Empty;
|
|
if (attribute.Prefix == "xmlns")
|
|
prefix = attribute.LocalName;
|
|
|
|
// check if it is "xml" namespace
|
|
if (prefix == "xml" && attribute.Value == "http://www.w3.org/XML/1998/namespace")
|
|
continue;
|
|
|
|
// make sure that this is an active namespace
|
|
// for our node
|
|
string ns = node.GetNamespaceOfPrefix (prefix);
|
|
if (ns != attribute.Value)
|
|
continue;
|
|
|
|
// check that it is selected with XPath
|
|
if (!IsNodeVisible (attribute))
|
|
continue;
|
|
|
|
// check that we have not rendered it yet
|
|
bool rendered = IsNamespaceRendered (prefix, attribute.Value);
|
|
|
|
// For exc-c14n, only visibly utilized
|
|
// namespaces are written.
|
|
if (exclusive && !IsVisiblyUtilized (node as XmlElement, attribute))
|
|
continue;
|
|
|
|
// add to the visible namespaces stack
|
|
if (visible)
|
|
visibleNamespaces.Add (attribute);
|
|
|
|
if (!rendered)
|
|
list.Add (attribute);
|
|
|
|
if (prefix == string.Empty)
|
|
has_empty_namespace = true;
|
|
}
|
|
}
|
|
|
|
// add empty namespace if needed
|
|
if (visible && !has_empty_namespace && !IsNamespaceRendered (string.Empty, string.Empty) && node.NamespaceURI == String.Empty)
|
|
res.Append (" xmlns=\"\"");
|
|
|
|
list.Sort (new XmlDsigC14NTransformNamespacesComparer ());
|
|
foreach (object obj in list) {
|
|
XmlNode attribute = (obj as XmlNode);
|
|
if (attribute != null) {
|
|
res.Append (" ");
|
|
res.Append (attribute.Name);
|
|
res.Append ("=\"");
|
|
res.Append (attribute.Value);
|
|
res.Append ("\"");
|
|
}
|
|
}
|
|
|
|
// move the rendered namespaces stack
|
|
if (visible) {
|
|
prevVisibleNamespacesStart = prevVisibleNamespacesEnd;
|
|
prevVisibleNamespacesEnd = visibleNamespaces.Count;
|
|
}
|
|
}
|
|
|
|
// Attribute Axis
|
|
// In lexicographic order (ascending), process each node that
|
|
// is in the element's attribute axis and in the node-set.
|
|
//
|
|
// The processing of an element node E MUST be modified slightly
|
|
// when an XPath node-set is given as input and the element's
|
|
// parent is omitted from the node-set.
|
|
private void WriteAttributesAxis (XmlNode node)
|
|
{
|
|
// Console.WriteLine ("Debug: attributes");
|
|
|
|
ArrayList list = new ArrayList ();
|
|
foreach (XmlNode attribute in node.Attributes) {
|
|
if (!IsNamespaceNode (attribute) && IsNodeVisible (attribute))
|
|
list.Add (attribute);
|
|
}
|
|
|
|
// Add attributes from "xml" namespace for "inclusive" c14n only:
|
|
//
|
|
// The method for processing the attribute axis of an element E
|
|
// in the node-set is enhanced. All element nodes along E's
|
|
// ancestor axis are examined for nearest occurrences of
|
|
// attributes in the xml namespace, such as xml:lang and
|
|
// xml:space (whether or not they are in the node-set).
|
|
// From this list of attributes, remove any that are in E's
|
|
// attribute axis (whether or not they are in the node-set).
|
|
// Then, lexicographically merge this attribute list with the
|
|
// nodes of E's attribute axis that are in the node-set. The
|
|
// result of visiting the attribute axis is computed by
|
|
// processing the attribute nodes in this merged attribute list.
|
|
if (!exclusive && node.ParentNode != null && node.ParentNode.ParentNode != null && !IsNodeVisible (node.ParentNode.ParentNode)) {
|
|
// if we have whole document then the node.ParentNode.ParentNode
|
|
// is always visible
|
|
for (XmlNode cur = node.ParentNode; cur != null; cur = cur.ParentNode) {
|
|
if (cur.Attributes == null)
|
|
continue;
|
|
foreach (XmlNode attribute in cur.Attributes) {
|
|
// we are looking for "xml:*" attributes
|
|
if (attribute.Prefix != "xml")
|
|
continue;
|
|
|
|
// exclude ones that are in the node's attributes axis
|
|
if (node.Attributes.GetNamedItem (attribute.LocalName, attribute.NamespaceURI) != null)
|
|
continue;
|
|
|
|
// finally check that we don't have the same attribute in our list
|
|
bool found = false;
|
|
foreach (object obj in list) {
|
|
XmlNode n = (obj as XmlNode);
|
|
if (n.Prefix == "xml" && n.LocalName == attribute.LocalName) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
continue;
|
|
|
|
// now we can add this attribute to our list
|
|
list.Add (attribute);
|
|
}
|
|
}
|
|
}
|
|
|
|
// sort namespaces and write results
|
|
list.Sort (new XmlDsigC14NTransformAttributesComparer ());
|
|
foreach (object obj in list) {
|
|
XmlNode attribute = (obj as XmlNode);
|
|
if (attribute != null) {
|
|
res.Append (" ");
|
|
res.Append (attribute.Name);
|
|
res.Append ("=\"");
|
|
res.Append (NormalizeString (attribute.Value, XmlNodeType.Attribute));
|
|
res.Append ("\"");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Text Nodes
|
|
// the string value, except all ampersands are replaced
|
|
// by &, all open angle brackets (<) are replaced by <, all closing
|
|
// angle brackets (>) are replaced by >, and all #xD characters are
|
|
// replaced by 
.
|
|
private void WriteTextNode (XmlNode node, bool visible)
|
|
{
|
|
// Console.WriteLine ("Debug: text node");
|
|
if (visible)
|
|
res.Append (NormalizeString (node.Value, node.NodeType));
|
|
// res.Append (NormalizeString (node.Value, XmlNodeType.Text));
|
|
}
|
|
|
|
// Comment Nodes
|
|
// Nothing if generating canonical XML without comments. For
|
|
// canonical XML with comments, generate the opening comment
|
|
// symbol (<!--), the string value of the node, and the
|
|
// closing comment symbol (-->). Also, a trailing #xA is rendered
|
|
// after the closing comment symbol for comment children of the
|
|
// root node with a lesser document order than the document
|
|
// element, and a leading #xA is rendered before the opening
|
|
// comment symbol of comment children of the root node with a
|
|
// greater document order than the document element. (Comment
|
|
// children of the root node represent comments outside of the
|
|
// top-level document element and outside of the document type
|
|
// declaration).
|
|
private void WriteCommentNode (XmlNode node, bool visible)
|
|
{
|
|
// Console.WriteLine ("Debug: comment node");
|
|
if (visible && comments) {
|
|
if (state == XmlCanonicalizerState.AfterDocElement)
|
|
res.Append ("\x0A<!--");
|
|
else
|
|
res.Append ("<!--");
|
|
|
|
res.Append (NormalizeString (node.Value, XmlNodeType.Comment));
|
|
|
|
if (state == XmlCanonicalizerState.BeforeDocElement)
|
|
res.Append ("-->\x0A");
|
|
else
|
|
res.Append ("-->");
|
|
}
|
|
}
|
|
|
|
// Processing Instruction (PI) Nodes-
|
|
// The opening PI symbol (<?), the PI target name of the node,
|
|
// a leading space and the string value if it is not empty, and
|
|
// the closing PI symbol (?>). If the string value is empty,
|
|
// then the leading space is not added. Also, a trailing #xA is
|
|
// rendered after the closing PI symbol for PI children of the
|
|
// root node with a lesser document order than the document
|
|
// element, and a leading #xA is rendered before the opening PI
|
|
// symbol of PI children of the root node with a greater document
|
|
// order than the document element.
|
|
private void WriteProcessingInstructionNode (XmlNode node, bool visible)
|
|
{
|
|
// Console.WriteLine ("Debug: PI node");
|
|
|
|
if (visible) {
|
|
if (state == XmlCanonicalizerState.AfterDocElement)
|
|
res.Append ("\x0A<?");
|
|
else
|
|
res.Append ("<?");
|
|
|
|
res.Append (node.Name);
|
|
if (node.Value.Length > 0) {
|
|
res.Append (" ");
|
|
res.Append (NormalizeString (node.Value, XmlNodeType.ProcessingInstruction));
|
|
}
|
|
|
|
if (state == XmlCanonicalizerState.BeforeDocElement)
|
|
res.Append ("?>\x0A");
|
|
else
|
|
res.Append ("?>");
|
|
}
|
|
}
|
|
|
|
// determines whether the node is in the node-set or not.
|
|
private bool IsNodeVisible (XmlNode node)
|
|
{
|
|
// if node list is empty then we process whole document
|
|
if (xnl == null)
|
|
return true;
|
|
|
|
// walk thru the list
|
|
foreach (XmlNode xn in xnl) {
|
|
if (node.Equals (xn))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// This method assumes that the namespace node is *not*
|
|
// rendered yet.
|
|
private bool IsVisiblyUtilized (XmlElement owner, XmlAttribute ns)
|
|
{
|
|
if (owner == null)
|
|
return false;
|
|
|
|
string prefix = ns.LocalName == "xmlns" ? String.Empty : ns.LocalName;
|
|
if (owner.Prefix == prefix && owner.NamespaceURI == ns.Value)
|
|
return true;
|
|
if (!owner.HasAttributes)
|
|
return false;
|
|
foreach (XmlAttribute a in owner.Attributes) {
|
|
if (a.Prefix == String.Empty)
|
|
continue;
|
|
if (a.Prefix != prefix || a.NamespaceURI != ns.Value)
|
|
continue;
|
|
if (IsNodeVisible (a))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsNamespaceRendered (string prefix, string uri)
|
|
{
|
|
// if the default namespace xmlns="" is not re-defined yet
|
|
// then we do not want to print it out
|
|
bool IsEmptyNs = prefix == string.Empty && uri == string.Empty;
|
|
int start = (IsEmptyNs) ? 0 : prevVisibleNamespacesStart;
|
|
for (int i = visibleNamespaces.Count - 1; i >= start; i--) {
|
|
XmlNode node = (visibleNamespaces[i] as XmlNode);
|
|
if (node != null) {
|
|
// get namespace prefix
|
|
string p = string.Empty;
|
|
if (node.Prefix == "xmlns")
|
|
p = node.LocalName;
|
|
if (p == prefix)
|
|
return node.Value == uri;
|
|
}
|
|
}
|
|
|
|
return IsEmptyNs;
|
|
}
|
|
|
|
private bool IsNamespaceNode (XmlNode node)
|
|
{
|
|
if (node == null || node.NodeType != XmlNodeType.Attribute)
|
|
return false;
|
|
return node.NamespaceURI == "http://www.w3.org/2000/xmlns/";
|
|
}
|
|
|
|
private bool IsTextNode (XmlNodeType type)
|
|
{
|
|
switch (type) {
|
|
case XmlNodeType.Text:
|
|
case XmlNodeType.CDATA:
|
|
case XmlNodeType.SignificantWhitespace:
|
|
case XmlNodeType.Whitespace:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private string NormalizeString (string input, XmlNodeType type)
|
|
{
|
|
StringBuilder sb = new StringBuilder ();
|
|
for (int i = 0; i < input.Length; i++) {
|
|
char ch = input[i];
|
|
if (ch == '<' && (type == XmlNodeType.Attribute || IsTextNode (type)))
|
|
sb.Append ("<");
|
|
else if (ch == '>' && IsTextNode (type))
|
|
sb.Append (">");
|
|
else if (ch == '&' && (type == XmlNodeType.Attribute || IsTextNode (type)))
|
|
sb.Append ("&");
|
|
else if (ch == '\"' && type == XmlNodeType.Attribute)
|
|
sb.Append (""");
|
|
else if (ch == '\x09' && type == XmlNodeType.Attribute)
|
|
sb.Append ("	");
|
|
else if (ch == '\x0A' && type == XmlNodeType.Attribute)
|
|
sb.Append ("
");
|
|
else if (ch == '\x0D')
|
|
sb.Append ("
");
|
|
else
|
|
sb.Append (ch);
|
|
}
|
|
|
|
return sb.ToString ();
|
|
}
|
|
}
|
|
|
|
internal class XmlDsigC14NTransformAttributesComparer : IComparer
|
|
{
|
|
public int Compare (object x, object y)
|
|
{
|
|
XmlNode n1 = (x as XmlNode);
|
|
XmlNode n2 = (y as XmlNode);
|
|
|
|
// simple cases
|
|
if (n1 == n2)
|
|
return 0;
|
|
else if (n1 == null)
|
|
return -1;
|
|
else if (n2 == null)
|
|
return 1;
|
|
else if (n1.Prefix == n2.Prefix)
|
|
return string.Compare (n1.LocalName, n2.LocalName);
|
|
|
|
// Attributes in the default namespace are first
|
|
// because the default namespace is not applied to
|
|
// unqualified attributes
|
|
if (n1.Prefix == string.Empty)
|
|
return -1;
|
|
else if (n2.Prefix == string.Empty)
|
|
return 1;
|
|
|
|
int ret = string.Compare (n1.NamespaceURI, n2.NamespaceURI);
|
|
if (ret == 0)
|
|
ret = string.Compare (n1.LocalName, n2.LocalName);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
internal class XmlDsigC14NTransformNamespacesComparer : IComparer
|
|
{
|
|
public int Compare (object x, object y)
|
|
{
|
|
XmlNode n1 = (x as XmlNode);
|
|
XmlNode n2 = (y as XmlNode);
|
|
|
|
// simple cases
|
|
if (n1 == n2)
|
|
return 0;
|
|
else if (n1 == null)
|
|
return -1;
|
|
else if (n2 == null)
|
|
return 1;
|
|
else if (n1.Prefix == string.Empty)
|
|
return -1;
|
|
else if (n2.Prefix == string.Empty)
|
|
return 1;
|
|
|
|
return string.Compare (n1.LocalName, n2.LocalName);
|
|
}
|
|
}
|
|
}
|
|
|