context: //
  // SignedXml.cs - SignedXml implementation for XML Signature
  //
  // Author:
  //	Sebastien Pouliot  <sebastien@ximian.com>
  //	Atsushi Enomoto <atsushi@ximian.com>
  //      Tim Coleman <tim@timcoleman.com>
  //
  // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
  // Copyright (C) Tim Coleman, 2004
  // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.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.Collections;
  using System.IO;
  using System.Runtime.InteropServices;
  using System.Security.Cryptography;
  using System.Security.Policy;
  using System.Net;
  using System.Text;
  using System.Xml;
  using System.Security.Cryptography.X509Certificates;
  
  namespace System.Security.Cryptography.Xml {
  
  	public class SignedXml {
  
  		public const string XmlDsigCanonicalizationUrl			= "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
  		public const string XmlDsigCanonicalizationWithCommentsUrl	= XmlDsigCanonicalizationUrl + "#WithComments";
  		public const string XmlDsigDSAUrl				= XmlDsigNamespaceUrl + "dsa-sha1";
  		public const string XmlDsigHMACSHA1Url				= XmlDsigNamespaceUrl + "hmac-sha1";
  		public const string XmlDsigMinimalCanonicalizationUrl		= XmlDsigNamespaceUrl + "minimal";
  		public const string XmlDsigNamespaceUrl				= "http://www.w3.org/2000/09/xmldsig#";
  		public const string XmlDsigRSASHA1Url				= XmlDsigNamespaceUrl + "rsa-sha1";
  		public const string XmlDsigSHA1Url				= XmlDsigNamespaceUrl + "sha1";
  
  		public const string XmlDecryptionTransformUrl			= "http://www.w3.org/2002/07/decrypt#XML";
  		public const string XmlDsigBase64TransformUrl			= XmlDsigNamespaceUrl + "base64";
  		public const string XmlDsigC14NTransformUrl			= XmlDsigCanonicalizationUrl;
  		public const string XmlDsigC14NWithCommentsTransformUrl		= XmlDsigCanonicalizationWithCommentsUrl;
  		public const string XmlDsigEnvelopedSignatureTransformUrl	= XmlDsigNamespaceUrl + "enveloped-signature";
  		public const string XmlDsigExcC14NTransformUrl			= "http://www.w3.org/2001/10/xml-exc-c14n#";
  		public const string XmlDsigExcC14NWithCommentsTransformUrl	= XmlDsigExcC14NTransformUrl + "WithComments";
  		public const string XmlDsigXPathTransformUrl			= "http://www.w3.org/TR/1999/REC-xpath-19991116";
  		public const string XmlDsigXsltTransformUrl			= "http://www.w3.org/TR/1999/REC-xslt-19991116";
  		public const string XmlLicenseTransformUrl			= "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform";
  
  		private EncryptedXml encryptedXml;
  
  		protected Signature m_signature;
  		private AsymmetricAlgorithm key;
  		protected string m_strSigningKeyName;
  		private XmlDocument envdoc;
  		private IEnumerator pkEnumerator;
  		private XmlElement signatureElement;
  		private Hashtable hashes;
  		// FIXME: enable it after CAS implementation
  		private XmlResolver xmlResolver = new XmlUrlResolver ();
  		private ArrayList manifests;
  		private IEnumerator _x509Enumerator;
  
  		private static readonly char [] whitespaceChars = new char [] {' ', '\r', '\n', '\t'};
  
  		public SignedXml () 
  		{
  			m_signature = new Signature ();
  			m_signature.SignedInfo = new SignedInfo ();
  			hashes = new Hashtable (2); // 98% SHA1 for now
  		}
  
  		public SignedXml (XmlDocument document) : this ()
  		{
  			if (document == null)
  				throw new ArgumentNullException ("document");
  			envdoc = document;
  		}
  
  		public SignedXml (XmlElement elem) : this ()
  		{
  			if (elem == null)
  				throw new ArgumentNullException ("elem");
  			envdoc = new XmlDocument ();
  			envdoc.LoadXml (elem.OuterXml);
  		}
  
  		[ComVisible (false)]
  		public EncryptedXml EncryptedXml {
  			get { return encryptedXml; }
  			set { encryptedXml = value; }
  		}
  
  		public KeyInfo KeyInfo {
  			get {
  				if (m_signature.KeyInfo == null)
  					m_signature.KeyInfo = new KeyInfo ();
  				return m_signature.KeyInfo;
  			}
  			set { m_signature.KeyInfo = value; }
  		}
  
  		public Signature Signature {
  			get { return m_signature; }
  		}
  
  		public string SignatureLength {
  			get { return m_signature.SignedInfo.SignatureLength; }
  		}
  
  		public string SignatureMethod {
  			get { return m_signature.SignedInfo.SignatureMethod; }
  		}
  
  		public byte[] SignatureValue {
  			get { return m_signature.SignatureValue; }
  		}
  
  		public SignedInfo SignedInfo {
  			get { return m_signature.SignedInfo; }
  		}
  
  		public AsymmetricAlgorithm SigningKey {
  			get { return key; }
  			set { key = value; }
  		}
  
  		// NOTE: CryptoAPI related ? documented as fx internal
  		public string SigningKeyName {
  			get { return m_strSigningKeyName; }
  			set { m_strSigningKeyName = value; }
  		}
  
  		public void AddObject (DataObject dataObject) 
  		{
  			m_signature.AddObject (dataObject);
  		}
  
  		public void AddReference (Reference reference) 
  		{
  			if (reference == null)
  				throw new ArgumentNullException ("reference");
  			m_signature.SignedInfo.AddReference (reference);
  		}
  
  		private Stream ApplyTransform (Transform t, XmlDocument input) 
  		{
  			// These transformer modify input document, which should
  			// not affect to the input itself.
  			if (t is XmlDsigXPathTransform 
  				|| t is XmlDsigEnvelopedSignatureTransform
  				|| t is XmlDecryptionTransform
  			)
  				input = (XmlDocument) input.Clone ();
  
  			t.LoadInput (input);
  
  			if (t is XmlDsigEnvelopedSignatureTransform)
  				// It returns XmlDocument for XmlDocument input.
  				return CanonicalizeOutput (t.GetOutput ());
  
  			object obj = t.GetOutput ();
  			if (obj is Stream)
  				return (Stream) obj;
  			else if (obj is XmlDocument) {
  				MemoryStream ms = new MemoryStream ();
  				XmlTextWriter xtw = new XmlTextWriter (ms, Encoding.UTF8);
  				((XmlDocument) obj).WriteTo (xtw);
  
  				xtw.Flush ();
  
  				// Rewind to the start of the stream
  				ms.Position = 0;
  				return ms;
  			}
  			else if (obj == null) {
  				throw new NotImplementedException ("This should not occur. Transform is " + t + ".");
  			}
  			else {
  				// e.g. XmlDsigXPathTransform returns XmlNodeList
  				return CanonicalizeOutput (obj);
  			}
  		}
  
  		private Stream CanonicalizeOutput (object obj)
  		{
  			Transform c14n = GetC14NMethod ();
  			c14n.LoadInput (obj);
  			return (Stream) c14n.GetOutput ();
  		}
  
  		private XmlDocument GetManifest (Reference r) 
  		{
  			XmlDocument doc = new XmlDocument ();
  			doc.PreserveWhitespace = true;
  
  			if (r.Uri [0] == '#') {
  				// local manifest
  				if (signatureElement != null) {
  					XmlElement xel = GetIdElement (signatureElement.OwnerDocument, r.Uri.Substring (1));
  					if (xel == null)
  						throw new CryptographicException ("Manifest targeted by Reference was not found: " + r.Uri.Substring (1));
  					doc.AppendChild (doc.ImportNode (xel, true));
  					FixupNamespaceNodes (xel, doc.DocumentElement, false);
  				}
  			}
  			else if (xmlResolver != null) {
  				// TODO: need testing
  				Stream s = (Stream) xmlResolver.GetEntity (new Uri (r.Uri), null, typeof (Stream));
  				doc.Load (s);
  			}
  
  			if (doc.FirstChild != null) {
  				// keep a copy of the manifests to check their references later
  				if (manifests == null)
  					manifests = new ArrayList ();
  				manifests.Add (doc);
  
  				return doc;
  			}
  			return null;
  		}
  
  		private void FixupNamespaceNodes (XmlElement src, XmlElement dst, bool ignoreDefault)
  		{
  			// add namespace nodes
  			foreach (XmlAttribute attr in src.SelectNodes ("namespace::*")) {
  				if (attr.LocalName == "xml")
  					continue;
  				if (ignoreDefault && attr.LocalName == "xmlns")
  					continue;
  				dst.SetAttributeNode (dst.OwnerDocument.ImportNode (attr, true) as XmlAttribute);
  			}
  		}
  
  		private byte[] GetReferenceHash (Reference r, bool check_hmac) 
  		{
  			Stream s = null;
  			XmlDocument doc = null;
  			if (r.Uri == String.Empty) {
  				doc = envdoc;
  			}
  			else if (r.Type == XmlSignature.Uri.Manifest) {
  				doc = GetManifest (r);
  			}
  			else {
  				doc = new XmlDocument ();
  				doc.PreserveWhitespace = true;
  				string objectName = null;
  
  				if (r.Uri.StartsWith ("#xpointer")) {
  					string uri = string.Join ("", r.Uri.Substring (9).Split (whitespaceChars));
  					if (uri.Length < 2 || uri [0] != '(' || uri [uri.Length - 1] != ')')
  						// FIXME: how to handle invalid xpointer?
  						uri = String.Empty;
  					else
  						uri = uri.Substring (1, uri.Length - 2);
  					if (uri == "/")
  						doc = envdoc;
  					else if (uri.Length > 6 && uri.StartsWith ("id(") && uri [uri.Length - 1] == ')')
  						// id('foo'), id("foo")
  						objectName = uri.Substring (4, uri.Length - 6);
  				}
  				else if (r.Uri [0] == '#') {
  					objectName = r.Uri.Substring (1);
  				}
  				else if (xmlResolver != null) {
  					// TODO: test but doc says that Resolver = null -> no access
  					try {
  						// no way to know if valid without throwing an exception
  						Uri uri = new Uri (r.Uri);
  						s = (Stream) xmlResolver.GetEntity (uri, null, typeof (Stream));
  					}
  					catch {
  						// may still be a local file (and maybe not xml)
  						s = File.OpenRead (r.Uri);
  					}
  				}
  				if (objectName != null) {
  					XmlElement found = null;
  					foreach (DataObject obj in m_signature.ObjectList) {
  						if (obj.Id == objectName) {
  							found = obj.GetXml ();
  
gt: 							found.SetAttribute ("xmlns", SignedXml.XmlDsigNamespaceUrl);